Skip to content

Commit bf46f3e

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 d8065db commit bf46f3e

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
@@ -19587,18 +19587,13 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters
1958719587
} else {
1958819588
instantiated = true
1958919589
mapper = newTypeMapper(typeParameters, typeArguments)
19590-
properties = c.getNamedMembers(resolved.declaredMembers, t.symbol)
19591-
for _, symbol := range properties {
19592-
if resolved.declaredMembers[symbol.Name()] != symbol {
19593-
members = c.instantiateSymbolTable(resolved.declaredMembers, mapper)
19594-
properties = nil
19595-
break
19596-
}
19597-
}
19598-
if members == nil {
19590+
if symbolTableKeysMatchNames(resolved.declaredMembers) {
19591+
properties = c.getNamedMembers(resolved.declaredMembers, t.symbol)
1959919592
for i, symbol := range properties {
1960019593
properties[i] = c.instantiateSymbol(symbol, mapper)
1960119594
}
19595+
} else {
19596+
members = c.instantiateSymbolTable(resolved.declaredMembers, mapper)
1960219597
}
1960319598
callSignatures = c.instantiateSignatures(resolved.declaredCallSignatures, mapper)
1960419599
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)