Skip to content

Commit 44ada08

Browse files
committed
Guard against infinite type instantiations and constraints
1 parent 60b8f8c commit 44ada08

File tree

2 files changed

+37
-24
lines changed

2 files changed

+37
-24
lines changed

src/compiler/checker.ts

+37-20
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ namespace ts {
5353
let typeCount = 0;
5454
let symbolCount = 0;
5555
let enumCount = 0;
56-
let symbolInstantiationDepth = 0;
56+
let instantiationDepth = 0;
57+
let constraintDepth = 0;
5758

5859
const emptySymbols = createSymbolTable();
5960
const identityMapper: (type: Type) => Type = identity;
@@ -5295,22 +5296,14 @@ namespace ts {
52955296
function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
52965297
const links = getSymbolLinks(symbol);
52975298
if (!links.type) {
5298-
if (symbolInstantiationDepth === 100) {
5299-
error(symbol.valueDeclaration, Diagnostics.Generic_type_instantiation_is_excessively_deep_and_possibly_infinite);
5300-
links.type = errorType;
5299+
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
5300+
return links.type = errorType;
53015301
}
5302-
else {
5303-
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
5304-
return links.type = errorType;
5305-
}
5306-
symbolInstantiationDepth++;
5307-
let type = instantiateType(getTypeOfSymbol(links.target!), links.mapper!);
5308-
symbolInstantiationDepth--;
5309-
if (!popTypeResolution()) {
5310-
type = reportCircularityError(symbol);
5311-
}
5312-
links.type = type;
5302+
let type = instantiateType(getTypeOfSymbol(links.target!), links.mapper!);
5303+
if (!popTypeResolution()) {
5304+
type = reportCircularityError(symbol);
53135305
}
5306+
links.type = type;
53145307
}
53155308
return links.type;
53165309
}
@@ -7003,6 +6996,7 @@ namespace ts {
70036996
* circularly references the type variable.
70046997
*/
70056998
function getResolvedBaseConstraint(type: InstantiableType | UnionOrIntersectionType): Type {
6999+
let nonTerminating = false;
70067000
return type.resolvedBaseConstraint ||
70077001
(type.resolvedBaseConstraint = getTypeWithThisArgument(getImmediateBaseConstraint(type), type));
70087002

@@ -7011,8 +7005,18 @@ namespace ts {
70117005
if (!pushTypeResolution(t, TypeSystemPropertyName.ImmediateBaseConstraint)) {
70127006
return circularConstraintType;
70137007
}
7008+
if (constraintDepth === 50) {
7009+
// We have reached 50 recursive invocations of getImmediateBaseConstraint and there is a
7010+
// very high likelyhood we're dealing with an infinite generic type that perpetually generates
7011+
// new type identities as we descend into it. We stop the recursion here and mark this type
7012+
// and the outer types as having circular constraints.
7013+
nonTerminating = true;
7014+
return t.immediateBaseConstraint = noConstraintType;
7015+
}
7016+
constraintDepth++;
70147017
let result = computeBaseConstraint(getSimplifiedType(t));
7015-
if (!popTypeResolution()) {
7018+
constraintDepth--;
7019+
if (!popTypeResolution() || nonTerminating) {
70167020
result = circularConstraintType;
70177021
}
70187022
t.immediateBaseConstraint = result || noConstraintType;
@@ -10282,23 +10286,36 @@ namespace ts {
1028210286
return getConditionalType(root, mapper);
1028310287
}
1028410288

10289+
function instantiateWithDepthCheck(type: Type, mapper: TypeMapper, instantiator: (type: Type, mapper: TypeMapper) => Type): Type {
10290+
if (instantiationDepth < 50) {
10291+
instantiationDepth++;
10292+
const result = instantiator(type, mapper);
10293+
instantiationDepth--;
10294+
return result;
10295+
}
10296+
// We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing
10297+
// with a combination of infinite generic types that perpetually generate new type identities. We stop
10298+
// the recursion here by yielding the error type.
10299+
return errorType;
10300+
}
10301+
1028510302
function instantiateType(type: Type, mapper: TypeMapper | undefined): Type;
1028610303
function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined;
1028710304
function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined {
1028810305
if (type && mapper && mapper !== identityMapper) {
1028910306
if (type.flags & TypeFlags.TypeParameter) {
10290-
return mapper(<TypeParameter>type);
10307+
return mapper(type);
1029110308
}
1029210309
if (type.flags & TypeFlags.Object) {
1029310310
if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
1029410311
// If the anonymous type originates in a declaration of a function, method, class, or
1029510312
// interface, in an object type literal, or in an object literal expression, we may need
1029610313
// to instantiate the type because it might reference a type parameter.
1029710314
return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ?
10298-
getAnonymousTypeInstantiation(<AnonymousType>type, mapper) : type;
10315+
instantiateWithDepthCheck(type, mapper, getAnonymousTypeInstantiation) : type;
1029910316
}
1030010317
if ((<ObjectType>type).objectFlags & ObjectFlags.Mapped) {
10301-
return getAnonymousTypeInstantiation(<MappedType>type, mapper);
10318+
return instantiateWithDepthCheck(type, mapper, getAnonymousTypeInstantiation);
1030210319
}
1030310320
if ((<ObjectType>type).objectFlags & ObjectFlags.Reference) {
1030410321
const typeArguments = (<TypeReference>type).typeArguments;
@@ -10323,7 +10340,7 @@ namespace ts {
1032310340
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper));
1032410341
}
1032510342
if (type.flags & TypeFlags.Conditional) {
10326-
return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper));
10343+
return instantiateWithDepthCheck(type, combineTypeMappers((<ConditionalType>type).mapper, mapper), getConditionalTypeInstantiation);
1032710344
}
1032810345
if (type.flags & TypeFlags.Substitution) {
1032910346
return instantiateType((<SubstitutionType>type).typeVariable, mapper);

src/compiler/diagnosticMessages.json

-4
Original file line numberDiff line numberDiff line change
@@ -1964,10 +1964,6 @@
19641964
"category": "Error",
19651965
"code": 2549
19661966
},
1967-
"Generic type instantiation is excessively deep and possibly infinite.": {
1968-
"category": "Error",
1969-
"code": 2550
1970-
},
19711967
"Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?": {
19721968
"category": "Error",
19731969
"code": 2551

0 commit comments

Comments
 (0)