diff --git a/tsc/internal/ast/symbol.go b/tsc/internal/ast/symbol.go index 95cb4f2154d27..00312310691c5 100644 --- a/tsc/internal/ast/symbol.go +++ b/tsc/internal/ast/symbol.go @@ -4,6 +4,7 @@ import ( "strings" "sync/atomic" + "github.com/microsoft/TypeScript/tsc/internal/core" "github.com/microsoft/TypeScript/tsc/internal/debug" ) @@ -23,10 +24,20 @@ type symbolData struct { name string declarations []*Node valueDeclaration *Node - members SymbolTable - exports SymbolTable parent *Symbol - exportSymbol *Symbol + extra *symbolExtra +} + +type symbolExtra struct { + members SymbolTable + exports SymbolTable + exportSymbol *Symbol +} + +// SymbolExtraArena batches optional symbol metadata without adding an allocator +// pointer to every symbol. References from symbolData keep its blocks alive. +type SymbolExtraArena struct { + arena core.Arena[symbolExtra] } func (s *Symbol) Flags() SymbolFlags { return s.flags } @@ -34,20 +45,60 @@ func (s *Symbol) CheckFlags() CheckFlags { return s.checkFlags } func (s *Symbol) Name() string { return s.data.name } func (s *Symbol) Declarations() []*Node { return s.data.declarations } func (s *Symbol) ValueDeclaration() *Node { return s.data.valueDeclaration } -func (s *Symbol) Members() SymbolTable { return s.data.members } -func (s *Symbol) Exports() SymbolTable { return s.data.exports } func (s *Symbol) Parent() *Symbol { return s.data.parent } -func (s *Symbol) ExportSymbol() *Symbol { return s.data.exportSymbol } + +func (s *Symbol) Members() SymbolTable { + if s.data.extra == nil { + return nil + } + return s.data.extra.members +} + +func (s *Symbol) Exports() SymbolTable { + if s.data.extra == nil { + return nil + } + return s.data.extra.exports +} + +func (s *Symbol) ExportSymbol() *Symbol { + if s.data.extra == nil { + return nil + } + return s.data.extra.exportSymbol +} func (s *Symbol) SetFlags(value SymbolFlags) { s.flags = value } func (s *Symbol) SetCheckFlags(value CheckFlags) { s.checkFlags = value } func (s *Symbol) SetName(value string) { s.data.name = value } func (s *Symbol) SetDeclarations(value []*Node) { s.data.declarations = value } func (s *Symbol) SetValueDeclaration(value *Node) { s.data.valueDeclaration = value } -func (s *Symbol) SetMembers(value SymbolTable) { s.data.members = value } -func (s *Symbol) SetExports(value SymbolTable) { s.data.exports = value } func (s *Symbol) SetParent(value *Symbol) { s.data.parent = value } -func (s *Symbol) SetExportSymbol(value *Symbol) { s.data.exportSymbol = value } + +func (s *Symbol) ensureExtra(arena *SymbolExtraArena) *symbolExtra { + if s.data.extra == nil { + s.data.extra = arena.arena.New() + } + return s.data.extra +} + +func (s *Symbol) SetMembers(value SymbolTable, arena *SymbolExtraArena) { + if value != nil || s.data.extra != nil { + s.ensureExtra(arena).members = value + } +} + +func (s *Symbol) SetExports(value SymbolTable, arena *SymbolExtraArena) { + if value != nil || s.data.extra != nil { + s.ensureExtra(arena).exports = value + } +} + +func (s *Symbol) SetExportSymbol(value *Symbol, arena *SymbolExtraArena) { + if value != nil || s.data.extra != nil { + s.ensureExtra(arena).exportSymbol = value + } +} // SymbolWithData is a helper structure that contains both a Symbol and its associated symbolData. type SymbolWithData struct { diff --git a/tsc/internal/ast/symbol_test.go b/tsc/internal/ast/symbol_test.go index 3c1997e352b20..1f8f44aaa66d2 100644 --- a/tsc/internal/ast/symbol_test.go +++ b/tsc/internal/ast/symbol_test.go @@ -2,7 +2,9 @@ package ast_test import ( "reflect" + "runtime" "testing" + "unsafe" "github.com/microsoft/TypeScript/tsc/internal/ast" "gotest.tools/v3/assert" @@ -17,6 +19,7 @@ func TestSymbolFieldsArePrivate(t *testing.T) { func TestSymbolAccessors(t *testing.T) { t.Parallel() + var arena ast.SymbolExtraArena symbol := ast.NewSymbol() assert.Equal(t, symbol.Flags(), ast.SymbolFlagsNone) assert.Equal(t, symbol.CheckFlags(), ast.CheckFlags(0)) @@ -43,10 +46,10 @@ func TestSymbolAccessors(t *testing.T) { symbol.SetName("C") symbol.SetDeclarations(declarations) symbol.SetValueDeclaration(declaration) - symbol.SetMembers(members) - symbol.SetExports(exports) + symbol.SetMembers(members, &arena) + symbol.SetExports(exports, &arena) symbol.SetParent(parent) - symbol.SetExportSymbol(exportSymbol) + symbol.SetExportSymbol(exportSymbol, &arena) assert.Equal(t, symbol.Flags(), ast.SymbolFlagsClass|ast.SymbolFlagsTransient) assert.Equal(t, symbol.CheckFlags(), ast.CheckFlagsReadonly) @@ -72,10 +75,10 @@ func TestSymbolAccessors(t *testing.T) { symbol.SetName("") symbol.SetDeclarations(nil) symbol.SetValueDeclaration(nil) - symbol.SetMembers(nil) - symbol.SetExports(nil) + symbol.SetMembers(nil, &arena) + symbol.SetExports(nil, &arena) symbol.SetParent(nil) - symbol.SetExportSymbol(nil) + symbol.SetExportSymbol(nil, &arena) assert.Equal(t, symbol.Flags(), ast.SymbolFlagsNone) assert.Equal(t, symbol.CheckFlags(), ast.CheckFlags(0)) @@ -90,24 +93,76 @@ func TestSymbolAccessors(t *testing.T) { func TestSymbolTableInitialization(t *testing.T) { t.Parallel() + var arena ast.SymbolExtraArena symbol := ast.NewSymbol() member := ast.NewSymbol() export := ast.NewSymbol() - members := ast.GetMembers(symbol) + members := ast.GetMembers(symbol, &arena) members["member"] = member assert.Equal(t, symbol.Members()["member"], member) - assert.Equal(t, ast.GetMembers(symbol)["member"], member) + assert.Equal(t, ast.GetMembers(symbol, &arena)["member"], member) - exports := ast.GetExports(symbol) + exports := ast.GetExports(symbol, &arena) exports["export"] = export assert.Equal(t, symbol.Exports()["export"], export) - assert.Equal(t, ast.GetExports(symbol)["export"], export) + assert.Equal(t, ast.GetExports(symbol, &arena)["export"], export) - symbol.SetMembers(nil) - symbol.SetExports(nil) - assert.Assert(t, ast.GetMembers(symbol) != nil) - assert.Assert(t, ast.GetExports(symbol) != nil) + symbol.SetMembers(nil, &arena) + symbol.SetExports(nil, &arena) + assert.Assert(t, ast.GetMembers(symbol, &arena) != nil) + assert.Assert(t, ast.GetExports(symbol, &arena) != nil) assert.Equal(t, len(symbol.Members()), 0) assert.Equal(t, len(symbol.Exports()), 0) } + +func TestSymbolHotRecordLayout(t *testing.T) { + t.Parallel() + assert.Equal(t, unsafe.Sizeof(ast.SymbolWithData{}), unsafe.Sizeof(ast.Symbol{})+8*unsafe.Sizeof((*ast.Node)(nil))) +} + +func TestSymbolNilExtraFieldsDoNotAllocate(t *testing.T) { //nolint:paralleltest + var arena ast.SymbolExtraArena + symbol := ast.NewSymbol() + allocs := testing.AllocsPerRun(10, func() { + symbol.SetMembers(nil, &arena) + symbol.SetExports(nil, &arena) + symbol.SetExportSymbol(nil, &arena) + }) + assert.Equal(t, allocs, float64(0)) + assert.Assert(t, reflect.ValueOf(symbol).Elem().FieldByName("data").Elem().FieldByName("extra").IsNil()) +} + +func TestSymbolSharedExtraData(t *testing.T) { + t.Parallel() + var arena ast.SymbolExtraArena + var otherArena ast.SymbolExtraArena + symbol := ast.NewSymbol() + alias := ast.NewSymbol() + alias.SetSymbolData(symbol) + member := ast.NewSymbol() + export := ast.NewSymbol() + alias.SetMembers(ast.SymbolTable{"member": member}, &arena) + symbol.SetExports(ast.SymbolTable{"export": export}, &otherArena) + alias.SetExportSymbol(export, &otherArena) + runtime.GC() + assert.Equal(t, symbol.Members()["member"], member) + assert.Equal(t, alias.Exports()["export"], export) + assert.Equal(t, symbol.ExportSymbol(), export) + symbol.SetMembers(nil, &otherArena) + alias.SetExports(nil, &arena) + symbol.SetExportSymbol(nil, &arena) + assert.Assert(t, alias.Members() == nil) + assert.Assert(t, symbol.Exports() == nil) + assert.Assert(t, alias.ExportSymbol() == nil) + ast.GetMembers(alias, &otherArena)["new"] = export + ast.GetExports(symbol, &arena)["new"] = member + for range 1024 { + ast.NewSymbol().SetExports(ast.SymbolTable{"export": export}, &arena) + } + arena = ast.SymbolExtraArena{} + otherArena = ast.SymbolExtraArena{} + runtime.GC() + assert.Equal(t, symbol.Members()["new"], export) + assert.Equal(t, alias.Exports()["new"], member) +} diff --git a/tsc/internal/ast/utilities.go b/tsc/internal/ast/utilities.go index 668883c00d285..ff11fd6a2c2f4 100644 --- a/tsc/internal/ast/utilities.go +++ b/tsc/internal/ast/utilities.go @@ -99,12 +99,12 @@ func GetSymbolTable(data *SymbolTable) SymbolTable { return *data } -func GetMembers(symbol *Symbol) SymbolTable { - return GetSymbolTable(&symbol.data.members) +func GetMembers(symbol *Symbol, arena *SymbolExtraArena) SymbolTable { + return GetSymbolTable(&symbol.ensureExtra(arena).members) } -func GetExports(symbol *Symbol) SymbolTable { - return GetSymbolTable(&symbol.data.exports) +func GetExports(symbol *Symbol, arena *SymbolExtraArena) SymbolTable { + return GetSymbolTable(&symbol.ensureExtra(arena).exports) } func GetLocals(container *Node) SymbolTable { diff --git a/tsc/internal/binder/binder.go b/tsc/internal/binder/binder.go index eb9bcebe5cf35..a95bb19ecdff7 100644 --- a/tsc/internal/binder/binder.go +++ b/tsc/internal/binder/binder.go @@ -74,6 +74,7 @@ type Binder struct { symbolCount int notConstEnumOnlyModules collections.Set[*ast.Symbol] symbolWithDataArena core.Arena[ast.SymbolWithData] + symbolExtraArena ast.SymbolExtraArena flowNodeArena core.Arena[ast.FlowNode] flowListArena core.Arena[ast.FlowList] singleDeclarationsArena core.Arena[*ast.Node] @@ -379,7 +380,7 @@ func (b *Binder) declareModuleMember(node *ast.Node, symbolFlags ast.SymbolFlags hasExportModifier := ast.GetCombinedModifierFlags(node)&ast.ModifierFlagsExport != 0 || ast.IsImplicitlyExportedJSDocDeclaration(node) if symbolFlags&ast.SymbolFlagsAlias != 0 { if node.Kind == ast.KindExportSpecifier || (node.Kind == ast.KindImportEqualsDeclaration && hasExportModifier) { - return b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, symbolFlags, symbolExcludes) + return b.declareSymbol(ast.GetExports(container.Symbol(), &b.symbolExtraArena), container.Symbol(), node, symbolFlags, symbolExcludes) } return b.declareSymbol(ast.GetLocals(container), nil /*parent*/, node, symbolFlags, symbolExcludes) } @@ -400,7 +401,7 @@ func (b *Binder) declareModuleMember(node *ast.Node, symbolFlags ast.SymbolFlags // and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed. if !ast.IsAmbientModule(node) && (hasExportModifier || container.Flags&ast.NodeFlagsExportContext != 0) { if !ast.IsLocalsContainer(container) || (ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) && b.getDeclarationName(node) == ast.InternalSymbolNameMissing) { - return b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, symbolFlags, symbolExcludes) + return b.declareSymbol(ast.GetExports(container.Symbol(), &b.symbolExtraArena), container.Symbol(), node, symbolFlags, symbolExcludes) // No local symbol for an unnamed default! } exportKind := ast.SymbolFlagsNone @@ -408,7 +409,7 @@ func (b *Binder) declareModuleMember(node *ast.Node, symbolFlags ast.SymbolFlags exportKind = ast.SymbolFlagsExportValue } local := b.declareSymbol(ast.GetLocals(container), nil /*parent*/, node, exportKind, symbolExcludes) - local.SetExportSymbol(b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, symbolFlags, symbolExcludes)) + local.SetExportSymbol(b.declareSymbol(ast.GetExports(container.Symbol(), &b.symbolExtraArena), container.Symbol(), node, symbolFlags, symbolExcludes), &b.symbolExtraArena) node.ExportableData().LocalSymbol = local return local } @@ -417,9 +418,9 @@ func (b *Binder) declareModuleMember(node *ast.Node, symbolFlags ast.SymbolFlags func (b *Binder) declareClassMember(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol { if ast.IsStatic(node) { - return b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + return b.declareSymbol(ast.GetExports(b.container.Symbol(), &b.symbolExtraArena), b.container.Symbol(), node, symbolFlags, symbolExcludes) } - return b.declareSymbol(ast.GetMembers(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + return b.declareSymbol(ast.GetMembers(b.container.Symbol(), &b.symbolExtraArena), b.container.Symbol(), node, symbolFlags, symbolExcludes) } func (b *Binder) declareSourceFileMember(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol { @@ -438,9 +439,9 @@ func (b *Binder) declareSymbolAndAddToSymbolTable(node *ast.Node, symbolFlags as case ast.KindClassExpression, ast.KindClassDeclaration: return b.declareClassMember(node, symbolFlags, symbolExcludes) case ast.KindEnumDeclaration: - return b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + return b.declareSymbol(ast.GetExports(b.container.Symbol(), &b.symbolExtraArena), b.container.Symbol(), node, symbolFlags, symbolExcludes) case ast.KindTypeLiteral, ast.KindObjectLiteralExpression, ast.KindInterfaceDeclaration, ast.KindJsxAttributes: - return b.declareSymbol(ast.GetMembers(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + return b.declareSymbol(ast.GetMembers(b.container.Symbol(), &b.symbolExtraArena), b.container.Symbol(), node, symbolFlags, symbolExcludes) case ast.KindFunctionType, ast.KindConstructorType, ast.KindCallSignature, ast.KindConstructSignature, ast.KindIndexSignature, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindArrowFunction, @@ -763,7 +764,7 @@ func (b *Binder) bindSourceFileIfExternalModule() { b.bindSourceFileAsExternalModule() // Create symbol equivalent for the module.exports = {} originalSymbol := b.file.Symbol - b.declareSymbol(ast.GetExports(b.file.Symbol), b.file.Symbol, b.file.AsNode(), ast.SymbolFlagsProperty, ast.SymbolFlagsAll) + b.declareSymbol(ast.GetExports(b.file.Symbol, &b.symbolExtraArena), b.file.Symbol, b.file.AsNode(), ast.SymbolFlagsProperty, ast.SymbolFlagsAll) b.file.Symbol = originalSymbol } } @@ -853,9 +854,9 @@ func (b *Binder) bindExportDeclaration(node *ast.Node) { b.bindAnonymousDeclaration(node, ast.SymbolFlagsExportStar, b.getDeclarationName(node)) } else if decl.ExportClause == nil { // All export * declarations are collected in an __export symbol - b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), node, ast.SymbolFlagsExportStar, ast.SymbolFlagsNone) + b.declareSymbol(ast.GetExports(b.container.Symbol(), &b.symbolExtraArena), b.container.Symbol(), node, ast.SymbolFlagsExportStar, ast.SymbolFlagsNone) } else if ast.IsNamespaceExport(decl.ExportClause) { - b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), decl.ExportClause, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) + b.declareSymbol(ast.GetExports(b.container.Symbol(), &b.symbolExtraArena), b.container.Symbol(), decl.ExportClause, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) } } @@ -868,7 +869,7 @@ func (b *Binder) bindExportAssignment(node *ast.Node) { // If there is an `export default x;` alias declaration, can't `export default` anything else. // (In contrast, you can still have `export default function f() {}` and `export default interface I {}`.) flags := core.IfElse(ast.ExpressionIsAlias(node.Expression()), ast.SymbolFlagsAlias, ast.SymbolFlagsProperty) - symbol := b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, flags, ast.SymbolFlagsAll) + symbol := b.declareSymbol(ast.GetExports(container.Symbol(), &b.symbolExtraArena), container.Symbol(), node, flags, ast.SymbolFlagsAll) if node.AsExportAssignment().IsExportEquals { // Ensure export assignments have a ValueDeclaration set. SetValueDeclaration(symbol, node) @@ -967,11 +968,11 @@ func (b *Binder) bindClassLikeDeclaration(node *ast.Node) { // module might have an exported variable called 'prototype'. We can't allow that as // that would clash with the built-in 'prototype' for the class. prototypeSymbol := b.newSymbol(ast.SymbolFlagsProperty|ast.SymbolFlagsPrototype, "prototype") - symbolExport := ast.GetExports(symbol)[prototypeSymbol.Name()] + symbolExport := ast.GetExports(symbol, &b.symbolExtraArena)[prototypeSymbol.Name()] if symbolExport != nil { b.errorOnNode(symbolExport.Declarations()[0], diagnostics.Duplicate_identifier_0, ast.SymbolName(prototypeSymbol)) } - ast.GetExports(symbol)[prototypeSymbol.Name()] = prototypeSymbol + ast.GetExports(symbol, &b.symbolExtraArena)[prototypeSymbol.Name()] = prototypeSymbol prototypeSymbol.SetParent(symbol) } @@ -1000,12 +1001,12 @@ func (b *Binder) bindFunctionOrConstructorType(node *ast.Node) { b.addDeclarationToSymbol(symbol, node, ast.SymbolFlagsSignature) typeLiteralSymbol := b.newSymbol(ast.SymbolFlagsTypeLiteral, ast.InternalSymbolNameType) b.addDeclarationToSymbol(typeLiteralSymbol, node, ast.SymbolFlagsTypeLiteral) - typeLiteralSymbol.SetMembers(make(ast.SymbolTable)) + typeLiteralSymbol.SetMembers(make(ast.SymbolTable), &b.symbolExtraArena) typeLiteralSymbol.Members()[symbol.Name()] = symbol } func (b *Binder) addLateBoundAssignmentDeclarationToSymbol(node *ast.Node, symbol *ast.Symbol) { - exports := ast.GetExports(symbol) + exports := ast.GetExports(symbol, &b.symbolExtraArena) assignmentSymbol := exports[ast.InternalSymbolNameAssignmentDeclaration] if assignmentSymbol == nil { assignmentSymbol = b.newSymbol(ast.SymbolFlagsNone, ast.InternalSymbolNameAssignmentDeclaration) @@ -1018,7 +1019,7 @@ func (b *Binder) bindModuleExportsAssignment(node *ast.Node) { if b.setCommonJSModuleIndicator(node) { container := b.file.AsNode() flags := core.IfElse(ast.ExpressionIsAlias(node.AsBinaryExpression().Right), ast.SymbolFlagsAlias, ast.SymbolFlagsProperty) - symbol := b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, flags, 0) + symbol := b.declareSymbol(ast.GetExports(container.Symbol(), &b.symbolExtraArena), container.Symbol(), node, flags, 0) SetValueDeclaration(symbol, node) } } @@ -1047,7 +1048,7 @@ func (b *Binder) bindCommonJSTypeExports(moduleSymbol *ast.Symbol) { if exportEquals := moduleExports[ast.InternalSymbolNameExportEquals]; exportEquals != nil { for _, symbol := range moduleExports { if symbol.Name() != ast.InternalSymbolNameExportEquals && symbol.Flags()&(ast.SymbolFlagsType|ast.SymbolFlagsNamespace) != 0 { - ast.GetExports(exportEquals)[symbol.Name()] = symbol + ast.GetExports(exportEquals, &b.symbolExtraArena)[symbol.Name()] = symbol exportEquals.SetFlags(exportEquals.Flags() | ast.SymbolFlagsNamespaceModule) } } @@ -1066,7 +1067,7 @@ func (b *Binder) bindDeferredExpandoAssignment(node *ast.Node) { b.addLateBoundAssignmentDeclarationToSymbol(node, symbol) } else { // We declare expandos only when there are no non-expando declarations for that name. - exports := ast.GetExports(symbol) + exports := ast.GetExports(symbol, &b.symbolExtraArena) if existing := exports[b.getDeclarationName(node)]; existing == nil || existing.Flags()&ast.SymbolFlagsAssignment != 0 { b.declareSymbol(exports, symbol, node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.SymbolFlagsPropertyExcludes) } @@ -1088,7 +1089,7 @@ func (b *Binder) bindExportsOrObjectDefineProperty(node *ast.Node) { if b.setCommonJSModuleIndicator(node) { container := b.file.AsNode() flags := core.IfElse(ast.IsBinaryExpression(node) && ast.ExpressionIsAlias(node.AsBinaryExpression().Right), ast.SymbolFlagsAlias, ast.SymbolFlagsFunctionScopedVariable) - b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, flags, ast.SymbolFlagsFunctionScopedVariableExcludes) + b.declareSymbol(ast.GetExports(container.Symbol(), &b.symbolExtraArena), container.Symbol(), node, flags, ast.SymbolFlagsFunctionScopedVariableExcludes) } } @@ -1152,9 +1153,9 @@ func (b *Binder) getThisClassAndSymbolTable() (classSymbol *ast.Symbol, symbolTa // this.property assignment in class member -- bind to the containing class classSymbol = b.thisContainer.Parent.Symbol() if ast.IsStatic(b.thisContainer) { - symbolTable = ast.GetExports(classSymbol) + symbolTable = ast.GetExports(classSymbol, &b.symbolExtraArena) } else { - symbolTable = ast.GetMembers(classSymbol) + symbolTable = ast.GetMembers(classSymbol, &b.symbolExtraArena) } } return classSymbol, symbolTable @@ -1211,7 +1212,7 @@ func (b *Binder) bindParameter(node *ast.Node) { if ast.IsParameterPropertyDeclaration(node, node.Parent) { classDeclaration := node.Parent.Parent flags := ast.SymbolFlagsProperty | core.IfElse(decl.QuestionToken != nil, ast.SymbolFlagsOptional, ast.SymbolFlagsNone) - b.declareSymbol(ast.GetMembers(classDeclaration.Symbol()), classDeclaration.Symbol(), node, flags, ast.SymbolFlagsPropertyExcludes) + b.declareSymbol(ast.GetMembers(classDeclaration.Symbol(), &b.symbolExtraArena), classDeclaration.Symbol(), node, flags, ast.SymbolFlagsPropertyExcludes) } } @@ -1635,7 +1636,7 @@ func (b *Binder) declareCommonJSVariable(name string) { exportsProperty.SetDeclarations(symbol.Declarations()) exportsProperty.SetValueDeclaration(symbol.ValueDeclaration()) exportsProperty.SetParent(symbol) - symbol.SetMembers(make(ast.SymbolTable, 1)) + symbol.SetMembers(make(ast.SymbolTable, 1), &b.symbolExtraArena) symbol.Members()["exports"] = exportsProperty } locals[name] = symbol diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 6bf62110a9b9b..144c82f02c93a 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -672,6 +672,7 @@ type Checker struct { suggestionDiagnostics ast.DiagnosticsCollection symbolArena core.Arena[ast.Symbol] symbolWithDataArena core.Arena[ast.SymbolWithData] + symbolExtraArena ast.SymbolExtraArena signatureArena core.Arena[Signature] indexInfoArena core.Arena[IndexInfo] literalTypeArena core.Arena[LiteralType] @@ -686,6 +687,7 @@ type Checker struct { conditionalTypeArena core.Arena[ConditionalType] typeListArena core.Arena[*Type] valueSymbolExtraArena core.Arena[valueSymbolExtra] + structuredTypeExtraArena core.Arena[structuredTypeExtra] freeTypeSets *temporaryTypeSet mergedSymbols map[*ast.Symbol]*ast.Symbol mergedExportsChecked collections.Set[*ast.Symbol] @@ -989,7 +991,7 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.errorTypes = make(map[CacheHashKey]*Type) c.moduleSymbols = make(map[*ast.Node]*ast.Symbol) c.globalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly) - c.globalThisSymbol.SetExports(c.globals) + c.globalThisSymbol.SetExports(c.globals, &c.symbolExtraArena) c.globals[c.globalThisSymbol.Name()] = c.globalThisSymbol c.resolveName = c.createNameResolver().Resolve c.resolveNameForSymbolSuggestion = c.createNameResolverForSuggestion().Resolve @@ -4605,7 +4607,7 @@ func (c *Checker) getTypeWithoutSignatures(t *Type) *Type { switch { case t.flags&TypeFlagsObject != 0: resolved := c.resolveStructuredTypeMembers(t) - if len(resolved.signatures) != 0 { + if len(resolved.getSignatures()) != 0 { result := c.newObjectType(ObjectFlagsAnonymous, t.symbol) result.objectFlags |= ObjectFlagsMembersResolved result.AsObjectType().members = resolved.members @@ -10946,7 +10948,7 @@ func (c *Checker) getInstantiationExpressionType(exprType *Type, node *ast.Node) debug.Assert(t.symbol != nil, "Instantiation expression source type must have a symbol") symbol.SetDeclarations(t.symbol.Declarations()) result := c.newObjectType(ObjectFlagsAnonymous|ObjectFlagsInstantiationExpressionType, symbol) - c.setStructuredTypeMembers(result, resolved.members, callSignatures, constructSignatures, resolved.indexInfos) + c.setStructuredTypeMembers(result, resolved.members, callSignatures, constructSignatures, resolved.getIndexInfos()) result.AsInstantiationExpressionType().node = node return result } @@ -14474,10 +14476,10 @@ func (c *Checker) mergeSymbol(target *ast.Symbol, source *ast.Symbol, unidirecti } target.SetDeclarations(append(target.Declarations(), source.Declarations()...)) if source.Members() != nil { - c.mergeSymbolTable(ast.GetMembers(target), source.Members(), unidirectional, nil) + c.mergeSymbolTable(ast.GetMembers(target, &c.symbolExtraArena), source.Members(), unidirectional, nil) } if source.Exports() != nil { - c.mergeSymbolTable(ast.GetExports(target), source.Exports(), unidirectional, target) + c.mergeSymbolTable(ast.GetExports(target, &c.symbolExtraArena), source.Exports(), unidirectional, target) } if !unidirectional { c.recordMergedSymbol(target, source) @@ -14637,8 +14639,8 @@ func (c *Checker) cloneSymbol(symbol *ast.Symbol) *ast.Symbol { result.SetDeclarations(symbol.Declarations()[0:len(symbol.Declarations()):len(symbol.Declarations())]) result.SetParent(symbol.Parent()) result.SetValueDeclaration(symbol.ValueDeclaration()) - result.SetMembers(maps.Clone(symbol.Members())) - result.SetExports(maps.Clone(symbol.Exports())) + result.SetMembers(maps.Clone(symbol.Members()), &c.symbolExtraArena) + result.SetExports(maps.Clone(symbol.Exports()), &c.symbolExtraArena) c.recordMergedSymbol(result, symbol) return result } @@ -15101,8 +15103,8 @@ func (c *Checker) combineValueAndTypeSymbols(valueSymbol *ast.Symbol, typeSymbol result.SetParent(typeSymbol.Parent()) } result.SetValueDeclaration(valueSymbol.ValueDeclaration()) - result.SetMembers(maps.Clone(typeSymbol.Members())) - result.SetExports(maps.Clone(valueSymbol.Exports())) + result.SetMembers(maps.Clone(typeSymbol.Members()), &c.symbolExtraArena) + result.SetExports(maps.Clone(valueSymbol.Exports()), &c.symbolExtraArena) return result } @@ -16151,14 +16153,14 @@ func (c *Checker) cloneTypeAsModuleType(symbol *ast.Symbol, moduleType *Type, re result := c.newSymbol(symbol.Flags(), symbol.Name()) result.SetDeclarations(slices.Clone(symbol.Declarations())) result.SetValueDeclaration(symbol.ValueDeclaration()) - result.SetMembers(maps.Clone(symbol.Members())) - result.SetExports(maps.Clone(symbol.Exports())) + result.SetMembers(maps.Clone(symbol.Members()), &c.symbolExtraArena) + result.SetExports(maps.Clone(symbol.Exports()), &c.symbolExtraArena) result.SetParent(symbol.Parent()) links := c.exportTypeLinks.Get(result) links.target = symbol links.originatingImport = referenceParent resolvedModuleType := c.resolveStructuredTypeMembers(moduleType) - c.valueSymbolLinks.Get(result).resolvedType = c.newAnonymousType(result, resolvedModuleType.members, nil, nil, resolvedModuleType.indexInfos) + c.valueSymbolLinks.Get(result).resolvedType = c.newAnonymousType(result, resolvedModuleType.members, nil, nil, resolvedModuleType.getIndexInfos()) return result } @@ -19422,10 +19424,13 @@ func (c *Checker) getSignaturesOfStructuredType(t *Type, kind SignatureKind) []* return nil } resolved := c.resolveStructuredTypeMembers(t) + if resolved.extra == nil { + return nil + } if kind == SignatureKindCall { - return resolved.signatures[:resolved.callSignatureCount] + return resolved.extra.signatures[:resolved.extra.callSignatureCount] } - return resolved.signatures[resolved.callSignatureCount:] + return resolved.extra.signatures[resolved.extra.callSignatureCount:] } func (c *Checker) getIndexInfosOfType(t *Type) []*IndexInfo { @@ -19434,7 +19439,7 @@ func (c *Checker) getIndexInfosOfType(t *Type) []*IndexInfo { func (c *Checker) getIndexInfosOfStructuredType(t *Type) []*IndexInfo { if t.flags&TypeFlagsStructuredType != 0 { - return c.resolveStructuredTypeMembers(t).indexInfos + return c.resolveStructuredTypeMembers(t).getIndexInfos() } return nil } @@ -19807,7 +19812,7 @@ func (c *Checker) getSingleCallOrConstructSignature(t *Type) *Signature { func (c *Checker) getSingleSignature(t *Type, kind SignatureKind, allowMembers bool) *Signature { if t.flags&TypeFlagsObject != 0 { resolved := c.resolveStructuredTypeMembers(t) - if allowMembers || len(resolved.properties) == 0 && len(resolved.indexInfos) == 0 { + if allowMembers || len(resolved.properties) == 0 && len(resolved.getIndexInfos()) == 0 { if kind == SignatureKindCall && len(resolved.CallSignatures()) == 1 && len(resolved.ConstructSignatures()) == 0 { return resolved.CallSignatures()[0] } @@ -21160,14 +21165,20 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { indexInfos = append(indexInfos, c.enumNumberIndexInfo) } } - d.indexInfos = indexInfos + if indexInfos != nil || d.extra != nil { + d.ensureExtra().indexInfos = indexInfos + } // We resolve the members before computing the signatures because a signature may use // typeof with a qualified name expression that circularly references the type we are // in the process of resolving (see issue #6072). The temporarily empty signature list // will never be observed because a qualified name can't reference signatures. if symbol.Flags()&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 { - d.signatures = c.getSignaturesOfSymbol(symbol) - d.callSignatureCount = len(d.signatures) + signatures := c.getSignaturesOfSymbol(symbol) + if signatures != nil || d.extra != nil { + extra := d.ensureExtra() + extra.signatures = signatures + extra.callSignatureCount = len(signatures) + } } // And likewise for construct signatures for classes if symbol.Flags()&ast.SymbolFlagsClass != 0 { @@ -21176,7 +21187,10 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { if len(constructSignatures) == 0 { constructSignatures = c.getDefaultConstructSignatures(classType) } - d.signatures = append(d.signatures, constructSignatures...) + if len(constructSignatures) != 0 { + extra := d.ensureExtra() + extra.signatures = append(extra.signatures, constructSignatures...) + } } } @@ -25268,7 +25282,7 @@ func (c *Checker) getGlobalImportMetaExpressionType() *Type { metaPropertySymbol.SetParent(symbol) c.valueSymbolLinks.Get(metaPropertySymbol).resolvedType = importMetaType members := createSymbolTable([]*ast.Symbol{metaPropertySymbol}) - symbol.SetMembers(members) + symbol.SetMembers(members, &c.symbolExtraArena) c.deferredGlobalImportMetaExpressionType = c.newAnonymousType(symbol, members, nil, nil, nil) } return c.deferredGlobalImportMetaExpressionType @@ -25735,22 +25749,30 @@ func (c *Checker) setStructuredTypeMembers(t *Type, members ast.SymbolTable, cal data := t.AsStructuredType() data.members = members data.properties = c.getNamedMembers(members, t.symbol) + c.setStructuredTypeSignatures(data, callSignatures, constructSignatures, indexInfos) +} + +func (c *Checker) setStructuredTypeSignatures(data *StructuredType, callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo) { + if data.extra == nil && callSignatures == nil && constructSignatures == nil && indexInfos == nil { + return + } + extra := data.ensureExtra() if len(callSignatures) != 0 { if len(constructSignatures) != 0 { - data.signatures = core.Concatenate(callSignatures, constructSignatures) + extra.signatures = core.Concatenate(callSignatures, constructSignatures) } else { - data.signatures = slices.Clip(callSignatures) + extra.signatures = slices.Clip(callSignatures) } - data.callSignatureCount = len(callSignatures) + extra.callSignatureCount = len(callSignatures) } else { if len(constructSignatures) != 0 { - data.signatures = slices.Clip(constructSignatures) + extra.signatures = slices.Clip(constructSignatures) } else { - data.signatures = nil + extra.signatures = nil } - data.callSignatureCount = 0 + extra.callSignatureCount = 0 } - data.indexInfos = slices.Clip(indexInfos) + extra.indexInfos = slices.Clip(indexInfos) } func (c *Checker) newTypeParameter(symbol *ast.Symbol) *Type { @@ -27080,7 +27102,7 @@ func (c *Checker) IsEmptyAnonymousObjectType(t *Type) bool { } func (c *Checker) isEmptyResolvedType(t *StructuredType) bool { - return t.AsType() != c.anyFunctionType && len(t.properties) == 0 && len(t.signatures) == 0 && len(t.indexInfos) == 0 + return t.AsType() != c.anyFunctionType && len(t.properties) == 0 && len(t.getSignatures()) == 0 && len(t.getIndexInfos()) == 0 } func (c *Checker) isEmptyObjectType(t *Type) bool { @@ -28464,7 +28486,7 @@ func (c *Checker) containsUndefinedType(t *Type) bool { } func (c *Checker) typeHasCallOrConstructSignatures(t *Type) bool { - return t.flags&TypeFlagsStructuredType != 0 && len(c.resolveStructuredTypeMembers(t).signatures) != 0 + return t.flags&TypeFlagsStructuredType != 0 && len(c.resolveStructuredTypeMembers(t).getSignatures()) != 0 } func (c *Checker) getNormalizedType(t *Type, writing bool) *Type { @@ -28791,7 +28813,7 @@ func (c *Checker) getRegularTypeOfObjectLiteral(t *Type) *Type { } resolved := c.resolveStructuredTypeMembers(t) members := c.transformTypeOfMembers(t, c.getRegularTypeOfObjectLiteral) - regular := c.newAnonymousType(t.symbol, members, resolved.CallSignatures(), resolved.ConstructSignatures(), resolved.indexInfos) + regular := c.newAnonymousType(t.symbol, members, resolved.CallSignatures(), resolved.ConstructSignatures(), resolved.getIndexInfos()) regular.flags = resolved.flags regular.objectFlags |= resolved.objectFlags & ^ObjectFlagsFreshLiteral c.cachedTypes[key] = regular @@ -31788,7 +31810,7 @@ func (c *Checker) isFunctionObjectType(t *Type) bool { // We do a quick check for a "bind" property before performing the more expensive subtype // check. This gives us a quicker out in the common case where an object type is not a function. resolved := c.resolveStructuredTypeMembers(t) - return len(resolved.signatures) != 0 || resolved.members["bind"] != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) + return len(resolved.getSignatures()) != 0 || resolved.members["bind"] != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) } func (c *Checker) getTypeWithFacts(t *Type, include TypeFacts) *Type { diff --git a/tsc/internal/checker/nodebuilderimpl.go b/tsc/internal/checker/nodebuilderimpl.go index c7c545fc0d89a..8e4b0c63f4d2a 100644 --- a/tsc/internal/checker/nodebuilderimpl.go +++ b/tsc/internal/checker/nodebuilderimpl.go @@ -566,19 +566,19 @@ func (b *NodeBuilderImpl) getResolvedTypeWithoutAbstractConstructSignatures(t *S if len(t.ConstructSignatures()) == 0 { return t.AsType() } - if t.objectTypeWithoutAbstractConstructSignatures != nil { - return t.objectTypeWithoutAbstractConstructSignatures + if t.extra.objectTypeWithoutAbstractConstructSignatures != nil { + return t.extra.objectTypeWithoutAbstractConstructSignatures } constructSignatures := core.Filter(t.ConstructSignatures(), func(signature *Signature) bool { return signature.flags&SignatureFlagsAbstract == 0 }) if len(constructSignatures) == len(t.ConstructSignatures()) { - t.objectTypeWithoutAbstractConstructSignatures = t.AsType() + t.extra.objectTypeWithoutAbstractConstructSignatures = t.AsType() return t.AsType() } - typeCopy := b.ch.newAnonymousType(t.symbol, t.members, t.CallSignatures(), core.IfElse(len(constructSignatures) > 0, constructSignatures, []*Signature{}), t.indexInfos) - t.objectTypeWithoutAbstractConstructSignatures = typeCopy - typeCopy.AsStructuredType().objectTypeWithoutAbstractConstructSignatures = typeCopy + typeCopy := b.ch.newAnonymousType(t.symbol, t.members, t.CallSignatures(), core.IfElse(len(constructSignatures) > 0, constructSignatures, []*Signature{}), t.getIndexInfos()) + t.extra.objectTypeWithoutAbstractConstructSignatures = typeCopy + typeCopy.AsStructuredType().ensureExtra().objectTypeWithoutAbstractConstructSignatures = typeCopy return typeCopy } @@ -2750,7 +2750,7 @@ func (b *NodeBuilderImpl) createTypeNodesFromResolvedType(resolvedType *Structur } typeElements = append(typeElements, b.signatureToSignatureDeclarationHelper(signature, ast.KindConstructSignature, nil)) } - for _, info := range resolvedType.indexInfos { + for _, info := range resolvedType.getIndexInfos() { var typeNode *ast.TypeNode if resolvedType.objectFlags&ObjectFlagsReverseMapped != 0 && b.ctx.flags&nodebuilder.FlagsAllowAnonymousIdentifier != 0 { typeNode = b.createElidedInformationPlaceholder() @@ -2807,7 +2807,7 @@ func (b *NodeBuilderImpl) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { resolved := b.ch.resolveStructuredTypeMembers(t) callSigs := resolved.CallSignatures() ctorSigs := resolved.ConstructSignatures() - if len(resolved.properties) == 0 && len(resolved.indexInfos) == 0 { + if len(resolved.properties) == 0 && len(resolved.getIndexInfos()) == 0 { if len(callSigs) == 0 && len(ctorSigs) == 0 { b.ctx.approximateLength += 2 result := b.f.NewTypeLiteralNode(b.f.NewNodeList([]*ast.Node{})) @@ -2836,7 +2836,7 @@ func (b *NodeBuilderImpl) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { return b.ch.getOrCreateTypeFromSignature(s) }) // count the number of type elements excluding abstract constructors - typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.indexInfos) + core.IfElse(b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.properties, func(p *ast.Symbol) bool { + typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.getIndexInfos()) + core.IfElse(b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.properties, func(p *ast.Symbol) bool { return p.Flags()&ast.SymbolFlagsPrototype == 0 }), len(resolved.properties)) // don't include an empty object literal if there were no other static-side diff --git a/tsc/internal/checker/relater.go b/tsc/internal/checker/relater.go index a9bc484fef1d2..8bf177bfd9e3a 100644 --- a/tsc/internal/checker/relater.go +++ b/tsc/internal/checker/relater.go @@ -679,7 +679,7 @@ func (c *Checker) elaborateArrowFunction(node *ast.Node, source *Type, target *T func (c *Checker) isWeakType(t *Type) bool { if t.flags&TypeFlagsObject != 0 { resolved := c.resolveStructuredTypeMembers(t) - return len(resolved.signatures) == 0 && len(resolved.indexInfos) == 0 && len(resolved.properties) > 0 && core.Every(resolved.properties, func(p *ast.Symbol) bool { + return len(resolved.getSignatures()) == 0 && len(resolved.getIndexInfos()) == 0 && len(resolved.properties) > 0 && core.Every(resolved.properties, func(p *ast.Symbol) bool { return p.Flags()&ast.SymbolFlagsOptional != 0 }) } diff --git a/tsc/internal/checker/types.go b/tsc/internal/checker/types.go index 4809ceed97cbb..9266430f3a715 100644 --- a/tsc/internal/checker/types.go +++ b/tsc/internal/checker/types.go @@ -1096,8 +1096,12 @@ func (t *ConstrainedType) AsConstrainedType() *ConstrainedType { return t } type StructuredType struct { ConstrainedType - members ast.SymbolTable - properties []*ast.Symbol + members ast.SymbolTable + properties []*ast.Symbol + extra *structuredTypeExtra +} + +type structuredTypeExtra struct { signatures []*Signature // Signatures (call + construct) callSignatureCount int // Count of call signatures indexInfos []*IndexInfo @@ -1108,11 +1112,38 @@ type StructuredType struct { func (t *StructuredType) AsStructuredType() *StructuredType { return t } func (t *StructuredType) CallSignatures() []*Signature { - return slices.Clip(t.signatures[:t.callSignatureCount]) + if t.extra == nil { + return nil + } + return slices.Clip(t.extra.signatures[:t.extra.callSignatureCount]) } func (t *StructuredType) ConstructSignatures() []*Signature { - return slices.Clip(t.signatures[t.callSignatureCount:]) + if t.extra == nil { + return nil + } + return slices.Clip(t.extra.signatures[t.extra.callSignatureCount:]) +} + +func (t *StructuredType) getSignatures() []*Signature { + if t.extra == nil { + return nil + } + return t.extra.signatures +} + +func (t *StructuredType) getIndexInfos() []*IndexInfo { + if t.extra == nil { + return nil + } + return t.extra.indexInfos +} + +func (t *StructuredType) ensureExtra() *structuredTypeExtra { + if t.extra == nil { + t.extra = t.checker.structuredTypeExtraArena.New() + } + return t.extra } func (t *StructuredType) Properties() []*ast.Symbol { diff --git a/tsc/internal/checker/types_test.go b/tsc/internal/checker/types_test.go index 286f5c6dc8368..d08d1803e2035 100644 --- a/tsc/internal/checker/types_test.go +++ b/tsc/internal/checker/types_test.go @@ -391,3 +391,53 @@ func TestCompactValueSymbolLinksLayout(t *testing.T) { assert.Equal(t, links.getWriteType(), types[0]) assert.Equal(t, links.getContainingType(), types[2]) } + +func TestCompactStructuredTypeLayout(t *testing.T) { + t.Parallel() + assert.Equal(t, unsafe.Sizeof(StructuredType{}), unsafe.Sizeof(ConstrainedType{})+5*unsafe.Sizeof(uintptr(0))) +} + +func TestStructuredTypeExtra(t *testing.T) { + t.Parallel() + c, types := newCompositeTestChecker() + data := c.newAnonymousType(nil, nil, nil, nil, nil).AsStructuredType() + assert.Assert(t, data.extra == nil) + assert.Assert(t, data.CallSignatures() == nil) + assert.Assert(t, data.ConstructSignatures() == nil) + assert.Assert(t, data.getIndexInfos() == nil) + c.setStructuredTypeSignatures(data, nil, nil, nil) + assert.Assert(t, data.extra == nil) + + call := &Signature{} + construct := &Signature{} + info := &IndexInfo{keyType: types[0]} + c.setStructuredTypeSignatures(data, []*Signature{call}, []*Signature{construct}, []*IndexInfo{info}) + assert.Equal(t, data.CallSignatures()[0], call) + assert.Equal(t, data.ConstructSignatures()[0], construct) + assert.Equal(t, cap(data.CallSignatures()), 1) + assert.Equal(t, cap(data.ConstructSignatures()), 1) + assert.Equal(t, data.getIndexInfos()[0], info) + extra := data.extra + extra.objectTypeWithoutAbstractConstructSignatures = types[2] + for range 1024 { + other := c.newAnonymousType(nil, nil, []*Signature{call}, nil, nil) + assert.Assert(t, other.AsStructuredType().extra != extra) + } + runtime.GC() + assert.Equal(t, data.CallSignatures()[0], call) + assert.Equal(t, data.ConstructSignatures()[0], construct) + assert.Equal(t, data.getIndexInfos()[0], info) + assert.Equal(t, data.extra.objectTypeWithoutAbstractConstructSignatures, types[2]) + c.setStructuredTypeSignatures(data, nil, []*Signature{construct}, nil) + assert.Equal(t, len(data.CallSignatures()), 0) + assert.Equal(t, data.ConstructSignatures()[0], construct) + assert.Assert(t, data.getIndexInfos() == nil) + c.setStructuredTypeSignatures(data, nil, nil, nil) + assert.Assert(t, data.CallSignatures() == nil) + assert.Assert(t, data.ConstructSignatures() == nil) + assert.Equal(t, data.extra, extra) + assert.Equal(t, extra.objectTypeWithoutAbstractConstructSignatures, types[2]) + c.setStructuredTypeSignatures(data, nil, nil, []*IndexInfo{}) + assert.Assert(t, data.getIndexInfos() != nil) + runtime.KeepAlive(c) +}