@@ -1695,6 +1695,87 @@ export class NodeLinks extends SpeculatableLinks {
1695
1695
declare fakeScopeForSignatureDeclaration?: "params" | "typeParams";
1696
1696
}
1697
1697
1698
+ /**
1699
+ * SpeculatableMap is like a Map, except it lazily follows the speculation state of the host
1700
+ *
1701
+ * Also, it doesn't support `.delete` or `.clear` - not because it couldn't, buit just because
1702
+ * the use for this (relationship caches), doesn't use either feature of `Map`s.
1703
+ */
1704
+ class SpeculatableMap<K, V> implements Map<K, V> {
1705
+ private innerMap = new Map<K, [epoch: number, value: V][]>();
1706
+ constructor(public host: SpeculationHost) {}
1707
+
1708
+ private getCurrentStateFromList(key: K, valueList: [epoch: number, value: V][]): [exists: boolean, value: V] {
1709
+ const discarded = this.host.getDiscardedSpeculativeEpochs();
1710
+ let unwrappedValue: V | undefined;
1711
+ let exists = false;
1712
+ for (let i = valueList.length - 1; i >= 0; i--) {
1713
+ const [epoch, value] = valueList[i];
1714
+ if (discarded.has(epoch)) {
1715
+ valueList.splice(i, 1);
1716
+ if (valueList.length === 0) {
1717
+ this.innerMap.delete(key);
1718
+ break;
1719
+ }
1720
+ continue;
1721
+ }
1722
+ unwrappedValue = value;
1723
+ exists = true;
1724
+ break;
1725
+ }
1726
+ return [exists, unwrappedValue!];
1727
+ }
1728
+
1729
+ get(key: K): V | undefined {
1730
+ const valueList = this.innerMap.get(key);
1731
+ if (!valueList || !valueList.length) return undefined;
1732
+ const [ exists, unwrappedValue ] = this.getCurrentStateFromList(key, valueList);
1733
+ if (!exists) return undefined;
1734
+ return unwrappedValue;
1735
+ }
1736
+ has(key: K): boolean {
1737
+ const valueList = this.innerMap.get(key);
1738
+ if (!valueList || !valueList.length) return false;
1739
+ const [ exists, _unwrappedValue ] = this.getCurrentStateFromList(key, valueList);
1740
+ return exists;
1741
+ }
1742
+ set(key: K, value: V): this {
1743
+ const valueList = this.innerMap.get(key) || this.innerMap.set(key, []).get(key)!;
1744
+ valueList.push([ this.host.getCurrentSpeculativeEpoch(), value ]);
1745
+ return this;
1746
+ }
1747
+ /**
1748
+ * Approximate - does not include lazy removals
1749
+ */
1750
+ get size(): number {
1751
+ return this.innerMap.size;
1752
+ }
1753
+
1754
+ // These methods are unimplemented mostly because they're thus-far unused.
1755
+ declare clear: never;
1756
+ declare delete: never;
1757
+ declare entries: never;
1758
+ declare values: never;
1759
+ declare [globalThis.Symbol.iterator]: never;
1760
+
1761
+ // Implemented just to provide utlity when debugging
1762
+ forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void {
1763
+ return this.innerMap.forEach((valueList, k, _inner) => {
1764
+ const [ exists, unwrappedValue ] = this.getCurrentStateFromList(k, valueList);
1765
+ if (exists) {
1766
+ return callbackfn.call(thisArg, unwrappedValue, k, this);
1767
+ }
1768
+ });
1769
+ }
1770
+
1771
+ // Likewise, provided for debuggability
1772
+ keys(): IterableIterator<K> {
1773
+ return this.innerMap.keys();
1774
+ }
1775
+
1776
+ [globalThis.Symbol.toStringTag] = SpeculatableMap.name;
1777
+ }
1778
+
1698
1779
/** @internal */
1699
1780
export function getNodeId(node: Node): number {
1700
1781
if (!node.id) {
@@ -2586,12 +2667,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2586
2667
2587
2668
// Relationship caches need to be speculative so we can un-upgrade "ErrorAndReported" results back down, since we're rewinding the error
2588
2669
// Otherwise, we think we've emitted lots of elaboration that we just swallow up in discarded speculative contexts
2589
- var subtypeRelation = registerSpeculativeCache( new Map <string, RelationComparisonResult>() );
2590
- var strictSubtypeRelation = registerSpeculativeCache( new Map <string, RelationComparisonResult>() );
2591
- var assignableRelation = registerSpeculativeCache( new Map <string, RelationComparisonResult>() );
2592
- var comparableRelation = registerSpeculativeCache( new Map <string, RelationComparisonResult>() );
2593
- var identityRelation = registerSpeculativeCache( new Map <string, RelationComparisonResult>() );
2594
- var enumRelation = registerSpeculativeCache( new Map <string, RelationComparisonResult>() );
2670
+ var subtypeRelation = new SpeculatableMap <string, RelationComparisonResult>(speculationHost );
2671
+ var strictSubtypeRelation = new SpeculatableMap <string, RelationComparisonResult>(speculationHost );
2672
+ var assignableRelation = new SpeculatableMap <string, RelationComparisonResult>(speculationHost );
2673
+ var comparableRelation = new SpeculatableMap <string, RelationComparisonResult>(speculationHost );
2674
+ var identityRelation = new SpeculatableMap <string, RelationComparisonResult>(speculationHost );
2675
+ var enumRelation = new SpeculatableMap <string, RelationComparisonResult>(speculationHost );
2595
2676
2596
2677
var builtinGlobals = createSymbolTable();
2597
2678
builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol);
0 commit comments