Skip to content

Commit 48d2bb2

Browse files
committed
Validate member table pairs before indexing by name
The name index and the instantiated fast path both assumed a table whose properties are found under their own names has no other keys. A symbol stored under its own name and an alias satisfies every reverse lookup, so the alias was silently dropped. Checking each (key, symbol) pair directly keeps such tables on the map path.
1 parent 66ac538 commit 48d2bb2

3 files changed

Lines changed: 27 additions & 17 deletions

File tree

‎tsc/internal/checker/checker.go‎

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19567,18 +19567,13 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters
1956719567
} else {
1956819568
instantiated = true
1956919569
mapper = newTypeMapper(typeParameters, typeArguments)
19570-
properties = c.getNamedMembers(resolved.declaredMembers, t.symbol)
19571-
for _, symbol := range properties {
19572-
if resolved.declaredMembers[symbol.Name()] != symbol {
19573-
members = c.instantiateSymbolTable(resolved.declaredMembers, mapper)
19574-
properties = nil
19575-
break
19576-
}
19577-
}
19578-
if members == nil {
19570+
if symbolTableKeysMatchNames(resolved.declaredMembers) {
19571+
properties = c.getNamedMembers(resolved.declaredMembers, t.symbol)
1957919572
for i, symbol := range properties {
1958019573
properties[i] = c.instantiateSymbol(symbol, mapper)
1958119574
}
19575+
} else {
19576+
members = c.instantiateSymbolTable(resolved.declaredMembers, mapper)
1958219577
}
1958319578
callSignatures = c.instantiateSignatures(resolved.declaredCallSignatures, mapper)
1958419579
constructSignatures = c.instantiateSignatures(resolved.declaredConstructSignatures, mapper)

‎tsc/internal/checker/types.go‎

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,14 +1177,7 @@ type memberIndex struct {
11771177
// Property order is semantic. Keep it unchanged and index it with pointer-free
11781178
// slots instead of retaining a second map of names and symbol pointers.
11791179
func (c *Checker) newMemberIndex(members ast.SymbolTable, properties []*ast.Symbol, index *memberIndex) *memberIndex {
1180-
needsFallback := len(members) != len(properties)
1181-
for _, symbol := range properties {
1182-
if members[symbol.Name()] != symbol {
1183-
needsFallback = true
1184-
break
1185-
}
1186-
}
1187-
if needsFallback {
1180+
if len(members) != len(properties) || !symbolTableKeysMatchNames(members) {
11881181
if index == nil {
11891182
index = c.memberIndexArena.New()
11901183
}
@@ -1197,6 +1190,18 @@ func (c *Checker) newMemberIndex(members ast.SymbolTable, properties []*ast.Symb
11971190
return c.newPropertyIndex(properties, index)
11981191
}
11991192

1193+
// Reports whether every symbol is stored under its own name. Checking the
1194+
// table's pairs, rather than looking each property up by name, also rejects a
1195+
// symbol aliased under a second key, which a name-keyed index would drop.
1196+
func symbolTableKeysMatchNames(members ast.SymbolTable) bool {
1197+
for name, symbol := range members {
1198+
if symbol.Name() != name {
1199+
return false
1200+
}
1201+
}
1202+
return true
1203+
}
1204+
12001205
func (c *Checker) newPropertyIndex(properties []*ast.Symbol, index *memberIndex) *memberIndex {
12011206
if len(properties) <= 8 {
12021207
return nil

‎tsc/internal/checker/types_test.go‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,16 @@ func TestStructuredMembersLookup(t *testing.T) {
573573
member := c.newAnonymousType(nil, ast.SymbolTable{"different": renamed}, nil, nil, nil).AsStructuredType()
574574
assert.Equal(t, member.getMember("different"), renamed)
575575
assert.Assert(t, member.getMember("original") == nil)
576+
aliased := c.newSymbol(ast.SymbolFlagsProperty, "original")
577+
aliasedMembers := ast.SymbolTable{"original": aliased, "different": aliased}
578+
for i := range 10 {
579+
name := "p" + strconv.Itoa(i)
580+
aliasedMembers[name] = c.newSymbol(ast.SymbolFlagsProperty, name)
581+
}
582+
aliasedType := c.newAnonymousType(nil, aliasedMembers, nil, nil, nil).AsStructuredType()
583+
for name, symbol := range aliasedMembers {
584+
assert.Equal(t, aliasedType.getMember(name), symbol)
585+
}
576586
}
577587

578588
func TestCompactStructuredTypeLayout(t *testing.T) {

0 commit comments

Comments
 (0)