Skip to content

Commit 556da72

Browse files
authored
[WIP] Improve optional chaining checker performance (microsoft#33794)
* Improve optional chaining checker performance * Improve optional chaining checker performance * Add flags to Signature * Inline getOptionalExpression * split checks for optional chains * Cache optional call signatures
1 parent 91196fc commit 556da72

File tree

8 files changed

+210
-152
lines changed

8 files changed

+210
-152
lines changed

src/compiler/binder.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,7 @@ namespace ts {
953953
}
954954
if (expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition ||
955955
expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) {
956-
if (!isOptionalChainRoot(expression.parent)) {
956+
if (!isExpressionOfOptionalChainRoot(expression)) {
957957
return unreachableFlow;
958958
}
959959
}

src/compiler/checker.ts

+167-133
Large diffs are not rendered by default.

src/compiler/types.ts

+18-8
Original file line numberDiff line numberDiff line change
@@ -3451,8 +3451,7 @@ namespace ts {
34513451
resolvedReturnType: Type,
34523452
typePredicate: TypePredicate | undefined,
34533453
minArgumentCount: number,
3454-
hasRestParameter: boolean,
3455-
hasLiteralTypes: boolean,
3454+
flags: SignatureFlags
34563455
): Signature;
34573456
/* @internal */ createSymbol(flags: SymbolFlags, name: __String): TransientSymbol;
34583457
/* @internal */ createIndexInfo(type: Type, isReadonly: boolean, declaration?: SignatureDeclaration): IndexInfo;
@@ -4671,7 +4670,22 @@ namespace ts {
46714670
Construct,
46724671
}
46734672

4673+
/* @internal */
4674+
export const enum SignatureFlags {
4675+
None = 0,
4676+
HasRestParameter = 1 << 0, // Indicates last parameter is rest parameter
4677+
HasLiteralTypes = 1 << 1, // Indicates signature is specialized
4678+
IsOptionalCall = 1 << 2, // Indicates signature comes from a CallChain
4679+
4680+
// We do not propagate `IsOptionalCall` to instantiated signatures, as that would result in us
4681+
// attempting to add `| undefined` on each recursive call to `getReturnTypeOfSignature` when
4682+
// instantiating the return type.
4683+
PropagatingFlags = HasRestParameter | HasLiteralTypes,
4684+
}
4685+
46744686
export interface Signature {
4687+
/* @internal */ flags: SignatureFlags;
4688+
/* @internal */ checker?: TypeChecker;
46754689
declaration?: SignatureDeclaration | JSDocSignature; // Originating declaration
46764690
typeParameters?: readonly TypeParameter[]; // Type parameters (undefined if non-generic)
46774691
parameters: readonly Symbol[]; // Parameters
@@ -4688,10 +4702,6 @@ namespace ts {
46884702
/* @internal */
46894703
minArgumentCount: number; // Number of non-optional parameters
46904704
/* @internal */
4691-
hasRestParameter: boolean; // True if last parameter is rest parameter
4692-
/* @internal */
4693-
hasLiteralTypes: boolean; // True if specialized
4694-
/* @internal */
46954705
target?: Signature; // Instantiation target
46964706
/* @internal */
46974707
mapper?: TypeMapper; // Instantiation mapper
@@ -4702,11 +4712,11 @@ namespace ts {
47024712
/* @internal */
47034713
canonicalSignatureCache?: Signature; // Canonical version of signature (deferred)
47044714
/* @internal */
4715+
optionalCallSignatureCache?: Signature; // Optional chained call version of signature (deferred)
4716+
/* @internal */
47054717
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
47064718
/* @internal */
47074719
instantiations?: Map<Signature>; // Generic signature instantiation cache
4708-
/* @internal */
4709-
isOptionalCall?: boolean;
47104720
}
47114721

47124722
export const enum IndexKind {

src/compiler/utilities.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -5911,6 +5911,14 @@ namespace ts {
59115911
|| kind === SyntaxKind.CallExpression);
59125912
}
59135913

5914+
/**
5915+
* Determines whether a node is the expression preceding an optional chain (i.e. `a` in `a?.b`).
5916+
*/
5917+
/* @internal */
5918+
export function isExpressionOfOptionalChainRoot(node: Node): node is Expression & { parent: OptionalChainRoot } {
5919+
return isOptionalChainRoot(node.parent) && node.parent.expression === node;
5920+
}
5921+
59145922
export function isNewExpression(node: Node): node is NewExpression {
59155923
return node.kind === SyntaxKind.NewExpression;
59165924
}
@@ -7310,7 +7318,7 @@ namespace ts {
73107318
getSourceFileConstructor(): new (kind: SyntaxKind.SourceFile, pos?: number, end?: number) => SourceFile;
73117319
getSymbolConstructor(): new (flags: SymbolFlags, name: __String) => Symbol;
73127320
getTypeConstructor(): new (checker: TypeChecker, flags: TypeFlags) => Type;
7313-
getSignatureConstructor(): new (checker: TypeChecker) => Signature;
7321+
getSignatureConstructor(): new (checker: TypeChecker, flags: SignatureFlags) => Signature;
73147322
getSourceMapSourceConstructor(): new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource;
73157323
}
73167324

@@ -7331,7 +7339,12 @@ namespace ts {
73317339
}
73327340
}
73337341

7334-
function Signature() {}
7342+
function Signature(this: Signature, checker: TypeChecker, flags: SignatureFlags) {
7343+
this.flags = flags;
7344+
if (Debug.isDebugging) {
7345+
this.checker = checker;
7346+
}
7347+
}
73357348

73367349
function Node(this: Node, kind: SyntaxKind, pos: number, end: number) {
73377350
this.pos = pos;

src/services/codefixes/helpers.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -233,14 +233,14 @@ namespace ts.codefix {
233233
let someSigHasRestParameter = false;
234234
for (const sig of signatures) {
235235
minArgumentCount = Math.min(sig.minArgumentCount, minArgumentCount);
236-
if (sig.hasRestParameter) {
236+
if (signatureHasRestParameter(sig)) {
237237
someSigHasRestParameter = true;
238238
}
239-
if (sig.parameters.length >= maxArgsSignature.parameters.length && (!sig.hasRestParameter || maxArgsSignature.hasRestParameter)) {
239+
if (sig.parameters.length >= maxArgsSignature.parameters.length && (!signatureHasRestParameter(sig) || signatureHasRestParameter(maxArgsSignature))) {
240240
maxArgsSignature = sig;
241241
}
242242
}
243-
const maxNonRestArgs = maxArgsSignature.parameters.length - (maxArgsSignature.hasRestParameter ? 1 : 0);
243+
const maxNonRestArgs = maxArgsSignature.parameters.length - (signatureHasRestParameter(maxArgsSignature) ? 1 : 0);
244244
const maxArgsParameterSymbolNames = maxArgsSignature.parameters.map(symbol => symbol.name);
245245

246246
const parameters = createDummyParameters(maxNonRestArgs, maxArgsParameterSymbolNames, /* types */ undefined, minArgumentCount, /*inJs*/ false);

src/services/codefixes/inferFromUsage.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,7 @@ namespace ts.codefix {
11121112
}
11131113
const returnType = combineFromUsage(combineUsages(calls.map(call => call.return_)));
11141114
// TODO: GH#18217
1115-
return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
1115+
return checker.createSignature(/*declaration*/ undefined!, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, length, SignatureFlags.None);
11161116
}
11171117

11181118
function addCandidateType(usage: Usage, type: Type | undefined) {

src/services/services.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ namespace ts {
464464
}
465465

466466
class SignatureObject implements Signature {
467+
flags: SignatureFlags;
467468
checker: TypeChecker;
468469
declaration!: SignatureDeclaration;
469470
typeParameters?: TypeParameter[];
@@ -473,8 +474,6 @@ namespace ts {
473474
resolvedTypePredicate: TypePredicate | undefined;
474475
minTypeArgumentCount!: number;
475476
minArgumentCount!: number;
476-
hasRestParameter!: boolean;
477-
hasLiteralTypes!: boolean;
478477

479478
// Undefined is used to indicate the value has not been computed. If, after computing, the
480479
// symbol has no doc comment, then the empty array will be returned.
@@ -484,9 +483,11 @@ namespace ts {
484483
// symbol has no doc comment, then the empty array will be returned.
485484
jsDocTags?: JSDocTagInfo[];
486485

487-
constructor(checker: TypeChecker) {
486+
constructor(checker: TypeChecker, flags: SignatureFlags) {
488487
this.checker = checker;
488+
this.flags = flags;
489489
}
490+
490491
getDeclaration(): SignatureDeclaration {
491492
return this.declaration;
492493
}

src/services/stringCompletions.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ namespace ts.Completions.StringCompletions {
204204
const candidates: Signature[] = [];
205205
checker.getResolvedSignature(argumentInfo.invocation, candidates, argumentInfo.argumentCount);
206206
const types = flatMap(candidates, candidate => {
207-
if (!candidate.hasRestParameter && argumentInfo.argumentCount > candidate.parameters.length) return;
207+
if (!signatureHasRestParameter(candidate) && argumentInfo.argumentCount > candidate.parameters.length) return;
208208
const type = checker.getParameterType(candidate, argumentInfo.argumentIndex);
209209
isNewIdentifier = isNewIdentifier || !!(type.flags & TypeFlags.String);
210210
return getStringLiteralTypes(type, uniques);

0 commit comments

Comments
 (0)