Optimize caching of type literals

This commit is contained in:
Anders Hejlsberg
2017-09-03 08:53:04 -07:00
parent 601a21c77b
commit 319617c5d8
+31 -15
View File
@@ -8179,33 +8179,37 @@ namespace ts {
}
function getAnonymousTypeInstantiation(type: AnonymousType, mapper: TypeMapper) {
if (type.objectFlags & ObjectFlags.Instantiated) {
mapper = combineTypeMappers(type.mapper, mapper);
type = type.target;
}
const symbol = type.symbol;
const target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type;
const symbol = target.symbol;
const links = getSymbolLinks(symbol);
if (!links.typeParameters) {
// This first time an anonymous type is instantiated we compute and store a list of the type
// parameters that are in scope (and therefore potentially referenced).
const typeParameters = getOuterTypeParameters(symbol.declarations[0], /*includeThisTypes*/ true);
links.typeParameters = typeParameters || emptyArray;
if (typeParameters) {
let typeParameters = links.typeParameters;
if (!typeParameters) {
// The first time an anonymous type is instantiated we compute and store a list of the type
// parameters that are in scope (and therefore potentially referenced). For type literals that
// aren't the right hand side of a generic type alias declaration we optimize by reducing the
// set of type parameters to those that are actually referenced somewhere in the literal.
const declaration = symbol.declarations[0];
const outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true) || emptyArray;
typeParameters = symbol.flags & SymbolFlags.TypeLiteral && !target.aliasTypeArguments ?
filter(outerTypeParameters, tp => isTypeParameterReferencedWithin(tp, declaration)) :
outerTypeParameters;
links.typeParameters = typeParameters;
if (typeParameters.length) {
links.instantiations = createMap<Type>();
links.instantiations.set(getTypeListId(typeParameters), type);
links.instantiations.set(getTypeListId(typeParameters), target);
}
}
const typeParameters = links.typeParameters;
if (typeParameters.length) {
// We are instantiating an anonymous type that has one or more type parameters in scope. Apply the
// mapper to the type parameters to produce the effective list of type arguments, and compute the
// instantiation cache key from the type IDs of the type arguments.
const typeArguments = map(typeParameters, mapper);
const combinedMapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper;
const typeArguments = map(typeParameters, combinedMapper);
const id = getTypeListId(typeArguments);
let result = links.instantiations.get(id);
if (!result) {
const newMapper = createTypeMapper(typeParameters, typeArguments);
result = type.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(<MappedType>type, newMapper) : instantiateAnonymousType(type, newMapper);
result = target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(<MappedType>target, newMapper) : instantiateAnonymousType(target, newMapper);
links.instantiations.set(id, result);
}
return result;
@@ -8213,6 +8217,16 @@ namespace ts {
return type;
}
function isTypeParameterReferencedWithin(tp: TypeParameter, node: Node) {
return tp.isThisType ? forEachChild(node, checkThis) : forEachChild(node, checkIdentifier);
function checkThis(node: Node): boolean {
return node.kind === SyntaxKind.ThisType || forEachChild(node, checkThis);
}
function checkIdentifier(node: Node): boolean {
return node.kind === SyntaxKind.Identifier && isPartOfTypeNode(node) && getTypeFromTypeNode(<TypeNode>node) === tp || forEachChild(node, checkIdentifier);
}
}
function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type {
// Check if we have a homomorphic mapped type, i.e. a type of the form { [P in keyof T]: X } for some
// type variable T. If so, the mapped type is distributive over a union type and when T is instantiated
@@ -10420,6 +10434,7 @@ namespace ts {
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
let symbolStack: Symbol[];
let visited: Map<boolean>;
//sys.write(typeToString(originalSource) + " ==> " + typeToString(originalTarget) + "\n");
inferFromTypes(originalSource, originalTarget);
function inferFromTypes(source: Type, target: Type) {
@@ -10487,6 +10502,7 @@ namespace ts {
const inference = getInferenceInfoForType(target);
if (inference) {
if (!inference.isFixed) {
//sys.write(" " + typeToString(source) + "\n");
if (!inference.candidates || priority < inference.priority) {
inference.candidates = [source];
inference.priority = priority;