mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-10-28 16:36:04 +08:00
Count intersection property names with a plain map and slice (#64876)
This commit is contained in:
1 parent
b6a965883b
commit
615d415931
3 files changed
+76
-23
No files matched your search
@@ -22457,16 +22457,22 @@ func (c *Checker) somePropertyReducesToNever(t *Type) bool {
|
||||
// Collect declaration counts for each property across all constituent types of the intersection.
|
||||
counts := c.getTemporaryPropertyCounts()
|
||||
defer c.releaseTemporaryPropertyCounts(counts)
|
||||
for _, t := range t.Types() {
|
||||
// Property names are unique within each constituent, so the first
|
||||
// constituent's names can be added without looking them up.
|
||||
types := t.Types()
|
||||
for _, prop := range c.getLayoutPropertiesOfType(types[0]) {
|
||||
counts.AddNew(prop.Name())
|
||||
}
|
||||
for _, t := range types[1:] {
|
||||
for _, prop := range c.getLayoutPropertiesOfType(t) {
|
||||
counts.Set(prop.Name(), counts.GetOrZero(prop.Name())+1)
|
||||
counts.Add(prop.Name())
|
||||
}
|
||||
}
|
||||
// Check if any property appears in more than one constituent type and reduces to 'never'.
|
||||
// Go in the order the properties were found so the combined properties are created in the same order every time.
|
||||
for propName, count := range counts.Entries() {
|
||||
if count > 1 {
|
||||
if prop := c.getPropertyOfUnionOrIntersectionType(t, propName, true /*skipObjectFunctionPropertyAugment*/); prop != nil && c.isNeverReducedProperty(prop) {
|
||||
for i := range counts.entries {
|
||||
if e := counts.entries[i]; e.count > 1 {
|
||||
if prop := c.getPropertyOfUnionOrIntersectionType(t, e.name, true /*skipObjectFunctionPropertyAugment*/); prop != nil && c.isNeverReducedProperty(prop) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,33 +307,40 @@ func TestIntersectionReductionAllocations(t *testing.T) { //nolint:paralleltest
|
||||
func TestTemporaryPropertyCounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := &Checker{}
|
||||
names := func(p *temporaryPropertyCounts) []string {
|
||||
var result []string
|
||||
for _, e := range p.entries {
|
||||
result = append(result, e.name)
|
||||
}
|
||||
return result
|
||||
}
|
||||
outer := c.getTemporaryPropertyCounts()
|
||||
outer.Set("second", 1)
|
||||
outer.Set("first", 1)
|
||||
outer.Set("second", 2)
|
||||
assert.Assert(t, slices.Equal(slices.Collect(outer.Keys()), []string{"second", "first"}))
|
||||
outer.Add("second")
|
||||
outer.Add("first")
|
||||
outer.Add("second")
|
||||
assert.Assert(t, slices.Equal(names(outer), []string{"second", "first"}))
|
||||
inner := c.getTemporaryPropertyCounts()
|
||||
assert.Assert(t, inner != outer)
|
||||
inner.Set("first", 4)
|
||||
assert.Equal(t, outer.GetOrZero("first"), 1)
|
||||
assert.Equal(t, outer.GetOrZero("second"), 2)
|
||||
inner.Add("first")
|
||||
assert.Equal(t, outer.Count("first"), 1)
|
||||
assert.Equal(t, outer.Count("second"), 2)
|
||||
c.releaseTemporaryPropertyCounts(inner)
|
||||
assert.Equal(t, inner.Size(), 0)
|
||||
assert.Assert(t, !inner.Has("first"))
|
||||
assert.Equal(t, inner.Count("first"), 0)
|
||||
reused := c.getTemporaryPropertyCounts()
|
||||
assert.Equal(t, reused, inner)
|
||||
reused.Set("second", 3)
|
||||
reused.Set("first", 5)
|
||||
assert.Assert(t, slices.Equal(slices.Collect(reused.Keys()), []string{"second", "first"}))
|
||||
assert.Equal(t, outer.GetOrZero("first"), 1)
|
||||
reused.Add("second")
|
||||
reused.Add("first")
|
||||
assert.Assert(t, slices.Equal(names(reused), []string{"second", "first"}))
|
||||
assert.Equal(t, outer.Count("first"), 1)
|
||||
c.releaseTemporaryPropertyCounts(reused)
|
||||
c.releaseTemporaryPropertyCounts(outer)
|
||||
runtime.GC()
|
||||
reused = c.getTemporaryPropertyCounts()
|
||||
assert.Equal(t, reused.Size(), 0)
|
||||
assert.Assert(t, !reused.Has("second"))
|
||||
assert.Equal(t, reused.Count("second"), 0)
|
||||
for i := range 4097 {
|
||||
reused.Set(strconv.Itoa(i), i)
|
||||
reused.Add(strconv.Itoa(i))
|
||||
}
|
||||
c.releaseTemporaryPropertyCounts(reused)
|
||||
for counts := c.freePropertyCounts; counts != nil; counts = counts.next {
|
||||
@@ -364,7 +371,7 @@ func TestIntersectionReductionReentrancy(t *testing.T) {
|
||||
assert.Equal(t, c.freePropertyCounts, workspace)
|
||||
for counts := c.freePropertyCounts; counts != nil; counts = counts.next {
|
||||
assert.Equal(t, counts.Size(), 0)
|
||||
assert.Assert(t, !counts.Has("value"))
|
||||
assert.Equal(t, counts.Count("value"), 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/microsoft/TypeScript/tsc/internal/ast"
|
||||
"github.com/microsoft/TypeScript/tsc/internal/binder"
|
||||
"github.com/microsoft/TypeScript/tsc/internal/collections"
|
||||
"github.com/microsoft/TypeScript/tsc/internal/core"
|
||||
"github.com/microsoft/TypeScript/tsc/internal/debug"
|
||||
"github.com/microsoft/TypeScript/tsc/internal/diagnostics"
|
||||
@@ -933,9 +932,50 @@ func (c *Checker) releaseTemporaryTypeSet(set *temporaryTypeSet) {
|
||||
c.freeTypeSets = set
|
||||
}
|
||||
|
||||
// temporaryPropertyCounts counts property names in first-occurrence order.
|
||||
type temporaryPropertyCounts struct {
|
||||
collections.OrderedMap[string, int]
|
||||
next *temporaryPropertyCounts
|
||||
entries []propertyCount
|
||||
indices map[string]int32
|
||||
next *temporaryPropertyCounts
|
||||
}
|
||||
|
||||
type propertyCount struct {
|
||||
name string
|
||||
count int32
|
||||
}
|
||||
|
||||
func (p *temporaryPropertyCounts) Add(name string) {
|
||||
if i, ok := p.indices[name]; ok {
|
||||
p.entries[i].count++
|
||||
return
|
||||
}
|
||||
p.AddNew(name)
|
||||
}
|
||||
|
||||
// AddNew adds a name known not to have been added yet, skipping the lookup.
|
||||
func (p *temporaryPropertyCounts) AddNew(name string) {
|
||||
if p.indices == nil {
|
||||
p.indices = make(map[string]int32)
|
||||
}
|
||||
p.indices[name] = int32(len(p.entries))
|
||||
p.entries = append(p.entries, propertyCount{name: name, count: 1})
|
||||
}
|
||||
|
||||
func (p *temporaryPropertyCounts) Count(name string) int {
|
||||
if i, ok := p.indices[name]; ok {
|
||||
return int(p.entries[i].count)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p *temporaryPropertyCounts) Size() int {
|
||||
return len(p.entries)
|
||||
}
|
||||
|
||||
func (p *temporaryPropertyCounts) Clear() {
|
||||
clear(p.entries)
|
||||
p.entries = p.entries[:0]
|
||||
clear(p.indices)
|
||||
}
|
||||
|
||||
func (c *Checker) getTemporaryPropertyCounts() *temporaryPropertyCounts {
|
||||
|
||||
Reference in new issue
Block a user