From 3b78377cf0de87c3d17b6fd27b0627001a82b901 Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Fri, 17 Jul 2015 17:01:37 -0700 Subject: [PATCH] Clean up pushTypeResolution --- src/compiler/checker.ts | 89 +++++++++++++++++++++++------------------ src/compiler/types.ts | 2 + 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bf4f6e07b7..927e20b5d2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -159,9 +159,9 @@ namespace ts { let emitAwaiter = false; let emitGenerator = false; - let resolutionTargets: Object[] = []; + let resolutionTargets: TypeSystemEntity[] = []; let resolutionResults: boolean[] = []; - let resolutionKinds: TypeSystemObjectKind[] = []; + let resolutionPropertyNames: TypeSystemPropertyName[] = []; let mergedSymbols: Symbol[] = []; let symbolLinks: SymbolLinks[] = []; @@ -202,11 +202,11 @@ namespace ts { let assignableRelation: Map = {}; let identityRelation: Map = {}; - enum TypeSystemObjectKind { - Symbol, + enum TypeSystemPropertyName { Type, - SymbolLinks, - Signature + ResolvedBaseConstructorType, + DeclaredType, + ResolvedReturnType } initializeTypeChecker(); @@ -2185,45 +2185,58 @@ namespace ts { } } - // Push an entry on the type resolution stack. If an entry with the given target is not already on the stack, - // a new entry with that target and an associated result value of true is pushed on the stack, and the value - // true is returned. Otherwise, a circularity has occurred and the result values of the existing entry and - // all entries pushed after it are changed to false, and the value false is returned. The target object provides - // a unique identity for a particular type resolution result: Symbol instances are used to track resolution of - // SymbolLinks.type, SymbolLinks instances are used to track resolution of SymbolLinks.declaredType, and - // Signature instances are used to track resolution of Signature.resolvedReturnType. - function pushTypeResolution(target: Object, flags: TypeSystemObjectKind): boolean { - let count = resolutionTargets.length; - let i = count - 1; - let foundGoodType = false; - while (i >= 0 && !(foundGoodType = !!hasType(resolutionTargets[i], resolutionKinds[i])) && resolutionTargets[i] !== target) { - i--; - } - if (i >= 0 && !foundGoodType) { - do { - resolutionResults[i++] = false; + /** + * Push an entry on the type resolution stack. If an entry with the given target and the given property name + * is already on the stack, and no entries in between already have a type, then a circularity has occurred. + * In this case, the result values of the existing entry and all entries pushed after it are changed to false, + * and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned. + * In order to see if the same query has already been done before, the target object and the propertyName both + * must match the one passed in. + * + * @param target The symbol, type, or signature whose type is being queried + * @param propertyName The property name that should be used to query the target for its type + */ + function pushTypeResolution(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean { + let resolutionCycleStartIndex = findResolutionCycleStartIndex(target, propertyName); + if (resolutionCycleStartIndex >= 0) { + // A cycle was found + let { length } = resolutionTargets; + for (let i = resolutionCycleStartIndex; i < length; i++) { + resolutionResults[i] = false; } - while (i < count); return false; } resolutionTargets.push(target); resolutionResults.push(true); - resolutionKinds.push(flags); + resolutionPropertyNames.push(propertyName); return true; } - function hasType(target: Object, flags: TypeSystemObjectKind): Type { - if (flags === TypeSystemObjectKind.Symbol) { + function findResolutionCycleStartIndex(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): number { + for (let i = resolutionTargets.length - 1; i >= 0; i--) { + if (hasType(resolutionTargets[i], resolutionPropertyNames[i])) { + return -1; + } + if (resolutionTargets[i] === target && resolutionPropertyNames[i] === propertyName) { + return i; + } + } + + return -1; + } + + function hasType(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): Type { + if (propertyName === TypeSystemPropertyName.Type) { return getSymbolLinks(target).type; } - else if (flags === TypeSystemObjectKind.Type) { + else if (propertyName === TypeSystemPropertyName.DeclaredType) { + return getSymbolLinks(target).declaredType; + } + else if (propertyName === TypeSystemPropertyName.ResolvedBaseConstructorType) { Debug.assert(!!((target).flags & TypeFlags.Class)); return (target).resolvedBaseConstructorType; } - else if (flags === TypeSystemObjectKind.SymbolLinks) { - return (target).declaredType; - } - else if (flags === TypeSystemObjectKind.Signature) { + else if (propertyName === TypeSystemPropertyName.ResolvedReturnType) { return (target).resolvedReturnType; } @@ -2234,7 +2247,7 @@ namespace ts { // be true if no circularities were detected, or false if a circularity was found. function popTypeResolution(): boolean { resolutionTargets.pop(); - resolutionKinds.pop(); + resolutionPropertyNames.pop(); return resolutionResults.pop(); } @@ -2497,7 +2510,7 @@ namespace ts { return links.type = checkExpression((declaration).expression); } // Handle variable, parameter or property - if (!pushTypeResolution(symbol, TypeSystemObjectKind.Symbol)) { + if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { return unknownType; } let type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true); @@ -2538,7 +2551,7 @@ namespace ts { function getTypeOfAccessors(symbol: Symbol): Type { let links = getSymbolLinks(symbol); if (!links.type) { - if (!pushTypeResolution(symbol, TypeSystemObjectKind.Symbol)) { + if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { return unknownType; } let getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); @@ -2754,7 +2767,7 @@ namespace ts { if (!baseTypeNode) { return type.resolvedBaseConstructorType = undefinedType; } - if (!pushTypeResolution(type, TypeSystemObjectKind.Type)) { + if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedBaseConstructorType)) { return unknownType; } let baseConstructorType = checkExpression(baseTypeNode.expression); @@ -2881,7 +2894,7 @@ namespace ts { if (!links.declaredType) { // Note that we use the links object as the target here because the symbol object is used as the unique // identity for resolution of the 'type' property in SymbolLinks. - if (!pushTypeResolution(links, TypeSystemObjectKind.SymbolLinks)) { + if (!pushTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) { return unknownType; } let declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); @@ -3568,7 +3581,7 @@ namespace ts { function getReturnTypeOfSignature(signature: Signature): Type { if (!signature.resolvedReturnType) { - if (!pushTypeResolution(signature, TypeSystemObjectKind.Signature)) { + if (!pushTypeResolution(signature, TypeSystemPropertyName.ResolvedReturnType)) { return unknownType; } let type: Type; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f1b476d11b..edaf940e2f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1904,6 +1904,8 @@ namespace ts { isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison } + export type TypeSystemEntity = Symbol | Type | Signature; + export const enum IndexKind { String, Number,