Skip to content

Commit 1b095c1

Browse files
authored
Various node builder related performance optimizations (#1001)
1 parent f7c1c37 commit 1b095c1

File tree

6 files changed

+43
-53
lines changed

6 files changed

+43
-53
lines changed

internal/checker/checker.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15172,7 +15172,7 @@ type ExportCollisionTable = map[string]*ExportCollision
1517215172

1517315173
func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports ast.SymbolTable, typeOnlyExportStarMap map[string]*ast.Node) {
1517415174
var visitedSymbols []*ast.Symbol
15175-
var nonTypeOnlyNames core.Set[string]
15175+
nonTypeOnlyNames := core.NewSetWithSizeHint[string](len(moduleSymbol.Exports))
1517615176
// The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example,
1517715177
// module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error.
1517815178
var visit func(*ast.Symbol, *ast.Node, bool) ast.SymbolTable

internal/checker/nodebuilder.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ func (b *NodeBuilder) enterContext(enclosingDeclaration *ast.Node, flags nodebui
3636
tracker = NewSymbolTrackerImpl(b.impl.ctx, nil, b.host)
3737
b.impl.ctx.tracker = tracker
3838
}
39-
b.impl.initializeClosures() // recapture ctx
4039
b.ctxStack = append(b.ctxStack, b.impl.ctx)
4140
}
4241

@@ -159,7 +158,7 @@ func (b *NodeBuilder) TypePredicateToTypePredicateNode(predicate *TypePredicate,
159158
// TypeToTypeNode implements NodeBuilderInterface.
160159
func (b *NodeBuilder) TypeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
161160
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
162-
return b.exitContext(b.impl.typeToTypeNodeWorker(typ))
161+
return b.exitContext(b.impl.typeToTypeNode(typ))
163162
}
164163

165164
// var _ NodeBuilderInterface = NewNodeBuilderAPI(nil, nil)

