Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustdoc-search: reuse individual types in function signatures #119756

Merged
merged 2 commits into from
Jan 9, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 91 additions & 20 deletions src/librustdoc/html/static/js/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -2717,9 +2717,33 @@ ${item.displayPath}<span class="${type}">${name}</span>\
* @return {Array<FunctionSearchType>}
*/
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<number, Array<FunctionType>>}
*/
const EMPTY_BINDINGS_MAP = new Map();

/**
* Empty, immutable map used in item search types with no bindings.
*
* @type {Array<FunctionType>}
*/
const EMPTY_GENERICS_ARRAY = [];

/**
* Object pool for function types with no bindings or generics.
* This is reset after loading the index.
*
* @type {Map<number|null, FunctionType>}
*/
let TYPES_POOL = new Map();

/**
* Converts a single type.
*
Expand All @@ -2732,15 +2756,15 @@ ${item.displayPath}<span class="${type}">${name}</span>\
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
Expand All @@ -2759,38 +2783,83 @@ ${item.displayPath}<span class="${type}">${name}</span>\
];
}));
} 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;
}

/**
Expand All @@ -2801,7 +2870,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
* 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,
Expand Down Expand Up @@ -2970,8 +3039,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
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;
Expand Down Expand Up @@ -3199,6 +3268,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
}
currentIndex += itemTypes.length;
}
// Drop the (rather large) hash table used for reusing function items
TYPES_POOL = new Map();
}

/**
Expand Down
Loading