diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index a5e2bc1c7af..7995a33f09f 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -2717,9 +2717,33 @@ ${item.displayPath}${name}\
* @return {Array}
*/
function buildItemSearchTypeAll(types, lowercasePaths) {
- return types.map(type => buildItemSearchType(type, lowercasePaths));
+ return types.length > 0 ?
+ types.map(type => buildItemSearchType(type, lowercasePaths)) :
+ EMPTY_GENERICS_ARRAY;
}
+ /**
+ * Empty, immutable map used in item search types with no bindings.
+ *
+ * @type {Map>}
+ */
+ const EMPTY_BINDINGS_MAP = new Map();
+
+ /**
+ * Empty, immutable map used in item search types with no bindings.
+ *
+ * @type {Array}
+ */
+ const EMPTY_GENERICS_ARRAY = [];
+
+ /**
+ * Object pool for function types with no bindings or generics.
+ * This is reset after loading the index.
+ *
+ * @type {Map}
+ */
+ let TYPES_POOL = new Map();
+
/**
* Converts a single type.
*
@@ -2732,15 +2756,15 @@ ${item.displayPath}${name}\
let pathIndex, generics, bindings;
if (typeof type === "number") {
pathIndex = type;
- generics = [];
- bindings = new Map();
+ generics = EMPTY_GENERICS_ARRAY;
+ bindings = EMPTY_BINDINGS_MAP;
} else {
pathIndex = type[PATH_INDEX_DATA];
generics = buildItemSearchTypeAll(
type[GENERICS_DATA],
lowercasePaths
);
- if (type.length > BINDINGS_DATA) {
+ if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) {
bindings = new Map(type[BINDINGS_DATA].map(binding => {
const [assocType, constraints] = binding;
// Associated type constructors are represented sloppily in rustdoc's
@@ -2759,38 +2783,83 @@ ${item.displayPath}${name}\
];
}));
} else {
- bindings = new Map();
+ bindings = EMPTY_BINDINGS_MAP;
}
}
+ /**
+ * @type {FunctionType}
+ */
+ let result;
if (pathIndex < 0) {
// types less than 0 are generic parameters
// the actual names of generic parameters aren't stored, since they aren't API
- return {
+ result = {
id: pathIndex,
ty: TY_GENERIC,
path: null,
generics,
bindings,
};
- }
- if (pathIndex === 0) {
+ } else if (pathIndex === 0) {
// `0` is used as a sentinel because it's fewer bytes than `null`
- return {
+ result = {
id: null,
ty: null,
path: null,
generics,
bindings,
};
+ } else {
+ const item = lowercasePaths[pathIndex - 1];
+ result = {
+ id: buildTypeMapIndex(item.name, isAssocType),
+ ty: item.ty,
+ path: item.path,
+ generics,
+ bindings,
+ };
}
- const item = lowercasePaths[pathIndex - 1];
- return {
- id: buildTypeMapIndex(item.name, isAssocType),
- ty: item.ty,
- path: item.path,
- generics,
- bindings,
- };
+ const cr = TYPES_POOL.get(result.id);
+ if (cr) {
+ // Shallow equality check. Since this function is used
+ // to construct every type object, this should be mostly
+ // equivalent to a deep equality check, except if there's
+ // a conflict, we don't keep the old one around, so it's
+ // not a fully precise implementation of hashcons.
+ if (cr.generics.length === result.generics.length &&
+ cr.generics !== result.generics &&
+ cr.generics.every((x, i) => result.generics[i] === x)
+ ) {
+ result.generics = cr.generics;
+ }
+ if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) {
+ let ok = true;
+ for (const [k, v] of cr.bindings.entries()) {
+ const v2 = result.bindings.get(v);
+ if (!v2) {
+ ok = false;
+ break;
+ }
+ if (v !== v2 && v.length === v2.length && v.every((x, i) => v2[i] === x)) {
+ result.bindings.set(k, v);
+ } else if (v !== v2) {
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ result.bindings = cr.bindings;
+ }
+ }
+ if (cr.ty === result.ty && cr.path === result.path
+ && cr.bindings === result.bindings && cr.generics === result.generics
+ && cr.ty === result.ty
+ ) {
+ return cr;
+ }
+ }
+ TYPES_POOL.set(result.id, result);
+ return result;
}
/**
@@ -2801,7 +2870,7 @@ ${item.displayPath}${name}\
* object-based encoding so that the actual search code is more readable and easier to debug.
*
* The raw function search type format is generated using serde in
- * librustdoc/html/render/mod.rs: impl Serialize for IndexItemFunctionType
+ * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string
*
* @param {{
* string: string,
@@ -2970,8 +3039,8 @@ ${item.displayPath}${name}\
const fb = {
id: null,
ty: 0,
- generics: [],
- bindings: new Map(),
+ generics: EMPTY_GENERICS_ARRAY,
+ bindings: EMPTY_BINDINGS_MAP,
};
for (const [k, v] of type.bindings.entries()) {
fb.id = k;
@@ -3199,6 +3268,8 @@ ${item.displayPath}${name}\
}
currentIndex += itemTypes.length;
}
+ // Drop the (rather large) hash table used for reusing function items
+ TYPES_POOL = new Map();
}
/**