internal/checker/nodebuilderimpl.go

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,6 @@ type nodeBuilderImpl struct {
9191
links core.LinkStore[*ast.Node, NodeBuilderLinks]
9292
symbolLinks core.LinkStore[*ast.Symbol, NodeBuilderSymbolLinks]
9393

94-
// closures
95-
typeToTypeNode func(t *Type) *ast.TypeNode
96-
typeReferenceToTypeNode func(t *Type) *ast.TypeNode
97-
conditionalTypeToTypeNode func(t *Type) *ast.TypeNode
98-
createTypeNodeFromObjectType func(t *Type) *ast.TypeNode
99-
isStringNamed func(d *ast.Declaration) bool
100-
isSingleQuotedStringNamed func(d *ast.Declaration) bool
101-
10294
// state
10395
ctx *NodeBuilderContext
10496
}
@@ -111,20 +103,10 @@ const (
111103
// Node builder utility functions
112104

113105
func newNodeBuilderImpl(ch *Checker, e *printer.EmitContext) nodeBuilderImpl {
114-
result := nodeBuilderImpl{f: e.Factory.AsNodeFactory(), ch: ch, e: e, typeToTypeNode: nil, typeReferenceToTypeNode: nil, conditionalTypeToTypeNode: nil, ctx: nil}
115-
result.initializeClosures()
106+
result := nodeBuilderImpl{f: e.Factory.AsNodeFactory(), ch: ch, e: e}
116107
return result
117108
}
118109

119-
func (b *nodeBuilderImpl) initializeClosures() {
120-
b.typeToTypeNode = b.typeToTypeNodeWorker
121-
b.typeReferenceToTypeNode = b.typeReferenceToTypeNodeWorker
122-
b.conditionalTypeToTypeNode = b.conditionalTypeToTypeNodeWorker
123-
b.createTypeNodeFromObjectType = b.createTypeNodeFromObjectTypeWorker
124-
b.isStringNamed = b.isStringNamedWorker
125-
b.isSingleQuotedStringNamed = b.isSingleQuotedStringNamedWorker
126-
}
127-
128110
func (b *nodeBuilderImpl) saveRestoreFlags() func() {
129111
flags := b.ctx.flags
130112
internalFlags := b.ctx.internalFlags
@@ -1976,7 +1958,7 @@ func (b *nodeBuilderImpl) createPropertyNameNodeForIdentifierOrLiteral(name stri
19761958
return result
19771959
}
19781960

1979-
func (b *nodeBuilderImpl) isStringNamedWorker(d *ast.Declaration) bool {
1961+
func (b *nodeBuilderImpl) isStringNamed(d *ast.Declaration) bool {
19801962
name := ast.GetNameOfDeclaration(d)
19811963
if name == nil {
19821964
return false
@@ -1992,7 +1974,7 @@ func (b *nodeBuilderImpl) isStringNamedWorker(d *ast.Declaration) bool {
19921974
return ast.IsStringLiteral(name)
19931975
}
19941976

1995-
func (b *nodeBuilderImpl) isSingleQuotedStringNamedWorker(d *ast.Declaration) bool {
1977+
func (b *nodeBuilderImpl) isSingleQuotedStringNamed(d *ast.Declaration) bool {
19961978
return false // !!!
19971979
// TODO: actually support single-quote-style-maintenance
19981980
// name := ast.GetNameOfDeclaration(d)
@@ -2216,7 +2198,7 @@ func (b *nodeBuilderImpl) createTypeNodesFromResolvedType(resolvedType *Structur
22162198
}
22172199
}
22182200

2219-
func (b *nodeBuilderImpl) createTypeNodeFromObjectTypeWorker(t *Type) *ast.TypeNode {
2201+
func (b *nodeBuilderImpl) createTypeNodeFromObjectType(t *Type) *ast.TypeNode {
22202202
if b.ch.isGenericMappedType(t) || (t.objectFlags&ObjectFlagsMapped != 0 && t.AsMappedType().containsError) {
22212203
return b.createMappedTypeNodeFromType(t)
22222204
}
@@ -2329,7 +2311,7 @@ func (b *nodeBuilderImpl) createAnonymousTypeNode(t *Type) *ast.TypeNode {
23292311
if _, ok := b.ctx.visitedTypes[typeId]; ok {
23302312
return b.createElidedInformationPlaceholder()
23312313
}
2332-
return b.visitAndTransformType(t, b.createTypeNodeFromObjectType)
2314+
return b.visitAndTransformType(t, (*nodeBuilderImpl).createTypeNodeFromObjectType)
23332315
}
23342316
var isInstanceType ast.SymbolFlags
23352317
if isClassInstanceSide(b.ch, t) {
@@ -2355,7 +2337,7 @@ func (b *nodeBuilderImpl) createAnonymousTypeNode(t *Type) *ast.TypeNode {
23552337
return b.createElidedInformationPlaceholder()
23562338
}
23572339
} else {
2358-
return b.visitAndTransformType(t, b.createTypeNodeFromObjectType)
2340+
return b.visitAndTransformType(t, (*nodeBuilderImpl).createTypeNodeFromObjectType)
23592341
}
23602342
} else {
23612343
// Anonymous types without a symbol are never circular.
@@ -2386,12 +2368,12 @@ func (b *nodeBuilderImpl) typeToTypeNodeOrCircularityElision(t *Type) *ast.TypeN
23862368
}
23872369
return b.createElidedInformationPlaceholder()
23882370
}
2389-
return b.visitAndTransformType(t, b.typeToTypeNode)
2371+
return b.visitAndTransformType(t, (*nodeBuilderImpl).typeToTypeNode)
23902372
}
23912373
return b.typeToTypeNode(t)
23922374
}
23932375

2394-
func (b *nodeBuilderImpl) conditionalTypeToTypeNodeWorker(_t *Type) *ast.TypeNode {
2376+
func (b *nodeBuilderImpl) conditionalTypeToTypeNode(_t *Type) *ast.TypeNode {
23952377
t := _t.AsConditionalType()
23962378
checkTypeNode := b.typeToTypeNode(t.checkType)
23972379
b.ctx.approximateLength += 15
@@ -2449,7 +2431,7 @@ func (b *nodeBuilderImpl) getParentSymbolOfTypeParameter(typeParameter *TypePara
24492431
return b.ch.getSymbolOfNode(host)
24502432
}
24512433

2452-
func (b *nodeBuilderImpl) typeReferenceToTypeNodeWorker(t *Type) *ast.TypeNode {
2434+
func (b *nodeBuilderImpl) typeReferenceToTypeNode(t *Type) *ast.TypeNode {
24532435
var typeArguments []*Type = b.ch.getTypeArguments(t)
24542436
if t.Target() == b.ch.globalArrayType || t.Target() == b.ch.globalReadonlyArrayType {
24552437
if b.ctx.flags&nodebuilder.FlagsWriteArrayAsGenericType != 0 {
@@ -2583,7 +2565,7 @@ func (b *nodeBuilderImpl) typeReferenceToTypeNodeWorker(t *Type) *ast.TypeNode {
25832565
}
25842566
}
25852567

2586-
func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(t *Type) *ast.TypeNode) *ast.TypeNode {
2568+
func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *nodeBuilderImpl, t *Type) *ast.TypeNode) *ast.TypeNode {
25872569
typeId := t.id
25882570
isConstructorObject := t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsClass != 0
25892571
var id *CompositeSymbolIdentity
@@ -2629,7 +2611,7 @@ func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(t *Type)
26292611
prevTrackedSymbols := b.ctx.trackedSymbols
26302612
b.ctx.trackedSymbols = nil
26312613
startLength := b.ctx.approximateLength
2632-
result := transform(t)
2614+
result := transform(b, t)
26332615
addedLength := b.ctx.approximateLength - startLength
26342616
if !b.ctx.reportedDiagnostic && !b.ctx.encounteredError {
26352617
links := b.links.Get(b.ctx.enclosingDeclaration)
@@ -2668,7 +2650,7 @@ func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(t *Type)
26682650
// }
26692651
}
26702652

2671-
func (b *nodeBuilderImpl) typeToTypeNodeWorker(t *Type) *ast.TypeNode {
2653+
func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
26722654
inTypeAlias := b.ctx.flags & nodebuilder.FlagsInTypeAlias
26732655
b.ctx.flags &^= nodebuilder.FlagsInTypeAlias
26742656

@@ -2830,7 +2812,7 @@ func (b *nodeBuilderImpl) typeToTypeNodeWorker(t *Type) *ast.TypeNode {
28302812
if objectFlags&ObjectFlagsReference != 0 {
28312813
// Debug.assert(t.flags&TypeFlagsObject != 0) // !!!
28322814
if t.AsTypeReference().node != nil {
2833-
return b.visitAndTransformType(t, b.typeReferenceToTypeNode)
2815+
return b.visitAndTransformType(t, (*nodeBuilderImpl).typeReferenceToTypeNode)
28342816
} else {
28352817
return b.typeReferenceToTypeNode(t)
28362818
}
@@ -2936,7 +2918,7 @@ func (b *nodeBuilderImpl) typeToTypeNodeWorker(t *Type) *ast.TypeNode {
29362918
return b.f.NewIndexedAccessTypeNode(objectTypeNode, indexTypeNode)
29372919
}
29382920
if t.flags&TypeFlagsConditional != 0 {
2939-
return b.visitAndTransformType(t, b.conditionalTypeToTypeNode)
2921+
return b.visitAndTransformType(t, (*nodeBuilderImpl).conditionalTypeToTypeNode)
29402922
}
29412923
if t.flags&TypeFlagsSubstitution != 0 {
29422924
typeNode := b.typeToTypeNode(t.AsSubstitutionType().baseType)

internal/printer/emitcontext.go

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ type EmitContext struct {
2424
varScopeStack core.Stack[*varScope]
2525
letScopeStack core.Stack[*varScope]
2626
emitHelpers collections.OrderedSet[*EmitHelper]
27-
28-
isCustomPrologue func(node *ast.Statement) bool
29-
isHoistedFunction func(node *ast.Statement) bool
30-
isHoistedVariableStatement func(node *ast.Statement) bool
3127
}
3228

3329
type varScope struct {
@@ -38,12 +34,15 @@ type varScope struct {
3834
func NewEmitContext() *EmitContext {
3935
c := &EmitContext{}
4036
c.Factory = NewNodeFactory(c)
41-
c.isCustomPrologue = c.isCustomPrologueWorker
42-
c.isHoistedFunction = c.isHoistedFunctionWorker
43-
c.isHoistedVariableStatement = c.isHoistedVariableStatementWorker
4437
return c
4538
}
4639

40+
func (c *EmitContext) Reset() {
41+
*c = EmitContext{
42+
Factory: c.Factory,
43+
}
44+
}
45+
4746
func (c *EmitContext) onCreate(node *ast.Node) {
4847
node.Flags |= ast.NodeFlagsSynthesized
4948
}
@@ -260,14 +259,14 @@ func (c *EmitContext) mergeEnvironment(statements []*ast.Statement, declarations
260259

261260
// find standard prologues on left in the following order: standard directives, hoisted functions, hoisted variables, other custom
262261
leftStandardPrologueEnd := findSpanEnd(statements, ast.IsPrologueDirective, 0)
263-
leftHoistedFunctionsEnd := findSpanEnd(statements, c.isHoistedFunction, leftStandardPrologueEnd)
264-
leftHoistedVariablesEnd := findSpanEnd(statements, c.isHoistedVariableStatement, leftHoistedFunctionsEnd)
262+
leftHoistedFunctionsEnd := findSpanEndWithEmitContext(c, statements, (*EmitContext).isHoistedFunction, leftStandardPrologueEnd)
263+
leftHoistedVariablesEnd := findSpanEndWithEmitContext(c, statements, (*EmitContext).isHoistedVariableStatement, leftHoistedFunctionsEnd)
265264

266265
// find standard prologues on right in the following order: standard directives, hoisted functions, hoisted variables, other custom
267266
rightStandardPrologueEnd := findSpanEnd(declarations, ast.IsPrologueDirective, 0)
268-
rightHoistedFunctionsEnd := findSpanEnd(declarations, c.isHoistedFunction, rightStandardPrologueEnd)
269-
rightHoistedVariablesEnd := findSpanEnd(declarations, c.isHoistedVariableStatement, rightHoistedFunctionsEnd)
270-
rightCustomPrologueEnd := findSpanEnd(declarations, c.isCustomPrologue, rightHoistedVariablesEnd)
267+
rightHoistedFunctionsEnd := findSpanEndWithEmitContext(c, declarations, (*EmitContext).isHoistedFunction, rightStandardPrologueEnd)
268+
rightHoistedVariablesEnd := findSpanEndWithEmitContext(c, declarations, (*EmitContext).isHoistedVariableStatement, rightHoistedFunctionsEnd)
269+
rightCustomPrologueEnd := findSpanEndWithEmitContext(c, declarations, (*EmitContext).isCustomPrologue, rightHoistedVariablesEnd)
271270
if rightCustomPrologueEnd != len(declarations) {
272271
panic("Expected declarations to be valid standard or custom prologues")
273272
}
@@ -316,20 +315,20 @@ func (c *EmitContext) mergeEnvironment(statements []*ast.Statement, declarations
316315
return left, changed
317316
}
318317

319-
func (c *EmitContext) isCustomPrologueWorker(node *ast.Statement) bool {
318+
func (c *EmitContext) isCustomPrologue(node *ast.Statement) bool {
320319
return c.EmitFlags(node)&EFCustomPrologue != 0
321320
}
322321

323-
func (c *EmitContext) isHoistedFunctionWorker(node *ast.Statement) bool {
324-
return c.isCustomPrologueWorker(node) && ast.IsFunctionDeclaration(node)
322+
func (c *EmitContext) isHoistedFunction(node *ast.Statement) bool {
323+
return c.isCustomPrologue(node) && ast.IsFunctionDeclaration(node)
325324
}
326325

327326
func isHoistedVariable(node *ast.VariableDeclarationNode) bool {
328327
return ast.IsIdentifier(node.Name()) && node.Initializer() == nil
329328
}
330329

331-
func (c *EmitContext) isHoistedVariableStatementWorker(node *ast.Statement) bool {
332-
return c.isCustomPrologueWorker(node) &&
330+
func (c *EmitContext) isHoistedVariableStatement(node *ast.Statement) bool {
331+
return c.isCustomPrologue(node) &&
333332
ast.IsVariableStatement(node) &&
334333
core.Every(node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes, isHoistedVariable)
335334
}

internal/printer/utilities.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,14 @@ func makeIdentifierFromModuleName(moduleName string) string {
728728
return builder.String()
729729
}
730730

731+
func findSpanEndWithEmitContext[T any](c *EmitContext, array []T, test func(c *EmitContext, value T) bool, start int) int {
732+
i := start
733+
for i < len(array) && test(c, array[i]) {
734+
i++
735+
}
736+
return i
737+
}
738+
731739
func findSpanEnd[T any](array []T, test func(value T) bool, start int) int {
732740
i := start
733741
for i < len(array) && test(array[i]) {

internal/testutil/tsbaseline/type_symbol_baseline.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ func (walker *typeWriterWalker) writeTypeOrSymbol(node *ast.Node, isSymbolWalk b
346346
fileChecker, done := walker.getTypeCheckerForCurrentFile()
347347
defer done()
348348

349+
ctx := printer.NewEmitContext()
350+
349351
if !isSymbolWalk {
350352
// Don't try to get the type of something that's already a type.
351353
// Exception for `T` in `type T = something` because that may evaluate to some interesting type.
@@ -382,7 +384,7 @@ func (walker *typeWriterWalker) writeTypeOrSymbol(node *ast.Node, isSymbolWalk b
382384
!isIntrinsicJsxTag(node, walker.currentSourceFile) {
383385
typeString = t.AsIntrinsicType().IntrinsicName()
384386
} else {
385-
ctx := printer.NewEmitContext()
387+
ctx.Reset()
386388
builder := checker.NewNodeBuilder(fileChecker, ctx)
387389
typeFormatFlags := checker.TypeFormatFlagsNoTruncation | checker.TypeFormatFlagsAllowUniqueESSymbolType | checker.TypeFormatFlagsGenerateNamesForShadowedTypeParams
388390
typeNode := builder.TypeToTypeNode(t, node.Parent, nodebuilder.Flags(typeFormatFlags&checker.TypeFormatFlagsNodeBuilderFlagsMask)|nodebuilder.FlagsIgnoreErrors, nodebuilder.InternalFlagsAllowUnresolvedNames, nil)

0 commit comments

Comments
 (0)