From 24fa19d3dae3c75d02f89a176874fec8de1c4459 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 1 Oct 2019 16:53:14 -0700 Subject: [PATCH 01/10] Add full implemention of Map and Set to shims --- src/compiler/core.ts | 32 +- src/compiler/corePublic.ts | 182 +++++-- src/compiler/utilities.ts | 2 +- src/shims/collectionShims.ts | 490 ++++++++++++++++++ src/shims/mapShim.ts | 220 -------- src/shims/tsconfig.json | 2 +- src/testRunner/unittests/createMapShim.ts | 127 +++-- .../reference/api/tsserverlibrary.d.ts | 50 +- tests/baselines/reference/api/typescript.d.ts | 50 +- 9 files changed, 839 insertions(+), 316 deletions(-) create mode 100644 src/shims/collectionShims.ts delete mode 100644 src/shims/mapShim.ts diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 4be5998d6f3f9..092d95df86276 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -5,13 +5,17 @@ namespace ts { export const emptyArray: never[] = [] as never[]; /** Create a new map. */ - export function createMap(): Map { - return new Map(); + export function createMap(): Map; + export function createMap(): ESMap; + export function createMap(): ESMap { + return new Map(); } /** Create a new map from an array of entries. */ - export function createMapFromEntries(entries: [string, T][]): Map { - const map = createMap(); + export function createMapFromEntries(entries: readonly [string, T][]): Map; + export function createMapFromEntries(entries: readonly [K, V][]): ESMap; + export function createMapFromEntries(entries: readonly [K, V][]): ESMap { + const map = createMap(); for (const [key, value] of entries) { map.set(key, value); } @@ -20,7 +24,7 @@ namespace ts { /** Create a new map from a template object is provided, the map will copy entries from it. */ export function createMapFromTemplate(template: MapLike): Map { - const map: Map = new Map(); + const map: Map = new Map(); // Copies keys/values from template. Note that for..in will not throw if // template is undefined, and instead will just exit the loop. @@ -33,6 +37,18 @@ namespace ts { return map; } + export function createSet(): Set { + return new Set(); + } + + export function createSetFromValues(values: readonly T[]): Set { + const set = createSet(); + for (const value of values) { + set.add(value); + } + return set; + } + export function length(array: readonly any[] | undefined): number { return array ? array.length : 0; } @@ -1587,7 +1603,7 @@ namespace ts { * Case-sensitive comparisons compare both strings one code-point at a time using the integer * value of each code-point after applying `toUpperCase` to each string. We always map both * strings to their upper-case form as some unicode characters do not properly round-trip to - * lowercase (such as `ẞ` (German sharp capital s)). + * lowercase (such as `ẞ` (German sharp capital s)). */ export function equateStringsCaseInsensitive(a: string, b: string) { return a === b @@ -1645,7 +1661,7 @@ namespace ts { * Case-insensitive comparisons compare both strings one code-point at a time using the integer * value of each code-point after applying `toUpperCase` to each string. We always map both * strings to their upper-case form as some unicode characters do not properly round-trip to - * lowercase (such as `ẞ` (German sharp capital s)). + * lowercase (such as `ẞ` (German sharp capital s)). */ export function compareStringsCaseInsensitive(a: string, b: string) { if (a === b) return Comparison.EqualTo; @@ -1717,7 +1733,7 @@ namespace ts { // // For case insensitive comparisons we always map both strings to their // upper-case form as some unicode characters do not properly round-trip to - // lowercase (such as `ẞ` (German sharp capital s)). + // lowercase (such as `ẞ` (German sharp capital s)). return (a, b) => compareWithCallback(a, b, compareDictionaryOrder); function compareDictionaryOrder(a: string, b: string) { diff --git a/src/compiler/corePublic.ts b/src/compiler/corePublic.ts index f77b4bda4c260..3d0b77fe2dc30 100644 --- a/src/compiler/corePublic.ts +++ b/src/compiler/corePublic.ts @@ -23,53 +23,82 @@ namespace ts { } /** ES6 Map interface, only read methods included. */ - export interface ReadonlyMap { - get(key: string): T | undefined; - has(key: string): boolean; - forEach(action: (value: T, key: string) => void): void; + export interface ReadonlyESMap { + get(key: K): V | undefined; + has(key: K): boolean; + forEach(action: (value: V, key: K) => void): void; readonly size: number; - keys(): Iterator; - values(): Iterator; - entries(): Iterator<[string, T]>; + keys(): Iterator; + values(): Iterator; + entries(): Iterator<[K, V]>; } /** ES6 Map interface. */ - export interface Map extends ReadonlyMap { - set(key: string, value: T): this; - delete(key: string): boolean; + export interface ESMap extends ReadonlyESMap { + set(key: K, value: V): this; + delete(key: K): boolean; clear(): void; } + /** ES6 Map interface, only read methods included. */ + export interface ReadonlyMap extends ReadonlyESMap { + } + + /** ES6 Map interface. */ + export interface Map extends ESMap, ReadonlyMap { + } + /* @internal */ export interface MapConstructor { // eslint-disable-next-line @typescript-eslint/prefer-function-type - new (): Map; + new (): ESMap; + } + + export interface ReadonlySet { + readonly size: number; + has(value: T): boolean; + forEach(action: (value: T, key: T) => void): void; + keys(): Iterator; + values(): Iterator; + entries(): Iterator<[T, T]>; + } + + export interface Set extends ReadonlySet { + add(value: T): this; + delete(value: T): boolean; + clear(): void; } - /** - * Returns the native Map implementation if it is available and compatible (i.e. supports iteration). - */ /* @internal */ - export function tryGetNativeMap(): MapConstructor | undefined { - // Internet Explorer's Map doesn't support iteration, so don't use it. - // Natives - // NOTE: TS doesn't strictly allow in-line declares, but if we suppress the error, the declaration - // is still used for typechecking _and_ correctly elided, which is out goal, as this prevents us from - // needing to pollute an outer scope with a declaration of `Map` just to satisfy the checks in this function - //@ts-ignore - declare const Map: (new () => Map) | undefined; - // eslint-disable-next-line no-in-operator - return typeof Map !== "undefined" && "entries" in Map.prototype ? Map : undefined; + export interface SetConstructor { + // eslint-disable-next-line @typescript-eslint/prefer-function-type + new (): Set; + } + + export interface WeakMap { + get(key: K): V | undefined; + has(key: K): boolean; + set(key: K, value: V): this; + delete(key: K): boolean; } /* @internal */ - export const Map: MapConstructor = tryGetNativeMap() || (() => { - // NOTE: createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. - if (typeof createMapShim === "function") { - return createMapShim(); - } - throw new Error("TypeScript requires an environment that provides a compatible native Map implementation."); - })(); + export interface WeakMapConstructor { + // eslint-disable-next-line @typescript-eslint/prefer-function-type + new (): WeakMap; + } + + export interface WeakSet { + has(key: T): boolean; + add(key: T): this; + delete(key: T): boolean; + } + + /* @internal */ + export interface WeakSetConstructor { + // eslint-disable-next-line @typescript-eslint/prefer-function-type + new (): WeakSet; + } /** ES6 Iterator type. */ export interface Iterator { @@ -94,4 +123,95 @@ namespace ts { EqualTo = 0, GreaterThan = 1 } + + namespace NativeCollections { + declare const Map: MapConstructor | undefined; + declare const Set: SetConstructor | undefined; + declare const WeakMap: WeakMapConstructor | undefined; + declare const WeakSet: WeakSetConstructor | undefined; + + export function tryGetNativeMap(): MapConstructor | undefined { + // Internet Explorer's Map doesn't support iteration, so don't use it. + // eslint-disable-next-line no-in-operator + return typeof Map !== "undefined" && "entries" in Map.prototype ? Map : undefined; + } + + export function tryGetNativeSet(): SetConstructor | undefined { + // Internet Explorer's Set doesn't support iteration, so don't use it. + // eslint-disable-next-line no-in-operator + return typeof Set !== "undefined" && "entries" in Set.prototype ? Set : undefined; + } + + export function tryGetNativeWeakMap(): WeakMapConstructor | undefined { + return typeof WeakMap !== "undefined" ? WeakMap : undefined; + } + + export function tryGetNativeWeakSet(): WeakSetConstructor | undefined { + return typeof WeakSet !== "undefined" ? WeakSet : undefined; + } + } + + /** + * Returns the native Map implementation if it is available and compatible (i.e. supports iteration). + */ + /* @internal */ + export function tryGetNativeMap() { + return NativeCollections.tryGetNativeMap(); + } + + /** + * Returns the native Set implementation if it is available and compatible (i.e. supports iteration). + */ + /* @internal */ + export function tryGetNativeSet() { + return NativeCollections.tryGetNativeSet(); + } + + /** + * Returns the native WeakMap implementation if it is available. + */ + /* @internal */ + export function tryGetNativeWeakMap() { + return NativeCollections.tryGetNativeWeakMap(); + } + + /** + * Returns the native WeakSet implementation if it is available. + */ + /* @internal */ + export function tryGetNativeWeakSet() { + return NativeCollections.tryGetNativeWeakSet(); + } + + export const Map: MapConstructor = tryGetNativeMap() || (() => { + // NOTE: ts.createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. + if (typeof createMapShim === "function") { + return createMapShim(); + } + throw new Error("TypeScript requires an environment that provides a compatible native Map implementation."); + })(); + + export const Set: SetConstructor = tryGetNativeSet() || (() => { + // NOTE: ts.createSetShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. + if (typeof createSetShim === "function") { + return createSetShim(); + } + throw new Error("TypeScript requires an environment that provides a compatible native Set implementation."); + })(); + + export const WeakMap: WeakMapConstructor = tryGetNativeWeakMap() || (() => { + // NOTE: ts.createWeakMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. + if (typeof createWeakMapShim === "function") { + return createWeakMapShim(); + } + throw new Error("TypeScript requires an environment that provides a compatible native WeakMap implementation."); + })(); + + export const WeakSet: WeakSetConstructor = tryGetNativeWeakSet() || (() => { + // NOTE: ts.createWeakSetShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. + if (typeof createWeakSetShim === "function") { + return createWeakSetShim(); + } + throw new Error("TypeScript requires an environment that provides a compatible native WeakSet implementation."); + })(); } \ No newline at end of file diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b147e79348733..6ce2655a4ad8f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -24,7 +24,7 @@ namespace ts { /** Create a new escaped identifier map. */ export function createUnderscoreEscapedMap(): UnderscoreEscapedMap { - return new Map() as UnderscoreEscapedMap; + return new Map() as UnderscoreEscapedMap; } export function hasEntries(map: ReadonlyUnderscoreEscapedMap | undefined): map is ReadonlyUnderscoreEscapedMap { diff --git a/src/shims/collectionShims.ts b/src/shims/collectionShims.ts new file mode 100644 index 0000000000000..e54c6fa470494 --- /dev/null +++ b/src/shims/collectionShims.ts @@ -0,0 +1,490 @@ +/* @internal */ +namespace ts { + interface IteratorShim { + next(): { value: T, done?: false } | { value: never, done: true }; + } + + interface MapShim { + readonly size: number; + get(key: K): V | undefined; + set(key: K, value: V): this; + has(key: K): boolean; + delete(key: K): boolean; + clear(): void; + keys(): IteratorShim; + values(): IteratorShim; + entries(): IteratorShim<[K, V]>; + forEach(action: (value: V, key: K) => void): void; + } + + interface SetShim { + readonly size: number; + add(value: T): this; + delete(value: T): boolean; + has(value: T): boolean; + clear(): void; + keys(): IteratorShim; + values(): IteratorShim; + entries(): IteratorShim<[T, T]>; + forEach(action: (value: T, key: T) => void): void; + } + + interface WeakMapShim { + get(key: K): V | undefined; + has(key: K): boolean; + set(key: K, value: V): this; + delete(key: K): boolean; + } + + interface WeakSetShim { + has(key: T): boolean; + add(key: T): this; + delete(key: T): boolean; + } + + interface WeakMapData { + objects?: ObjectMapEntry; + } + + interface MapData { + size: number; + strings?: Record>; + numbers?: MapEntry[]; + objects?: ObjectMapEntry; + true?: MapEntry; + false?: MapEntry; + null?: MapEntry; + undefined?: MapEntry; + + // Linked list references for iterators. + // See https://github.com/Microsoft/TypeScript/pull/27292 + // for more information. + + /** + * The first entry in the linked list. + * Note that this is only a stub that serves as starting point + * for iterators and doesn't contain a key and a value. + */ + readonly first: MapEntry; + last: MapEntry; + } + + interface MapEntry { + readonly key?: K; + value?: V; + + // Linked list references for iterators. + nextIterationEntry?: MapEntry; + previousIterationEntry?: MapEntry; + + /** + * Specifies if iterators should skip the next entry. + * This will be set when an entry is deleted. + * See https://github.com/Microsoft/TypeScript/pull/27292 for more information. + */ + skipNextIteration?: boolean; + } + + interface ObjectMapEntry extends MapEntry { + // Linked list references for object-based keys + next?: ObjectMapEntry; + } + + function isObject(value: unknown): value is object { + // eslint-disable-next-line no-null/no-null + return typeof value === "object" && value !== null + || typeof value === "function"; + } + + function createDictionaryObject(): Record { + const map = Object.create(/*prototype*/ null); // eslint-disable-line no-null/no-null + + // Using 'delete' on an object causes V8 to put the object in dictionary mode. + // This disables creation of hidden classes, which are expensive when an object is + // constantly changing shape. + map.__ = undefined; + delete map.__; + return map; + } + + function tryAddIterator(value: T, iterator: (this: T) => IteratorShim) { + if (typeof Symbol === "function" && Symbol.iterator !== undefined) { + (value as any)[Symbol.iterator] = iterator; + } + } + + function createIterator(name: string) { + class Iterator { + private currentEntry?: MapEntry; + private selector: (key: K, value: V) => U; + + constructor(data: MapData, selector: (key: K, value: V) => U) { + this.currentEntry = data.first; + this.selector = selector; + } + + public next(): { value: U, done?: false } | { value: never, done: true } { + // Navigate to the next entry. + while (this.currentEntry) { + const skipNext = !!this.currentEntry.skipNextIteration; + this.currentEntry = this.currentEntry.nextIterationEntry; + if (!skipNext) { + break; + } + } + + if (this.currentEntry) { + return { value: this.selector(this.currentEntry.key!, this.currentEntry.value!), done: false }; + } + else { + return { value: undefined as never, done: true }; + } + } + } + tryAddIterator(Iterator.prototype, function() { return this; }) + Object.defineProperty(Iterator, "name", { + ...Object.getOwnPropertyDescriptor(Iterator, "name"), + configurable: true, + value: name + }); + return Iterator; + } + + function hasEntry(data: MapData, key: K): boolean { + /* eslint-disable no-in-operator, no-null/no-null */ + return typeof key === "string" ? !!data.strings && key in data.strings : + typeof key === "number" ? !!data.numbers && key in data.numbers : + typeof key === "boolean" ? key ? !!data.true : !!data.false : + key === null ? !!data.null : + key === undefined ? !!data.undefined : + !!getObjectEntry(data, key); + // eslint-enable no-in-operator, no-null/no-null + } + + function getEntry(data: MapData, key: K): MapEntry | undefined { + /* eslint-disable no-null/no-null */ + return typeof key === "string" ? data.strings && data.strings[key] : + typeof key === "number" ? data.numbers && data.numbers[key] : + typeof key === "boolean" ? key ? data.true : data.false : + key === undefined ? data.undefined : + key === null ? data.null : + getObjectEntry(data, key); + /* eslint-enable no-null/no-null */ + } + + function getObjectEntry(data: WeakMapData, key: K): ObjectMapEntry | undefined { + for (let node = data.objects; node; node = node.next) { + if (node.key === key) { + return node; + } + } + } + + function addOrUpdateEntry(data: MapData, key: K, value: V): MapEntry | undefined { + let entry: MapEntry | undefined; + if (typeof key === "string") { + if (!data.strings) data.strings = createDictionaryObject(); + if (!data.strings[key]) { + entry = data.strings[key] = { key, value }; + } + else { + data.strings[key].value = value; + } + } + else if (typeof key === "number") { + if (!data.numbers) data.numbers = []; + if (!data.numbers[key]) { + entry = data.numbers[key] = { key, value }; + } + else { + data.numbers[key].value = value; + } + } + else { + /* eslint-disable no-null/no-null */ + const prop = typeof key === "boolean" ? key ? "true" : "false" : + key === null ? "null" : + key === undefined ? "undefined" : + undefined; + /* eslint-enable no-null/no-null */ + if (prop) { + if (!data[prop]) { + entry = data[prop] = { key, value }; + } + else { + data[prop]!.value = value; + } + } + else { + entry = addOrUpdateObjectEntry(data, key, value); + } + } + if (entry) { + addToIteration(data, entry); + data.size++; + } + return entry; + } + + function addOrUpdateObjectEntry(data: WeakMapData, key: K, value: V): ObjectMapEntry | undefined { + if (!data.objects) return data.objects = { key, value, next: undefined }; + const existing = getObjectEntry(data, key); + if (!existing) return data.objects = { key, value, next: data.objects }; + existing.value = value; + } + + function deleteEntry(data: MapData, key: K): MapEntry | undefined { + let entry: MapEntry | undefined; + if (typeof key === "string") { + if (data.strings) { + entry = data.strings[key]; + if (entry) { + delete data.strings[key]; + } + } + } + else if (typeof key === "number") { + if (data.numbers) { + entry = data.numbers[key]; + if (entry) { + delete data.numbers[key]; + } + } + } + else { + /* eslint-disable no-null/no-null */ + const prop = typeof key === "boolean" ? key ? "true" : "false" : + key === null ? "null" : + key === undefined ? "undefined" : + undefined; + /* eslint-enable no-null/no-null */ + if (prop) { + entry = data[prop]; + if (entry) { + data[prop] = undefined; + } + } + else { + entry = deleteObjectEntry(data, key); + } + } + if (entry) { + removeFromIteration(data, entry); + data.size--; + } + return entry; + } + + function deleteObjectEntry(data: WeakMapData, key: K): ObjectMapEntry | undefined { + let prev: ObjectMapEntry | undefined; + for (let node = data.objects; node; prev = node, node = node.next) { + if (node.key === key) { + if (prev) { + prev.next = node.next; + } + else { + data.objects = node.next; + } + node.next = undefined; + return node; + } + } + } + + function clearEntries(data: MapData) { + if (data.strings) data.strings = undefined; + if (data.numbers) data.numbers = undefined; + if (data.true) data.true = undefined; + if (data.false) data.false = undefined; + if (data.null) data.null = undefined; + if (data.undefined) data.undefined = undefined; + clearObjectEntries(data); + removeAllFromIteration(data); + data.size = 0; + } + + function clearObjectEntries(data: WeakMapData) { + if (data.objects) { + let node = data.objects; + data.objects = undefined; + while (node) { + const next = node.next!; + node.next = undefined; + node = next; + } + } + } + + function addToIteration(data: MapData, newEntry: MapEntry) { + // Adjust the references. + const previousLastEntry = data.last; + previousLastEntry.nextIterationEntry = newEntry; + newEntry.previousIterationEntry = previousLastEntry; + data.last = newEntry; + } + + function removeFromIteration(data: MapData, entry: MapEntry) { + // Adjust the linked list references of the neighbor entries. + const previousEntry = entry.previousIterationEntry!; + previousEntry.nextIterationEntry = entry.nextIterationEntry; + if (entry.nextIterationEntry) { + entry.nextIterationEntry.previousIterationEntry = previousEntry; + } + + // When the deleted entry was the last one, we need to + // adjust the lastEntry reference. + if (data.last === entry) { + data.last = previousEntry; + } + + // Adjust the forward reference of the deleted entry + // in case an iterator still references it. This allows us + // to throw away the entry, but when an active iterator + // (which points to the current entry) continues, it will + // navigate to the entry that originally came before the + // current one and skip it. + entry.previousIterationEntry = undefined; + entry.nextIterationEntry = previousEntry; + entry.skipNextIteration = true; + } + + function removeAllFromIteration(data: MapData) { + // Reset the linked list. Note that we must adjust the forward + // references of the deleted entries to ensure iterators stuck + // in the middle of the list don't continue with deleted entries, + // but can continue with new entries added after the clear() + // operation. + const firstEntry = data.first; + let currentEntry = firstEntry.nextIterationEntry; + while (currentEntry) { + const nextEntry = currentEntry.nextIterationEntry; + currentEntry.previousIterationEntry = undefined; + currentEntry.nextIterationEntry = firstEntry; + currentEntry.skipNextIteration = true; + currentEntry = nextEntry; + } + + firstEntry.nextIterationEntry = undefined; + data.last = firstEntry; + } + + function forEachEntry(data: MapData, action: (value: V, key: K) => void) { + let currentEntry: MapEntry | undefined = data.first; + while (currentEntry) { + const skipNext = !!currentEntry.skipNextIteration; + currentEntry = currentEntry.nextIterationEntry; + if (skipNext) { + continue; + } + if (currentEntry) { + action(currentEntry.value!, currentEntry.key!); + } + } + } + + function createMapData(): MapData { + const entry: MapEntry = {}; + return { + first: entry, + last: entry, + size: 0 + }; + } + + function createWeakMapData(): WeakMapData { + return {}; + } + + function pickKey(key: K, _value: V) { return key; } + function pickValue(_key: K, value: V) { return value; } + function pickEntry(key: K, value: V) { return [key, value] as [K, V]; } + + /* @internal */ + export function createMapShim(): new () => MapShim { + const MapIterator = createIterator("MapIterator"); + class Map implements MapShim { + private _mapData = createMapData(); + get size() { return this._mapData.size; } + get(key: K): V | undefined { return getEntry(this._mapData, key)?.value; } + set(key: K, value: V): this { return addOrUpdateEntry(this._mapData, key, value), this; } + has(key: K): boolean { return hasEntry(this._mapData, key); } + delete(key: K): boolean { return !!deleteEntry(this._mapData, key); } + clear(): void { clearEntries(this._mapData); } + keys(): IteratorShim { return new MapIterator(this._mapData, pickKey); } + values(): IteratorShim { return new MapIterator(this._mapData, pickValue); } + entries(): IteratorShim<[K, V]> { return new MapIterator(this._mapData, pickEntry); } + forEach(action: (value: V, key: K) => void): void { forEachEntry(this._mapData, action); } + } + tryAddIterator(Map.prototype, Map.prototype.entries); + return Map; + } + + /* @internal */ + export function createSetShim(): new () => SetShim { + const SetIterator = createIterator("SetIterator"); + class Set implements SetShim { + private _mapData = createMapData(); + get size() { return this._mapData.size; } + add(value: T): this { return addOrUpdateEntry(this._mapData, value, value), this; } + has(value: T): boolean { return hasEntry(this._mapData, value); } + delete(value: T): boolean { return !!deleteEntry(this._mapData, value); } + clear(): void { clearEntries(this._mapData); } + keys(): IteratorShim { return new SetIterator(this._mapData, pickKey); } + values(): IteratorShim { return new SetIterator(this._mapData, pickValue); } + entries(): IteratorShim<[T, T]> { return new SetIterator(this._mapData, pickEntry); } + forEach(action: (value: T, key: T) => void): void { forEachEntry(this._mapData, action); } + } + tryAddIterator(Set.prototype, Set.prototype.values); + return Set; + } + + // NOTE: Not a real WeakMap, this implementation will hold onto references until it is GC'ed. However, it's the best + // we can do without storing data on each value itself. + /* @internal */ + export function createWeakMapShim(): new () => WeakMapShim { + class WeakMap implements WeakMapShim { + private _mapData = createWeakMapData(); + get(key: K): V | undefined { + if (!isObject(key)) throw new TypeError("Invalid value used as weak map key"); + return getObjectEntry(this._mapData, key)?.value; + } + set(key: K, value: V): this { + if (!isObject(key)) throw new TypeError("Invalid value used as weak map key"); + addOrUpdateObjectEntry(this._mapData, key, value); + return this; + } + has(key: K): boolean { + if (!isObject(key)) throw new TypeError("Invalid value used as weak map key"); + return !!getObjectEntry(this._mapData, key); + } + delete(key: K): boolean { + if (!isObject(key)) throw new TypeError("Invalid value used as weak map key"); + return !!deleteObjectEntry(this._mapData, key); + } + } + return WeakMap; + } + + // NOTE: Not a real WeakSet, this implementation will hold onto references until it is GC'ed. However, it's the best + // we can do without storing data on each value itself. + /* @internal */ + export function createWeakSetShim(): new () => WeakSetShim { + class WeakSet implements WeakSetShim { + private _mapData = createWeakMapData(); + add(value: T): this { + if (!isObject(value)) throw new TypeError("Invalid value used in weak set"); + addOrUpdateObjectEntry(this._mapData, value, value); + return this; + } + has(value: T): boolean { + if (!isObject(value)) throw new TypeError("Invalid value used in weak set"); + return !!getObjectEntry(this._mapData, value); + } + delete(value: T): boolean { + if (!isObject(value)) throw new TypeError("Invalid value used in weak set"); + return !!deleteObjectEntry(this._mapData, value); + } + } + return WeakSet; + } +} diff --git a/src/shims/mapShim.ts b/src/shims/mapShim.ts deleted file mode 100644 index 23391150941b7..0000000000000 --- a/src/shims/mapShim.ts +++ /dev/null @@ -1,220 +0,0 @@ -/* @internal */ -namespace ts { - interface IteratorShim { - next(): { value: T, done?: false } | { value: never, done: true }; - } - interface MapShim { - readonly size: number; - get(key: string): T | undefined; - set(key: string, value: T): this; - has(key: string): boolean; - delete(key: string): boolean; - clear(): void; - keys(): IteratorShim; - values(): IteratorShim; - entries(): IteratorShim<[string, T]>; - forEach(action: (value: T, key: string) => void): void; - } - export function createMapShim(): new () => MapShim { - /** Create a MapLike with good performance. */ - function createDictionaryObject(): Record { - const map = Object.create(/*prototype*/ null); // eslint-disable-line no-null/no-null - - // Using 'delete' on an object causes V8 to put the object in dictionary mode. - // This disables creation of hidden classes, which are expensive when an object is - // constantly changing shape. - map.__ = undefined; - delete map.__; - - return map; - } - - interface MapEntry { - readonly key?: string; - value?: T; - - // Linked list references for iterators. - nextEntry?: MapEntry; - previousEntry?: MapEntry; - - /** - * Specifies if iterators should skip the next entry. - * This will be set when an entry is deleted. - * See https://github.com/Microsoft/TypeScript/pull/27292 for more information. - */ - skipNext?: boolean; - } - - class MapIterator { - private currentEntry?: MapEntry; - private selector: (key: string, value: T) => U; - - constructor(currentEntry: MapEntry, selector: (key: string, value: T) => U) { - this.currentEntry = currentEntry; - this.selector = selector; - } - - public next(): { value: U, done?: false } | { value: never, done: true } { - // Navigate to the next entry. - while (this.currentEntry) { - const skipNext = !!this.currentEntry.skipNext; - this.currentEntry = this.currentEntry.nextEntry; - - if (!skipNext) { - break; - } - } - - if (this.currentEntry) { - return { value: this.selector(this.currentEntry.key!, this.currentEntry.value!), done: false }; - } - else { - return { value: undefined as never, done: true }; - } - } - } - - return class implements MapShim { - private data = createDictionaryObject>(); - public size = 0; - - // Linked list references for iterators. - // See https://github.com/Microsoft/TypeScript/pull/27292 - // for more information. - - /** - * The first entry in the linked list. - * Note that this is only a stub that serves as starting point - * for iterators and doesn't contain a key and a value. - */ - private readonly firstEntry: MapEntry; - private lastEntry: MapEntry; - - constructor() { - // Create a first (stub) map entry that will not contain a key - // and value but serves as starting point for iterators. - this.firstEntry = {}; - // When the map is empty, the last entry is the same as the - // first one. - this.lastEntry = this.firstEntry; - } - - get(key: string): T | undefined { - const entry = this.data[key] as MapEntry | undefined; - return entry && entry.value!; - } - - set(key: string, value: T): this { - if (!this.has(key)) { - this.size++; - - // Create a new entry that will be appended at the - // end of the linked list. - const newEntry: MapEntry = { - key, - value - }; - this.data[key] = newEntry; - - // Adjust the references. - const previousLastEntry = this.lastEntry; - previousLastEntry.nextEntry = newEntry; - newEntry.previousEntry = previousLastEntry; - this.lastEntry = newEntry; - } - else { - this.data[key].value = value; - } - - return this; - } - - has(key: string): boolean { - // eslint-disable-next-line no-in-operator - return key in this.data; - } - - delete(key: string): boolean { - if (this.has(key)) { - this.size--; - const entry = this.data[key]; - delete this.data[key]; - - // Adjust the linked list references of the neighbor entries. - const previousEntry = entry.previousEntry!; - previousEntry.nextEntry = entry.nextEntry; - if (entry.nextEntry) { - entry.nextEntry.previousEntry = previousEntry; - } - - // When the deleted entry was the last one, we need to - // adjust the lastEntry reference. - if (this.lastEntry === entry) { - this.lastEntry = previousEntry; - } - - // Adjust the forward reference of the deleted entry - // in case an iterator still references it. This allows us - // to throw away the entry, but when an active iterator - // (which points to the current entry) continues, it will - // navigate to the entry that originally came before the - // current one and skip it. - entry.previousEntry = undefined; - entry.nextEntry = previousEntry; - entry.skipNext = true; - - return true; - } - return false; - } - - clear(): void { - this.data = createDictionaryObject>(); - this.size = 0; - - // Reset the linked list. Note that we must adjust the forward - // references of the deleted entries to ensure iterators stuck - // in the middle of the list don't continue with deleted entries, - // but can continue with new entries added after the clear() - // operation. - const firstEntry = this.firstEntry; - let currentEntry = firstEntry.nextEntry; - while (currentEntry) { - const nextEntry = currentEntry.nextEntry; - currentEntry.previousEntry = undefined; - currentEntry.nextEntry = firstEntry; - currentEntry.skipNext = true; - - currentEntry = nextEntry; - } - firstEntry.nextEntry = undefined; - this.lastEntry = firstEntry; - } - - keys(): IteratorShim { - return new MapIterator(this.firstEntry, key => key); - } - - values(): IteratorShim { - return new MapIterator(this.firstEntry, (_key, value) => value); - } - - entries(): IteratorShim<[string, T]> { - return new MapIterator(this.firstEntry, (key, value) => [key, value] as [string, T]); - } - - forEach(action: (value: T, key: string) => void): void { - const iterator = this.entries(); - while (true) { - const iterResult = iterator.next(); - if (iterResult.done) { - break; - } - - const [key, value] = iterResult.value; - action(value, key); - } - } - }; - } -} \ No newline at end of file diff --git a/src/shims/tsconfig.json b/src/shims/tsconfig.json index cb058762fd616..572ac1538a9fe 100644 --- a/src/shims/tsconfig.json +++ b/src/shims/tsconfig.json @@ -4,6 +4,6 @@ "outFile": "../../built/local/shims.js" }, "files": [ - "mapShim.ts" + "collectionShims.ts" ] } diff --git a/src/testRunner/unittests/createMapShim.ts b/src/testRunner/unittests/createMapShim.ts index c49e1cc63ccc1..e335816ea72ee 100644 --- a/src/testRunner/unittests/createMapShim.ts +++ b/src/testRunner/unittests/createMapShim.ts @@ -1,63 +1,97 @@ namespace ts { describe("unittests:: createMapShim", () => { - function testMapIterationAddedValues(map: Map, useForEach: boolean): string { + const stringKeys = [ + "1", + "3", + "2", + "4", + "0", + "999", + "A", + "B", + "C", + "Z", + "X", + "X1", + "X2", + "Y" + ]; + + const mixedKeys = [ + true, + 3, + { toString() { return "2"; } }, + "4", + false, + null, // eslint-disable-line no-null/no-null + undefined, + "B", + { toString() { return "C"; } }, + "Z", + "X", + { toString() { return "X1"; } }, + "X2", + "Y" + ]; + + function testMapIterationAddedValues(keys: K[], map: ESMap, useForEach: boolean): string { let resultString = ""; - map.set("1", "1"); - map.set("3", "3"); - map.set("2", "2"); - map.set("4", "4"); + map.set(keys[0], "1"); + map.set(keys[1], "3"); + map.set(keys[2], "2"); + map.set(keys[3], "4"); let addedThree = false; - const doForEach = (value: string, key: string) => { + const doForEach = (value: string, key: K) => { resultString += `${key}:${value};`; // Add a new key ("0") - the map should provide this // one in the next iteration. - if (key === "1") { - map.set("1", "X1"); - map.set("0", "X0"); - map.set("4", "X4"); + if (key === keys[0]) { + map.set(keys[0], "X1"); + map.set(keys[4], "X0"); + map.set(keys[3], "X4"); } - else if (key === "3") { + else if (key === keys[1]) { if (!addedThree) { addedThree = true; // Remove and re-add key "3"; the map should // visit it after "0". - map.delete("3"); - map.set("3", "Y3"); + map.delete(keys[1]); + map.set(keys[1], "Y3"); // Change the value of "2"; the map should provide // it when visiting the key. - map.set("2", "Y2"); + map.set(keys[2], "Y2"); } else { // Check that an entry added when we visit the // currently last entry will still be visited. - map.set("999", "999"); + map.set(keys[5], "999"); } } - else if (key === "999") { + else if (key === keys[5]) { // Ensure that clear() behaves correctly same as removing all keys. - map.set("A", "A"); - map.set("B", "B"); - map.set("C", "C"); + map.set(keys[6], "A"); + map.set(keys[7], "B"); + map.set(keys[8], "C"); } - else if (key === "A") { + else if (key === keys[6]) { map.clear(); - map.set("Z", "Z"); + map.set(keys[9], "Z"); } - else if (key === "Z") { + else if (key === keys[9]) { // Check that the map behaves correctly when two items are // added and removed immediately. - map.set("X", "X"); - map.set("X1", "X1"); - map.set("X2", "X2"); - map.delete("X1"); - map.delete("X2"); - map.set("Y", "Y"); + map.set(keys[10], "X"); + map.set(keys[11], "X1"); + map.set(keys[12], "X2"); + map.delete(keys[11]); + map.delete(keys[12]); + map.set(keys[13], "Y"); } }; @@ -81,26 +115,49 @@ namespace ts { return resultString; } - it("iterates values in insertion order and handles changes", () => { + it("iterates values in insertion order and handles changes with string keys", () => { const expectedResult = "1:1;3:3;2:Y2;4:X4;0:X0;3:Y3;999:999;A:A;Z:Z;X:X;Y:Y;"; // First, ensure the test actually has the same behavior as a native Map. let nativeMap = createMap(); - const nativeMapForEachResult = testMapIterationAddedValues(nativeMap, /* useForEach */ true); + const nativeMapForEachResult = testMapIterationAddedValues(stringKeys, nativeMap, /* useForEach */ true); assert.equal(nativeMapForEachResult, expectedResult, "nativeMap-forEach"); nativeMap = createMap(); - const nativeMapIteratorResult = testMapIterationAddedValues(nativeMap, /* useForEach */ false); + const nativeMapIteratorResult = testMapIterationAddedValues(stringKeys, nativeMap, /* useForEach */ false); + assert.equal(nativeMapIteratorResult, expectedResult, "nativeMap-iterator"); + + // Then, test the map shim. + const MapShim = createMapShim(); // tslint:disable-line variable-name + let localShimMap = new MapShim(); + const shimMapForEachResult = testMapIterationAddedValues(stringKeys, localShimMap, /* useForEach */ true); + assert.equal(shimMapForEachResult, expectedResult, "shimMap-forEach"); + + localShimMap = new MapShim(); + const shimMapIteratorResult = testMapIterationAddedValues(stringKeys, localShimMap, /* useForEach */ false); + assert.equal(shimMapIteratorResult, expectedResult, "shimMap-iterator"); + }); + + it("iterates values in insertion order and handles changes with mixed-type keys", () => { + const expectedResult = "true:1;3:3;2:Y2;4:X4;false:X0;3:Y3;null:999;undefined:A;Z:Z;X:X;Y:Y;"; + + // First, ensure the test actually has the same behavior as a native Map. + let nativeMap = createMap(); + const nativeMapForEachResult = testMapIterationAddedValues(mixedKeys, nativeMap, /* useForEach */ true); + assert.equal(nativeMapForEachResult, expectedResult, "nativeMap-forEach"); + + nativeMap = createMap(); + const nativeMapIteratorResult = testMapIterationAddedValues(mixedKeys, nativeMap, /* useForEach */ false); assert.equal(nativeMapIteratorResult, expectedResult, "nativeMap-iterator"); // Then, test the map shim. const MapShim = createMapShim(); // tslint:disable-line variable-name - let localShimMap = new MapShim(); - const shimMapForEachResult = testMapIterationAddedValues(localShimMap, /* useForEach */ true); + let localShimMap = new MapShim(); + const shimMapForEachResult = testMapIterationAddedValues(mixedKeys, localShimMap, /* useForEach */ true); assert.equal(shimMapForEachResult, expectedResult, "shimMap-forEach"); - localShimMap = new MapShim(); - const shimMapIteratorResult = testMapIterationAddedValues(localShimMap, /* useForEach */ false); + localShimMap = new MapShim(); + const shimMapIteratorResult = testMapIterationAddedValues(mixedKeys, localShimMap, /* useForEach */ false); assert.equal(shimMapIteratorResult, expectedResult, "shimMap-iterator"); }); }); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 30b789890b3e7..0d0cf535cfbc0 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -32,21 +32,51 @@ declare namespace ts { " __sortedArrayBrand": any; } /** ES6 Map interface, only read methods included. */ - interface ReadonlyMap { - get(key: string): T | undefined; - has(key: string): boolean; - forEach(action: (value: T, key: string) => void): void; + interface ReadonlyESMap { + get(key: K): V | undefined; + has(key: K): boolean; + forEach(action: (value: V, key: K) => void): void; readonly size: number; - keys(): Iterator; - values(): Iterator; - entries(): Iterator<[string, T]>; + keys(): Iterator; + values(): Iterator; + entries(): Iterator<[K, V]>; + } + /** ES6 Map interface. */ + interface ESMap extends ReadonlyESMap { + set(key: K, value: V): this; + delete(key: K): boolean; + clear(): void; + } + /** ES6 Map interface, only read methods included. */ + interface ReadonlyMap extends ReadonlyESMap { } /** ES6 Map interface. */ - interface Map extends ReadonlyMap { - set(key: string, value: T): this; - delete(key: string): boolean; + interface Map extends ESMap, ReadonlyMap { + } + interface ReadonlySet { + readonly size: number; + has(value: T): boolean; + forEach(action: (value: T, key: T) => void): void; + keys(): Iterator; + values(): Iterator; + entries(): Iterator<[T, T]>; + } + interface Set extends ReadonlySet { + add(value: T): this; + delete(value: T): boolean; clear(): void; } + interface WeakMap { + get(key: K): V | undefined; + has(key: K): boolean; + set(key: K, value: V): this; + delete(key: K): boolean; + } + interface WeakSet { + has(key: T): boolean; + add(key: T): this; + delete(key: T): boolean; + } /** ES6 Iterator type. */ interface Iterator { next(): { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index c3149e9b7d17e..0aa33e2b43537 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -32,21 +32,51 @@ declare namespace ts { " __sortedArrayBrand": any; } /** ES6 Map interface, only read methods included. */ - interface ReadonlyMap { - get(key: string): T | undefined; - has(key: string): boolean; - forEach(action: (value: T, key: string) => void): void; + interface ReadonlyESMap { + get(key: K): V | undefined; + has(key: K): boolean; + forEach(action: (value: V, key: K) => void): void; readonly size: number; - keys(): Iterator; - values(): Iterator; - entries(): Iterator<[string, T]>; + keys(): Iterator; + values(): Iterator; + entries(): Iterator<[K, V]>; + } + /** ES6 Map interface. */ + interface ESMap extends ReadonlyESMap { + set(key: K, value: V): this; + delete(key: K): boolean; + clear(): void; + } + /** ES6 Map interface, only read methods included. */ + interface ReadonlyMap extends ReadonlyESMap { } /** ES6 Map interface. */ - interface Map extends ReadonlyMap { - set(key: string, value: T): this; - delete(key: string): boolean; + interface Map extends ESMap, ReadonlyMap { + } + interface ReadonlySet { + readonly size: number; + has(value: T): boolean; + forEach(action: (value: T, key: T) => void): void; + keys(): Iterator; + values(): Iterator; + entries(): Iterator<[T, T]>; + } + interface Set extends ReadonlySet { + add(value: T): this; + delete(value: T): boolean; clear(): void; } + interface WeakMap { + get(key: K): V | undefined; + has(key: K): boolean; + set(key: K, value: V): this; + delete(key: K): boolean; + } + interface WeakSet { + has(key: T): boolean; + add(key: T): this; + delete(key: T): boolean; + } /** ES6 Iterator type. */ interface Iterator { next(): { From eb3c9f448b1c43ac7200d81e124299083d25bee8 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 16 Jun 2020 15:29:31 -0700 Subject: [PATCH 02/10] Update default Map interface --- src/compiler/binder.ts | 6 +- src/compiler/builder.ts | 34 ++--- src/compiler/builderState.ts | 36 ++--- src/compiler/checker.ts | 82 +++++------ src/compiler/commandLineParser.ts | 46 +++--- src/compiler/core.ts | 135 +++++++++++------- src/compiler/corePublic.ts | 56 ++------ src/compiler/emitter.ts | 10 +- src/compiler/factory/emitHelpers.ts | 2 +- src/compiler/moduleNameResolver.ts | 22 +-- src/compiler/parser.ts | 6 +- src/compiler/performance.ts | 6 +- src/compiler/program.ts | 28 ++-- src/compiler/resolutionCache.ts | 22 +-- src/compiler/scanner.ts | 2 +- src/compiler/sourcemap.ts | 2 +- src/compiler/sys.ts | 2 +- src/compiler/transformers/declarations.ts | 12 +- src/compiler/transformers/es2015.ts | 8 +- src/compiler/transformers/generators.ts | 2 +- .../transformers/module/esnextAnd2015.ts | 2 +- src/compiler/transformers/utilities.ts | 4 +- src/compiler/tsbuildPublic.ts | 24 +--- src/compiler/types.ts | 72 ++++------ src/compiler/utilities.ts | 56 ++++---- src/compiler/watchPublic.ts | 4 +- src/compiler/watchUtilities.ts | 6 +- src/executeCommandLine/executeCommandLine.ts | 2 +- src/harness/client.ts | 2 +- src/harness/fourslashImpl.ts | 14 +- src/harness/fourslashInterfaceImpl.ts | 2 +- src/harness/harnessIO.ts | 10 +- src/harness/harnessLanguageService.ts | 2 +- src/harness/loggedIO.ts | 2 +- src/harness/virtualFileSystemWithWatch.ts | 58 ++++---- src/jsTyping/jsTyping.ts | 6 +- src/server/editorServices.ts | 38 ++--- src/server/project.ts | 48 +++---- src/server/scriptInfo.ts | 4 +- src/server/session.ts | 10 +- src/server/typingsCache.ts | 4 +- src/server/utilities.ts | 2 +- src/services/codefixes/addMissingAsync.ts | 4 +- src/services/codefixes/addMissingAwait.ts | 6 +- .../codefixes/convertToAsyncFunction.ts | 14 +- src/services/codefixes/convertToEs6Module.ts | 6 +- .../codefixes/disableJsDiagnostics.ts | 2 +- src/services/codefixes/importFixes.ts | 2 +- src/services/completions.ts | 4 +- src/services/documentRegistry.ts | 4 +- src/services/findAllReferences.ts | 16 +-- src/services/importTracker.ts | 8 +- src/services/navigationBar.ts | 4 +- src/services/patternMatcher.ts | 8 +- src/services/refactorProvider.ts | 2 +- src/services/refactors/extractSymbol.ts | 16 +-- src/services/services.ts | 14 +- src/services/shims.ts | 4 +- src/services/types.ts | 12 +- src/services/utilities.ts | 4 +- src/shims/collectionShims.ts | 61 ++++++-- src/testRunner/parallel/worker.ts | 4 +- .../unittests/config/commandLineParsing.ts | 2 +- src/testRunner/unittests/createMapShim.ts | 2 +- src/testRunner/unittests/moduleResolution.ts | 4 +- src/testRunner/unittests/programApi.ts | 2 +- .../unittests/reuseProgramStructure.ts | 8 +- .../unittests/services/extract/helpers.ts | 2 +- src/testRunner/unittests/tsbuild/helpers.ts | 4 +- .../unittests/tsbuild/watchEnvironment.ts | 2 +- src/testRunner/unittests/tsc/helpers.ts | 4 +- .../tsserver/cachingFileSystemInformation.ts | 4 +- src/testRunner/unittests/tsserver/helpers.ts | 2 +- src/testRunner/unittests/tsserver/symLinks.ts | 2 +- .../unittests/tsserver/typingsInstaller.ts | 2 +- src/tsserver/server.ts | 2 +- src/typingsInstaller/nodeTypingsInstaller.ts | 4 +- src/typingsInstallerCore/typingsInstaller.ts | 10 +- .../reference/api/tsserverlibrary.d.ts | 46 ++---- tests/baselines/reference/api/typescript.d.ts | 38 ++--- 80 files changed, 598 insertions(+), 628 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 7549759138967..a95582b53859a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -15,7 +15,7 @@ namespace ts { referenced: boolean; } - export function getModuleInstanceState(node: ModuleDeclaration, visited?: Map): ModuleInstanceState { + export function getModuleInstanceState(node: ModuleDeclaration, visited?: Map): ModuleInstanceState { if (node.body && !node.body.parent) { // getModuleInstanceStateForAliasTarget needs to walk up the parent chain, so parent pointers must be set on this tree already setParent(node.body, node); @@ -35,7 +35,7 @@ namespace ts { return result; } - function getModuleInstanceStateWorker(node: Node, visited: Map): ModuleInstanceState { + function getModuleInstanceStateWorker(node: Node, visited: Map): ModuleInstanceState { // A module is uninstantiated if it contains only switch (node.kind) { // 1. interface declarations, type alias declarations @@ -107,7 +107,7 @@ namespace ts { return ModuleInstanceState.Instantiated; } - function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: Map) { + function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: Map) { const name = specifier.propertyName || specifier.name; let p: Node | undefined = specifier.parent; while (p) { diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 16707941c63ac..488c89bea24ef 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -24,11 +24,11 @@ namespace ts { /** * Cache of bind and check diagnostics for files with their Path being the key */ - semanticDiagnosticsPerFile?: ReadonlyMap | undefined; + semanticDiagnosticsPerFile?: ReadonlyMap | undefined; /** * The map has key by source file's path that has been changed */ - changedFilesSet?: ReadonlyMap; + changedFilesSet?: ReadonlyMap; /** * Set of affected files being iterated */ @@ -41,7 +41,7 @@ namespace ts { * Map of file signatures, with key being file path, calculated while getting current changed file's affected files * These will be committed whenever the iteration through affected files of current changed file is complete */ - currentAffectedFilesSignatures?: ReadonlyMap | undefined; + currentAffectedFilesSignatures?: ReadonlyMap | undefined; /** * Newly computed visible to outside referencedSet */ @@ -49,7 +49,7 @@ namespace ts { /** * True if the semantic diagnostics were copied from the old state */ - semanticDiagnosticsFromOldState?: Map; + semanticDiagnosticsFromOldState?: Map; /** * program corresponding to this state */ @@ -65,7 +65,7 @@ namespace ts { /** * Files pending to be emitted kind. */ - affectedFilesPendingEmitKind?: ReadonlyMap | undefined; + affectedFilesPendingEmitKind?: ReadonlyMap | undefined; /** * Current index to retrieve pending affected file */ @@ -89,11 +89,11 @@ namespace ts { /** * Cache of bind and check diagnostics for files with their Path being the key */ - semanticDiagnosticsPerFile: Map | undefined; + semanticDiagnosticsPerFile: Map | undefined; /** * The map has key by source file's path that has been changed */ - changedFilesSet: Map; + changedFilesSet: Map; /** * Set of affected files being iterated */ @@ -110,7 +110,7 @@ namespace ts { * Map of file signatures, with key being file path, calculated while getting current changed file's affected files * These will be committed whenever the iteration through affected files of current changed file is complete */ - currentAffectedFilesSignatures: Map | undefined; + currentAffectedFilesSignatures: Map | undefined; /** * Newly computed visible to outside referencedSet */ @@ -118,7 +118,7 @@ namespace ts { /** * Already seen affected files */ - seenAffectedFiles: Map | undefined; + seenAffectedFiles: Map | undefined; /** * whether this program has cleaned semantic diagnostics cache for lib files */ @@ -126,7 +126,7 @@ namespace ts { /** * True if the semantic diagnostics were copied from the old state */ - semanticDiagnosticsFromOldState?: Map; + semanticDiagnosticsFromOldState?: Map; /** * program corresponding to this state */ @@ -142,7 +142,7 @@ namespace ts { /** * Files pending to be emitted kind. */ - affectedFilesPendingEmitKind: Map | undefined; + affectedFilesPendingEmitKind: Map | undefined; /** * Current index to retrieve pending affected file */ @@ -154,16 +154,16 @@ namespace ts { /** * Already seen emitted files */ - seenEmittedFiles: Map | undefined; + seenEmittedFiles: Map | undefined; /** * true if program has been emitted */ programEmitComplete?: true; } - function hasSameKeys(map1: ReadonlyMap | undefined, map2: ReadonlyMap | undefined): boolean { + function hasSameKeys(map1: ReadonlyMap | undefined, map2: ReadonlyMap | undefined): boolean { // Has same size and every key is present in both maps - return map1 as ReadonlyMap === map2 || map1 !== undefined && map2 !== undefined && map1.size === map2.size && !forEachKey(map1, key => !map2.has(key)); + return map1 as ReadonlyMap === map2 || map1 !== undefined && map2 !== undefined && map1.size === map2.size && !forEachKey(map1, key => !map2.has(key)); } /** @@ -555,7 +555,7 @@ namespace ts { /** * Iterate on files referencing referencedPath */ - function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Map, fn: (state: BuilderProgramState, filePath: Path) => boolean) { + function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Map, fn: (state: BuilderProgramState, filePath: Path) => boolean) { return forEachEntry(state.referencedMap!, (referencesInFile, filePath) => referencesInFile.has(referencedPath) && forEachFileAndExportsOfFile(state, filePath as Path, seenFileAndExportsOfFile, fn) ); @@ -564,7 +564,7 @@ namespace ts { /** * fn on file and iterate on anything that exports this file */ - function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Map, fn: (state: BuilderProgramState, filePath: Path) => boolean): boolean { + function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Map, fn: (state: BuilderProgramState, filePath: Path) => boolean): boolean { if (!addToSeen(seenFileAndExportsOfFile, filePath)) { return false; } @@ -1142,7 +1142,7 @@ namespace ts { } } - function getMapOfReferencedSet(mapLike: MapLike | undefined, toPath: (path: string) => Path): ReadonlyMap | undefined { + function getMapOfReferencedSet(mapLike: MapLike | undefined, toPath: (path: string) => Path): ReadonlyMap | undefined { if (!mapLike) return undefined; const map = createMap(); // Copies keys/values from template. Note that for..in will not throw if diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index 315e67bdbb2d5..beea442101eab 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -15,42 +15,42 @@ namespace ts { /** * Information of the file eg. its version, signature etc */ - fileInfos: ReadonlyMap; + fileInfos: ReadonlyMap; /** * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled * Otherwise undefined * Thus non undefined value indicates, module emit */ - readonly referencedMap?: ReadonlyMap | undefined; + readonly referencedMap?: ReadonlyMap | undefined; /** * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled * Otherwise undefined */ - readonly exportedModulesMap?: ReadonlyMap | undefined; + readonly exportedModulesMap?: ReadonlyMap | undefined; } export interface BuilderState { /** * Information of the file eg. its version, signature etc */ - fileInfos: Map; + fileInfos: Map; /** * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled * Otherwise undefined * Thus non undefined value indicates, module emit */ - readonly referencedMap: ReadonlyMap | undefined; + readonly referencedMap: ReadonlyMap | undefined; /** * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled * Otherwise undefined */ - readonly exportedModulesMap: Map | undefined; + readonly exportedModulesMap: Map | undefined; /** * Map of files that have already called update signature. * That means hence forth these files are assumed to have * no change in their signature for this version of the program */ - hasCalledUpdateShapeSignature: Map; + hasCalledUpdateShapeSignature: Map; /** * Cache of all files excluding default library file for the current program */ @@ -73,7 +73,7 @@ namespace ts { /** * Referenced files with values for the keys as referenced file's path to be true */ - export type ReferencedSet = ReadonlyMap; + export type ReferencedSet = ReadonlyMap; /** * Compute the hash to store the shape of the file */ @@ -83,7 +83,7 @@ namespace ts { * Exported modules to from declaration emit being computed. * This can contain false in the affected file path to specify that there are no exported module(types from other modules) for this file */ - export type ComputingExportedModulesMap = Map; + export type ComputingExportedModulesMap = Map; /** * Get the referencedFile from the imported module symbol @@ -113,8 +113,8 @@ namespace ts { /** * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true */ - function getReferencedFiles(program: Program, sourceFile: SourceFile, getCanonicalFileName: GetCanonicalFileName): Map | undefined { - let referencedFiles: Map | undefined; + function getReferencedFiles(program: Program, sourceFile: SourceFile, getCanonicalFileName: GetCanonicalFileName): Map | undefined { + let referencedFiles: Map | undefined; // We need to use a set here since the code can contain the same import twice, // but that will only be one dependency. @@ -195,7 +195,7 @@ namespace ts { /** * Returns true if oldState is reusable, that is the emitKind = module/non module has not changed */ - export function canReuseOldState(newReferencedMap: ReadonlyMap | undefined, oldState: Readonly | undefined) { + export function canReuseOldState(newReferencedMap: ReadonlyMap | undefined, oldState: Readonly | undefined) { return oldState && !oldState.referencedMap === !newReferencedMap; } @@ -265,7 +265,7 @@ namespace ts { /** * Gets the files affected by the path from the program */ - export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: Map, exportedModulesMapCache?: ComputingExportedModulesMap): readonly SourceFile[] { + export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: Map, exportedModulesMapCache?: ComputingExportedModulesMap): readonly SourceFile[] { // Since the operation could be cancelled, the signatures are always stored in the cache // They will be committed once it is safe to use them // eg when calling this api from tsserver, if there is no cancellation of the operation @@ -292,7 +292,7 @@ namespace ts { * Updates the signatures from the cache into state's fileinfo signatures * This should be called whenever it is safe to commit the state of the builder */ - export function updateSignaturesFromCache(state: BuilderState, signatureCache: Map) { + export function updateSignaturesFromCache(state: BuilderState, signatureCache: Map) { signatureCache.forEach((signature, path) => updateSignatureOfFile(state, signature, path as Path)); } @@ -304,7 +304,7 @@ namespace ts { /** * Returns if the shape of the signature has changed since last emit */ - export function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ComputingExportedModulesMap) { + export function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ComputingExportedModulesMap) { Debug.assert(!!sourceFile); Debug.assert(!exportedModulesMapCache || !!state.exportedModulesMap, "Compute visible to outside map only if visibleToOutsideReferencedMap present in the state"); @@ -365,7 +365,7 @@ namespace ts { return; } - let exportedModules: Map | undefined; + let exportedModules: Map | undefined; exportedModulesFromDeclarationEmit.forEach(symbol => addExportedModule(getReferencedFileFromImportedModuleSymbol(symbol))); exportedModulesMapCache.set(sourceFile.resolvedPath, exportedModules || false); @@ -528,7 +528,7 @@ namespace ts { /** * When program emits modular code, gets the files affected by the sourceFile whose shape has changed */ - function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined, exportedModulesMapCache: ComputingExportedModulesMap | undefined) { + function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined, exportedModulesMapCache: ComputingExportedModulesMap | undefined) { if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) { return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape); } @@ -563,7 +563,7 @@ namespace ts { } } - export function cloneMapOrUndefined(map: ReadonlyMap | undefined) { + export function cloneMapOrUndefined(map: ReadonlyMap | undefined) { return map ? cloneMap(map) : undefined; } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2134e7f9b2992..ea212dea29e7a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -134,7 +134,7 @@ namespace ts { EmptyObjectFacts = All, } - const typeofEQFacts: ReadonlyMap = createMapFromTemplate({ + const typeofEQFacts: ReadonlyMap = createMapFromTemplate({ string: TypeFacts.TypeofEQString, number: TypeFacts.TypeofEQNumber, bigint: TypeFacts.TypeofEQBigInt, @@ -145,7 +145,7 @@ namespace ts { function: TypeFacts.TypeofEQFunction }); - const typeofNEFacts: ReadonlyMap = createMapFromTemplate({ + const typeofNEFacts: ReadonlyMap = createMapFromTemplate({ string: TypeFacts.TypeofNEString, number: TypeFacts.TypeofNENumber, bigint: TypeFacts.TypeofNEBigInt, @@ -277,7 +277,7 @@ namespace ts { } export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker { - const getPackagesSet: () => Map = memoize(() => { + const getPackagesSet: () => Map = memoize(() => { const set = createMap(); host.getSourceFiles().forEach(sf => { if (!sf.resolvedModules) return; @@ -825,10 +825,10 @@ namespace ts { readonly firstFile: SourceFile; readonly secondFile: SourceFile; /** Key is symbol name. */ - readonly conflictingSymbols: Map; + readonly conflictingSymbols: Map; } /** Key is "/path/to/a.ts|/path/to/b.ts". */ - let amalgamatedDuplicates: Map | undefined; + let amalgamatedDuplicates: Map | undefined; const reverseMappedCache = createMap(); let inInferTypeForHomomorphicMappedType = false; let ambientModulesCache: Symbol[] | undefined; @@ -838,7 +838,7 @@ namespace ts { * This is only used if there is no exact match. */ let patternAmbientModules: PatternAmbientModule[]; - let patternAmbientModuleAugmentations: Map | undefined; + let patternAmbientModuleAugmentations: Map | undefined; let globalObjectType: ObjectType; let globalFunctionType: ObjectType; @@ -906,7 +906,7 @@ namespace ts { const mergedSymbols: Symbol[] = []; const symbolLinks: SymbolLinks[] = []; const nodeLinks: NodeLinks[] = []; - const flowLoopCaches: Map[] = []; + const flowLoopCaches: Map[] = []; const flowLoopNodes: FlowNode[] = []; const flowLoopKeys: string[] = []; const flowLoopTypes: Type[][] = []; @@ -922,7 +922,7 @@ namespace ts { const diagnostics = createDiagnosticCollection(); const suggestionDiagnostics = createDiagnosticCollection(); - const typeofTypesByName: ReadonlyMap = createMapFromTemplate({ + const typeofTypesByName: ReadonlyMap = createMapFromTemplate({ string: stringType, number: numberType, bigint: bigintType, @@ -3752,7 +3752,7 @@ namespace ts { return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace; } - function getAccessibleSymbolChain(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean, visitedSymbolTablesMap: Map = createMap()): Symbol[] | undefined { + function getAccessibleSymbolChain(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean, visitedSymbolTablesMap: Map = createMap()): Symbol[] | undefined { if (!(symbol && !isPropertyOrMethodDeclarationSymbol(symbol))) { return undefined; } @@ -5876,8 +5876,8 @@ namespace ts { // we're trying to emit from later on) const enclosingDeclaration = context.enclosingDeclaration!; let results: Statement[] = []; - const visitedSymbols: Map = createMap(); - let deferredPrivates: Map | undefined; + const visitedSymbols: Map = createMap(); + let deferredPrivates: Map | undefined; const oldcontext = context; context = { ...oldcontext, @@ -7189,16 +7189,16 @@ namespace ts { // State encounteredError: boolean; - visitedTypes: Map | undefined; - symbolDepth: Map | undefined; + visitedTypes: Map | undefined; + symbolDepth: Map | undefined; inferTypeParameters: TypeParameter[] | undefined; approximateLength: number; truncating?: boolean; - typeParameterSymbolList?: Map; - typeParameterNames?: Map; - typeParameterNamesByText?: Map; - usedSymbolNames?: Map; - remappedSymbolNames?: Map; + typeParameterSymbolList?: Map; + typeParameterNames?: Map; + typeParameterNamesByText?: Map; + usedSymbolNames?: Map; + remappedSymbolNames?: Map; } function isDefaultBindingContext(location: Node) { @@ -7394,7 +7394,7 @@ namespace ts { exportSymbol = getTargetOfExportSpecifier(node.parent, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } let result: Node[] | undefined; - let visited: Map | undefined; + let visited: Map | undefined; if (exportSymbol) { visited = createMap(); visited.set("" + getSymbolId(exportSymbol), true); @@ -10817,7 +10817,7 @@ namespace ts { function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol | undefined { let singleProp: Symbol | undefined; - let propSet: Map | undefined; + let propSet: Map | undefined; let indexTypes: Type[] | undefined; const isUnion = containingType.flags & TypeFlags.Union; // Flags we want to propagate to the result if they exist in all source symbols @@ -12768,7 +12768,7 @@ namespace ts { return links.resolvedType; } - function addTypeToIntersection(typeSet: Map, includes: TypeFlags, type: Type) { + function addTypeToIntersection(typeSet: Map, includes: TypeFlags, type: Type) { const flags = type.flags; if (flags & TypeFlags.Intersection) { return addTypesToIntersection(typeSet, includes, (type).types); @@ -12798,7 +12798,7 @@ namespace ts { // Add the given types to the given type set. Order is preserved, freshness is removed from literal // types, duplicates are removed, and nested types of the given kind are flattened into the set. - function addTypesToIntersection(typeSet: Map, includes: TypeFlags, types: readonly Type[]) { + function addTypesToIntersection(typeSet: Map, includes: TypeFlags, types: readonly Type[]) { for (const type of types) { includes = addTypeToIntersection(typeSet, includes, getRegularTypeOfLiteralType(type)); } @@ -12915,7 +12915,7 @@ namespace ts { // Also, unlike union types, the order of the constituent types is preserved in order that overload resolution // for intersections of types with signatures can be deterministic. function getIntersectionType(types: readonly Type[], aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { - const typeMembershipMap: Map = createMap(); + const typeMembershipMap: Map = createMap(); const includes = addTypesToIntersection(typeMembershipMap, 0, types); const typeSet: Type[] = arrayFrom(typeMembershipMap.values()); // An intersection type is considered empty if it contains @@ -14895,7 +14895,7 @@ namespace ts { function checkTypeRelatedToAndOptionallyElaborate( source: Type, target: Type, - relation: Map, + relation: Map, errorNode: Node | undefined, expr: Expression | undefined, headMessage: DiagnosticMessage | undefined, @@ -14917,7 +14917,7 @@ namespace ts { node: Expression | undefined, source: Type, target: Type, - relation: Map, + relation: Map, headMessage: DiagnosticMessage | undefined, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined @@ -14954,7 +14954,7 @@ namespace ts { node: Expression, source: Type, target: Type, - relation: Map, + relation: Map, headMessage: DiagnosticMessage | undefined, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined @@ -14983,7 +14983,7 @@ namespace ts { node: ArrowFunction, source: Type, target: Type, - relation: Map, + relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined ): boolean { @@ -15070,7 +15070,7 @@ namespace ts { iterator: ElaborationIterator, source: Type, target: Type, - relation: Map, + relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined ) { @@ -15184,7 +15184,7 @@ namespace ts { node: JsxAttributes, source: Type, target: Type, - relation: Map, + relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined ) { @@ -15285,7 +15285,7 @@ namespace ts { node: ArrayLiteralExpression, source: Type, target: Type, - relation: Map, + relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined ) { @@ -15338,7 +15338,7 @@ namespace ts { node: ObjectLiteralExpression, source: Type, target: Type, - relation: Map, + relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined ) { @@ -15629,7 +15629,7 @@ namespace ts { return true; } - function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { + function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { const s = source.flags; const t = target.flags; if (t & TypeFlags.AnyOrUnknown || s & TypeFlags.Never || source === wildcardType) return true; @@ -15666,7 +15666,7 @@ namespace ts { return false; } - function isTypeRelatedTo(source: Type, target: Type, relation: Map) { + function isTypeRelatedTo(source: Type, target: Type, relation: Map) { if (isFreshLiteralType(source)) { source = (source).regularType; } @@ -15729,7 +15729,7 @@ namespace ts { function checkTypeRelatedTo( source: Type, target: Type, - relation: Map, + relation: Map, errorNode: Node | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined, @@ -17782,7 +17782,7 @@ namespace ts { * To improve caching, the relation key for two generic types uses the target's id plus ids of the type parameters. * For other cases, the types ids are used. */ - function getRelationKey(source: Type, target: Type, intersectionState: IntersectionState, relation: Map) { + function getRelationKey(source: Type, target: Type, intersectionState: IntersectionState, relation: Map) { if (relation === identityRelation && source.id > target.id) { const temp = source; source = target; @@ -18919,7 +18919,7 @@ namespace ts { function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) { let symbolOrTypeStack: (Symbol | Type)[]; - let visited: Map; + let visited: Map; let bivariant = false; let propagationType: Type; let inferencePriority = InferencePriority.MaxValue; @@ -25568,7 +25568,7 @@ namespace ts { function checkApplicableSignatureForJsxOpeningLikeElement( node: JsxOpeningLikeElement, signature: Signature, - relation: Map, + relation: Map, checkMode: CheckMode, reportErrors: boolean, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, @@ -25668,7 +25668,7 @@ namespace ts { node: CallLikeExpression, args: readonly Expression[], signature: Signature, - relation: Map, + relation: Map, checkMode: CheckMode, reportErrors: boolean, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, @@ -26184,7 +26184,7 @@ namespace ts { return getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray); - function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { + function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { candidatesForArgumentError = undefined; candidateForArgumentArityError = undefined; candidateForTypeArgumentError = undefined; @@ -31925,7 +31925,7 @@ namespace ts { return !(getMergedSymbol(typeParameter.symbol).isReferenced! & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderscore(typeParameter.name); } - function addToGroup(map: Map<[K, V[]]>, key: K, value: V, getKey: (key: K) => number | string): void { + function addToGroup(map: Map, key: K, value: V, getKey: (key: K) => number | string): void { const keyString = String(getKey(key)); const group = map.get(keyString); if (group) { @@ -36817,7 +36817,7 @@ namespace ts { // this variable and functions that use it are deliberately moved here from the outer scope // to avoid scope pollution const resolvedTypeReferenceDirectives = host.getResolvedTypeReferenceDirectives(); - let fileToDirective: Map; + let fileToDirective: Map; if (resolvedTypeReferenceDirectives) { // populate reverse mapping: file path -> type reference directive that was resolved to this file fileToDirective = createMap(); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index a43e6244fcbf0..c6d310f3c68bb 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1090,8 +1090,8 @@ namespace ts { /* @internal */ export interface OptionsNameMap { - optionsNameMap: Map; - shortOptionNames: Map; + optionsNameMap: Map; + shortOptionNames: Map; } /*@internal*/ @@ -1469,7 +1469,7 @@ namespace ts { configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost, - extendedConfigCache?: Map, + extendedConfigCache?: Map, watchOptionsToExtend?: WatchOptions, extraFileExtensions?: readonly FileExtensionInfo[], ): ParsedCommandLine | undefined { @@ -1562,15 +1562,15 @@ namespace ts { optionTypeMismatchDiagnostic: Diagnostics.Watch_option_0_requires_a_value_of_type_1 }; - let commandLineCompilerOptionsMapCache: Map; + let commandLineCompilerOptionsMapCache: Map; function getCommandLineCompilerOptionsMap() { return commandLineCompilerOptionsMapCache || (commandLineCompilerOptionsMapCache = commandLineOptionsToMap(optionDeclarations)); } - let commandLineWatchOptionsMapCache: Map; + let commandLineWatchOptionsMapCache: Map; function getCommandLineWatchOptionsMap() { return commandLineWatchOptionsMapCache || (commandLineWatchOptionsMapCache = commandLineOptionsToMap(optionsForWatch)); } - let commandLineTypeAcquisitionMapCache: Map; + let commandLineTypeAcquisitionMapCache: Map; function getCommandLineTypeAcquisitionMap() { return commandLineTypeAcquisitionMapCache || (commandLineTypeAcquisitionMapCache = commandLineOptionsToMap(typeAcquisitionDeclarations)); } @@ -1703,13 +1703,13 @@ namespace ts { return convertPropertyValueToJson(sourceFile.statements[0].expression, knownRootOptions); - function isRootOptionMap(knownOptions: Map | undefined) { + function isRootOptionMap(knownOptions: Map | undefined) { return knownRootOptions && (knownRootOptions as TsConfigOnlyOption).elementOptions === knownOptions; } function convertObjectLiteralExpressionToJson( node: ObjectLiteralExpression, - knownOptions: Map | undefined, + knownOptions: Map | undefined, extraKeyDiagnostics: DidYouMeanOptionsDiagnostics | undefined, parentOption: string | undefined ): any { @@ -1964,7 +1964,7 @@ namespace ts { return config; } - function optionMapToObject(optionMap: Map): object { + function optionMapToObject(optionMap: Map): object { return { ...arrayFrom(optionMap.entries()).reduce((prev, cur) => ({ ...prev, [cur[0]]: cur[1] }), {}), }; @@ -1994,7 +1994,7 @@ namespace ts { return _ => true; } - function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined { + function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined { if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean" || optionDefinition.type === "object") { // this is of a type CommandLineOptionOfPrimitiveType return undefined; @@ -2007,7 +2007,7 @@ namespace ts { } } - function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map): string | undefined { + function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map): string | undefined { // There is a typeMap associated with this command-line option so use it to map value back to its name return forEachEntry(customTypeMap, (mapValue, key) => { if (mapValue === value) { @@ -2019,7 +2019,7 @@ namespace ts { function serializeCompilerOptions( options: CompilerOptions, pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean } - ): Map { + ): Map { return serializeOptionBaseObject(options, getOptionsNameMap(), pathOptions); } @@ -2031,7 +2031,7 @@ namespace ts { options: OptionsBase, { optionsNameMap }: OptionsNameMap, pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean } - ): Map { + ): Map { const result = createMap(); const getCanonicalFileName = pathOptions && createGetCanonicalFileName(pathOptions.useCaseSensitiveFileNames); @@ -2220,7 +2220,7 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map, existingWatchOptions?: WatchOptions): ParsedCommandLine { + export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map, existingWatchOptions?: WatchOptions): ParsedCommandLine { return parseJsonConfigFileContentWorker(json, /*sourceFile*/ undefined, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache); } @@ -2231,7 +2231,7 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map, existingWatchOptions?: WatchOptions): ParsedCommandLine { + export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map, existingWatchOptions?: WatchOptions): ParsedCommandLine { return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache); } @@ -2271,7 +2271,7 @@ namespace ts { configFileName?: string, resolutionStack: Path[] = [], extraFileExtensions: readonly FileExtensionInfo[] = [], - extendedConfigCache?: Map + extendedConfigCache?: Map ): ParsedCommandLine { Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined)); const errors: Diagnostic[] = []; @@ -2456,7 +2456,7 @@ namespace ts { configFileName: string | undefined, resolutionStack: string[], errors: Push, - extendedConfigCache?: Map + extendedConfigCache?: Map ): ParsedTsconfig { basePath = normalizeSlashes(basePath); const resolvedPath = getNormalizedAbsolutePath(configFileName || "", basePath); @@ -2645,7 +2645,7 @@ namespace ts { basePath: string, resolutionStack: string[], errors: Push, - extendedConfigCache?: Map + extendedConfigCache?: Map ): ParsedTsconfig | undefined { const path = host.useCaseSensitiveFileNames ? extendedConfigPath : toFileNameLowerCase(extendedConfigPath); let value: ExtendedConfigCacheEntry | undefined; @@ -2750,11 +2750,11 @@ namespace ts { return convertOptionsFromJson(getCommandLineWatchOptionsMap(), jsonOptions, basePath, /*defaultOptions*/ undefined, watchOptionsDidYouMeanDiagnostics, errors); } - function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, + function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, defaultOptions: undefined, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push): WatchOptions | undefined; - function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, + function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, defaultOptions: CompilerOptions | TypeAcquisition, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push): CompilerOptions | TypeAcquisition; - function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, + function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, defaultOptions: CompilerOptions | TypeAcquisition | WatchOptions | undefined, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push) { if (!jsonOptions) { @@ -3174,7 +3174,7 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: readonly string[], keyMapper: (value: string) => string) { + function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: readonly string[], keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority, extensions); for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) { @@ -3196,7 +3196,7 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: readonly string[], keyMapper: (value: string) => string) { + function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: readonly string[], keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority, extensions); for (let i = nextExtensionPriority; i < extensions.length; i++) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 092d95df86276..27976899cfe8c 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1,30 +1,54 @@ - /* @internal */ namespace ts { + export const Map: MapConstructor = tryGetNativeMap() || (() => { + // NOTE: ts.createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. + if (typeof createMapShim === "function") { + return createMapShim(); + } + throw new Error("TypeScript requires an environment that provides a compatible native Map implementation."); + })(); + + export const Set: SetConstructor = tryGetNativeSet() || (() => { + // NOTE: ts.createSetShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. + if (typeof createSetShim === "function") { + return createSetShim(); + } + throw new Error("TypeScript requires an environment that provides a compatible native Set implementation."); + })(); + + export const WeakMap: WeakMapConstructor = tryGetNativeWeakMap() || (() => { + // NOTE: ts.createWeakMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. + if (typeof createWeakMapShim === "function") { + return createWeakMapShim(); + } + throw new Error("TypeScript requires an environment that provides a compatible native WeakMap implementation."); + })(); + + export const WeakSet: WeakSetConstructor = tryGetNativeWeakSet() || (() => { + // NOTE: ts.createWeakSetShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. + if (typeof createWeakSetShim === "function") { + return createWeakSetShim(); + } + throw new Error("TypeScript requires an environment that provides a compatible native WeakSet implementation."); + })(); export const emptyArray: never[] = [] as never[]; /** Create a new map. */ - export function createMap(): Map; - export function createMap(): ESMap; - export function createMap(): ESMap { + export function createMap(): Map; + export function createMap(): Map; + export function createMap(): Map { return new Map(); } /** Create a new map from an array of entries. */ - export function createMapFromEntries(entries: readonly [string, T][]): Map; - export function createMapFromEntries(entries: readonly [K, V][]): ESMap; - export function createMapFromEntries(entries: readonly [K, V][]): ESMap { - const map = createMap(); - for (const [key, value] of entries) { - map.set(key, value); - } - return map; + export function createMapFromEntries(entries: readonly [K, V][]): Map { + return new Map(entries); } /** Create a new map from a template object is provided, the map will copy entries from it. */ - export function createMapFromTemplate(template: MapLike): Map { - const map: Map = new Map(); + export function createMapFromTemplate(template: MapLike): Map { + const map: Map = new Map(); // Copies keys/values from template. Note that for..in will not throw if // template is undefined, and instead will just exit the loop. @@ -42,11 +66,7 @@ namespace ts { } export function createSetFromValues(values: readonly T[]): Set { - const set = createSet(); - for (const value of values) { - set.add(value); - } - return set; + return new Set(values); } export function length(array: readonly any[] | undefined): number { @@ -136,9 +156,9 @@ namespace ts { }; } - export function zipToMap(keys: readonly string[], values: readonly T[]): Map { + export function zipToMap(keys: readonly K[], values: readonly V[]): Map { Debug.assert(keys.length === values.length); - const map = createMap(); + const map = createMap(); for (let i = 0; i < keys.length; ++i) { map.set(keys[i], values[i]); } @@ -530,14 +550,24 @@ namespace ts { }; } - export function mapDefinedMap(map: ReadonlyMap, mapValue: (value: T, key: string) => U | undefined, mapKey: (key: string) => string = identity): Map { - const result = createMap(); + export function mapDefinedEntries(map: ReadonlyMap, f: (key: K1, value: V1) => readonly [K2, V2] | undefined): Map; + export function mapDefinedEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2 | undefined, V2 | undefined] | undefined): Map | undefined; + export function mapDefinedEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2 | undefined, V2 | undefined] | undefined): Map | undefined { + if (!map) { + return undefined; + } + + const result = createMap(); map.forEach((value, key) => { - const mapped = mapValue(value, key); - if (mapped !== undefined) { - result.set(mapKey(key), mapped); + const entry = f(key, value); + if (entry !== undefined) { + const [newKey, newValue] = entry; + if (newKey !== undefined && newValue !== undefined) { + result.set(newKey, newValue); + } } }); + return result; } @@ -603,20 +633,21 @@ namespace ts { return result; } - export function mapEntries(map: ReadonlyMap, f: (key: string, value: T) => [string, U]): Map; - export function mapEntries(map: ReadonlyMap | undefined, f: (key: string, value: T) => [string, U]): Map | undefined; - export function mapEntries(map: ReadonlyMap | undefined, f: (key: string, value: T) => [string, U]): Map | undefined { + export function mapEntries(map: ReadonlyMap, f: (key: K1, value: V1) => readonly [K2, V2]): Map; + export function mapEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2, V2]): Map | undefined; + export function mapEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2, V2]): Map | undefined { if (!map) { return undefined; } - const result = createMap(); + const result = createMap(); map.forEach((value, key) => { const [newKey, newValue] = f(key, value); result.set(newKey, newValue); }); return result; } + export function some(array: readonly T[] | undefined): array is readonly T[]; export function some(array: readonly T[] | undefined, predicate: (value: T) => boolean): boolean; export function some(array: readonly T[] | undefined, predicate?: (value: T) => boolean): boolean { @@ -1276,10 +1307,12 @@ namespace ts { * the same key with the given 'makeKey' function, then the element with the higher * index in the array will be the one associated with the produced key. */ - export function arrayToMap(array: readonly T[], makeKey: (value: T) => string | undefined): Map; - export function arrayToMap(array: readonly T[], makeKey: (value: T) => string | undefined, makeValue: (value: T) => U): Map; - export function arrayToMap(array: readonly T[], makeKey: (value: T) => string | undefined, makeValue: (value: T) => T | U = identity): Map { - const result = createMap(); + export function arrayToMap(array: readonly T[], makeKey: (value: T) => string | undefined): Map; + export function arrayToMap(array: readonly T[], makeKey: (value: T) => string | undefined, makeValue: (value: T) => U): Map; + export function arrayToMap(array: readonly V[], makeKey: (value: V) => K | undefined): Map; + export function arrayToMap(array: readonly V1[], makeKey: (value: V1) => K | undefined, makeValue: (value: V1) => V2): Map; + export function arrayToMap(array: readonly V1[], makeKey: (value: V1) => K | undefined, makeValue: (value: V1) => V1 | V2 = identity): Map { + const result = createMap(); for (const value of array) { const key = makeKey(value); if (key !== undefined) result.set(key, makeValue(value)); @@ -1297,10 +1330,10 @@ namespace ts { return result; } - export function arrayToMultiMap(values: readonly T[], makeKey: (value: T) => string): MultiMap; - export function arrayToMultiMap(values: readonly T[], makeKey: (value: T) => string, makeValue: (value: T) => U): MultiMap; - export function arrayToMultiMap(values: readonly T[], makeKey: (value: T) => string, makeValue: (value: T) => T | U = identity): MultiMap { - const result = createMultiMap(); + export function arrayToMultiMap(values: readonly V[], makeKey: (value: V) => K): MultiMap; + export function arrayToMultiMap(values: readonly V[], makeKey: (value: V) => K, makeValue: (value: V) => U): MultiMap; + export function arrayToMultiMap(values: readonly V[], makeKey: (value: V) => K, makeValue: (value: V) => V | U = identity): MultiMap { + const result = createMultiMap(); for (const value of values) { result.add(makeKey(value), makeValue(value)); } @@ -1357,35 +1390,29 @@ namespace ts { return fn ? fn.bind(obj) : undefined; } - export function mapMap(map: Map, f: (t: T, key: string) => [string, U]): Map; - export function mapMap(map: UnderscoreEscapedMap, f: (t: T, key: __String) => [string, U]): Map; - export function mapMap(map: Map | UnderscoreEscapedMap, f: ((t: T, key: string) => [string, U]) | ((t: T, key: __String) => [string, U])): Map { - const result = createMap(); - map.forEach((t: T, key: string & __String) => result.set(...(f(t, key)))); - return result; - } - - export interface MultiMap extends Map { + export interface MultiMap extends Map { /** * Adds the value to an array of values associated with the key, and returns the array. * Creates the array if it does not already exist. */ - add(key: string, value: T): T[]; + add(key: K, value: V): V[]; /** * Removes a value from an array of values associated with the key. * Does not preserve the order of those values. * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. */ - remove(key: string, value: T): void; + remove(key: K, value: V): void; } - export function createMultiMap(): MultiMap { - const map = createMap() as MultiMap; + export function createMultiMap(): MultiMap; + export function createMultiMap(): MultiMap; + export function createMultiMap(): MultiMap { + const map = createMap() as MultiMap; map.add = multiMapAdd; map.remove = multiMapRemove; return map; } - function multiMapAdd(this: MultiMap, key: string, value: T) { + function multiMapAdd(this: MultiMap, key: K, value: V) { let values = this.get(key); if (values) { values.push(value); @@ -1395,7 +1422,7 @@ namespace ts { } return values; } - function multiMapRemove(this: MultiMap, key: string, value: T) { + function multiMapRemove(this: MultiMap, key: K, value: V) { const values = this.get(key); if (values) { unorderedRemoveItem(values, value); @@ -1420,7 +1447,7 @@ namespace ts { } export function createUnderscoreEscapedMultiMap(): UnderscoreEscapedMultiMap { - return createMultiMap() as UnderscoreEscapedMultiMap; + return createMultiMap() as UnderscoreEscapedMultiMap; } /** diff --git a/src/compiler/corePublic.ts b/src/compiler/corePublic.ts index 3d0b77fe2dc30..a9869a82e7693 100644 --- a/src/compiler/corePublic.ts +++ b/src/compiler/corePublic.ts @@ -23,35 +23,27 @@ namespace ts { } /** ES6 Map interface, only read methods included. */ - export interface ReadonlyESMap { + export interface ReadonlyMap { + readonly size: number; get(key: K): V | undefined; has(key: K): boolean; - forEach(action: (value: V, key: K) => void): void; - readonly size: number; keys(): Iterator; values(): Iterator; entries(): Iterator<[K, V]>; + forEach(action: (value: V, key: K) => void): void; } /** ES6 Map interface. */ - export interface ESMap extends ReadonlyESMap { + export interface Map extends ReadonlyMap { set(key: K, value: V): this; delete(key: K): boolean; clear(): void; } - /** ES6 Map interface, only read methods included. */ - export interface ReadonlyMap extends ReadonlyESMap { - } - - /** ES6 Map interface. */ - export interface Map extends ESMap, ReadonlyMap { - } - /* @internal */ export interface MapConstructor { // eslint-disable-next-line @typescript-eslint/prefer-function-type - new (): ESMap; + new (iterable?: readonly (readonly [K, V])[] | ReadonlyMap): Map; } export interface ReadonlySet { @@ -72,7 +64,7 @@ namespace ts { /* @internal */ export interface SetConstructor { // eslint-disable-next-line @typescript-eslint/prefer-function-type - new (): Set; + new (iterable?: readonly T[] | ReadonlySet): Set; } export interface WeakMap { @@ -133,13 +125,13 @@ namespace ts { export function tryGetNativeMap(): MapConstructor | undefined { // Internet Explorer's Map doesn't support iteration, so don't use it. // eslint-disable-next-line no-in-operator - return typeof Map !== "undefined" && "entries" in Map.prototype ? Map : undefined; + return typeof Map !== "undefined" && "entries" in Map.prototype && new Map([[0, 0]]).size === 1 ? Map : undefined; } export function tryGetNativeSet(): SetConstructor | undefined { // Internet Explorer's Set doesn't support iteration, so don't use it. // eslint-disable-next-line no-in-operator - return typeof Set !== "undefined" && "entries" in Set.prototype ? Set : undefined; + return typeof Set !== "undefined" && "entries" in Set.prototype && new Set([0]).size === 1 ? Set : undefined; } export function tryGetNativeWeakMap(): WeakMapConstructor | undefined { @@ -182,36 +174,4 @@ namespace ts { export function tryGetNativeWeakSet() { return NativeCollections.tryGetNativeWeakSet(); } - - export const Map: MapConstructor = tryGetNativeMap() || (() => { - // NOTE: ts.createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. - if (typeof createMapShim === "function") { - return createMapShim(); - } - throw new Error("TypeScript requires an environment that provides a compatible native Map implementation."); - })(); - - export const Set: SetConstructor = tryGetNativeSet() || (() => { - // NOTE: ts.createSetShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. - if (typeof createSetShim === "function") { - return createSetShim(); - } - throw new Error("TypeScript requires an environment that provides a compatible native Set implementation."); - })(); - - export const WeakMap: WeakMapConstructor = tryGetNativeWeakMap() || (() => { - // NOTE: ts.createWeakMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. - if (typeof createWeakMapShim === "function") { - return createWeakMapShim(); - } - throw new Error("TypeScript requires an environment that provides a compatible native WeakMap implementation."); - })(); - - export const WeakSet: WeakSetConstructor = tryGetNativeWeakSet() || (() => { - // NOTE: ts.createWeakSetShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. - if (typeof createWeakSetShim === "function") { - return createWeakSetShim(); - } - throw new Error("TypeScript requires an environment that provides a compatible native WeakSet implementation."); - })(); } \ No newline at end of file diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c9829f914fbc2..1243440e45810 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -839,11 +839,11 @@ namespace ts { let currentSourceFile: SourceFile | undefined; let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes. let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables. - let generatedNames: Map; // Set of names generated by the NameGenerator. + let generatedNames: Map; // Set of names generated by the NameGenerator. let tempFlagsStack: TempFlags[]; // Stack of enclosing name generation scopes. let tempFlags: TempFlags; // TempFlags for the current name generation scope. - let reservedNamesStack: Map[]; // Stack of TempFlags reserved in enclosing name generation scopes. - let reservedNames: Map; // TempFlags to reserve in nested name generation scopes. + let reservedNamesStack: Map[]; // Stack of TempFlags reserved in enclosing name generation scopes. + let reservedNames: Map; // TempFlags to reserve in nested name generation scopes. let preserveSourceNewlines = printerOptions.preserveSourceNewlines; // Can be overridden inside nodes with the `IgnoreSourceNewlines` emit flag. let writer: EmitTextWriter; @@ -3718,7 +3718,7 @@ namespace ts { * Emits any prologue directives at the start of a Statement list, returning the * number of prologue directives written to the output. */ - function emitPrologueDirectives(statements: readonly Node[], sourceFile?: SourceFile, seenPrologueDirectives?: Map, recordBundleFileSection?: true): number { + function emitPrologueDirectives(statements: readonly Node[], sourceFile?: SourceFile, seenPrologueDirectives?: Map, recordBundleFileSection?: true): number { let needsToSetSourceFile = !!sourceFile; for (let i = 0; i < statements.length; i++) { const statement = statements[i]; @@ -3747,7 +3747,7 @@ namespace ts { return statements.length; } - function emitUnparsedPrologues(prologues: readonly UnparsedPrologue[], seenPrologueDirectives: Map) { + function emitUnparsedPrologues(prologues: readonly UnparsedPrologue[], seenPrologueDirectives: Map) { for (const prologue of prologues) { if (!seenPrologueDirectives.has(prologue.data)) { writeLine(); diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index 9a76e759a2671..e59c06f151647 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -841,7 +841,7 @@ namespace ts { };` }; - let allUnscopedEmitHelpers: ReadonlyMap | undefined; + let allUnscopedEmitHelpers: ReadonlyMap | undefined; export function getAllUnscopedEmitHelpers() { return allUnscopedEmitHelpers || (allUnscopedEmitHelpers = arrayToMap([ diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 82e39ecfad460..61f6ccd8332b7 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -442,8 +442,8 @@ namespace ts { * This assumes that any module id will have the same resolution for sibling files located in the same folder. */ export interface ModuleResolutionCache extends NonRelativeModuleNameResolutionCache { - getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): Map; - /*@internal*/ directoryToModuleNameMap: CacheWithRedirects>; + getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): Map; + /*@internal*/ directoryToModuleNameMap: CacheWithRedirects>; } /** @@ -472,18 +472,18 @@ namespace ts { /*@internal*/ export interface CacheWithRedirects { - ownMap: Map; - redirectsMap: Map>; - getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map; + ownMap: Map; + redirectsMap: Map>; + getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map; clear(): void; setOwnOptions(newOptions: CompilerOptions): void; - setOwnMap(newOwnMap: Map): void; + setOwnMap(newOwnMap: Map): void; } /*@internal*/ export function createCacheWithRedirects(options?: CompilerOptions): CacheWithRedirects { - let ownMap: Map = createMap(); - const redirectsMap: Map> = createMap(); + let ownMap: Map = createMap(); + const redirectsMap: Map> = createMap(); return { ownMap, redirectsMap, @@ -497,7 +497,7 @@ namespace ts { options = newOptions; } - function setOwnMap(newOwnMap: Map) { + function setOwnMap(newOwnMap: Map) { ownMap = newOwnMap; } @@ -523,7 +523,7 @@ namespace ts { /*@internal*/ export function createModuleResolutionCacheWithMaps( - directoryToModuleNameMap: CacheWithRedirects>, + directoryToModuleNameMap: CacheWithRedirects>, moduleNameToDirectoryMap: CacheWithRedirects, currentDirectory: string, getCanonicalFileName: GetCanonicalFileName): ModuleResolutionCache { @@ -532,7 +532,7 @@ namespace ts { function getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference) { const path = toPath(directoryName, currentDirectory, getCanonicalFileName); - return getOrCreateCache>(directoryToModuleNameMap, redirectedReference, path, createMap); + return getOrCreateCache>(directoryToModuleNameMap, redirectedReference, path, createMap); } function getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index df8c34e12f880..ecff0dfa88bd1 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -749,13 +749,13 @@ namespace ts { let currentToken: SyntaxKind; let nodeCount: number; - let identifiers: Map; - let privateIdentifiers: Map; + let identifiers: Map; + let privateIdentifiers: Map; let identifierCount: number; let parsingContext: ParsingContext; - let notParenthesizedArrow: Map | undefined; + let notParenthesizedArrow: Map | undefined; // Flags that dictate what parsing context we're in. For example: // Whether or not we are in strict parsing mode. All that changes in strict parsing mode is diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 4a7f5c99278b2..3da794c638b16 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -8,9 +8,9 @@ namespace ts.performance { let enabled = false; let profilerStart = 0; - let counts: Map; - let marks: Map; - let measures: Map; + let counts: Map; + let marks: Map; + let measures: Map; export interface Timer { enter(): void; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 63873b9ac5d95..66741c6079e9b 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -126,7 +126,7 @@ namespace ts { } } - let outputFingerprints: Map; + let outputFingerprints: Map; function writeFileWorker(fileName: string, data: string, writeByteOrderMark: boolean) { if (!isWatchSet(options) || !system.createHash || !system.getModifiedTime) { system.writeFile(fileName, data, writeByteOrderMark); @@ -533,7 +533,7 @@ namespace ts { export const inferredTypesContainingFile = "__inferred type names__.ts"; interface DiagnosticCache { - perFile?: Map; + perFile?: Map; allDiagnostics?: readonly T[]; } @@ -702,14 +702,14 @@ namespace ts { let processingDefaultLibFiles: SourceFile[] | undefined; let processingOtherFiles: SourceFile[] | undefined; let files: SourceFile[]; - let symlinks: ReadonlyMap | undefined; + let symlinks: ReadonlyMap | undefined; let commonSourceDirectory: string; let diagnosticsProducingTypeChecker: TypeChecker; let noDiagnosticsTypeChecker: TypeChecker; let classifiableNames: UnderscoreEscapedMap; const ambientModuleNameToUnmodifiedFileName = createMap(); // Todo:: Use this to report why file was included in --extendedDiagnostics - let refFileMap: MultiMap | undefined; + let refFileMap: MultiMap | undefined; const cachedBindAndCheckDiagnosticsForFile: DiagnosticCache = {}; const cachedDeclarationDiagnosticsForFile: DiagnosticCache = {}; @@ -803,9 +803,9 @@ namespace ts { // A parallel array to projectReferences storing the results of reading in the referenced tsconfig files let resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined; - let projectReferenceRedirects: Map | undefined; - let mapFromFileToProjectReferenceRedirects: Map | undefined; - let mapFromToProjectReferenceRedirectSource: Map | undefined; + let projectReferenceRedirects: Map | undefined; + let mapFromFileToProjectReferenceRedirects: Map | undefined; + let mapFromToProjectReferenceRedirectSource: Map | undefined; const useSourceOfProjectReferenceRedirect = !!host.useSourceOfProjectReferenceRedirect?.() && !options.disableSourceOfProjectReferenceRedirect; @@ -2892,7 +2892,7 @@ namespace ts { function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean { let allFilesBelongToPath = true; const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory)); - let rootPaths: Map | undefined; + let rootPaths: Map | undefined; for (const sourceFile of sourceFiles) { if (!sourceFile.isDeclarationFile) { @@ -3214,7 +3214,7 @@ namespace ts { } // Verify that all the emit files are unique and don't overwrite input files - function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Map) { + function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Map) { if (emitFileName) { const emitFilePath = toPath(emitFileName); // Report error if the output overwrites input file @@ -3262,7 +3262,7 @@ namespace ts { return createFileDiagnostic(refFile, pos, end - pos, message, ...args); } - function addProgramDiagnosticAtRefPath(file: SourceFile, rootPaths: Map, message: DiagnosticMessage, ...args: (string | number | undefined)[]) { + function addProgramDiagnosticAtRefPath(file: SourceFile, rootPaths: Map, message: DiagnosticMessage, ...args: (string | number | undefined)[]) { const refPaths = refFileMap && refFileMap.get(file.path); const refPathToReportErrorOn = forEach(refPaths, refPath => rootPaths.has(refPath.file) ? refPath : undefined) || elementAt(refPaths, 0); @@ -3455,7 +3455,7 @@ namespace ts { return comparePaths(file1, file2, currentDirectory, !host.useCaseSensitiveFileNames()) === Comparison.EqualTo; } - function getProbableSymlinks(): ReadonlyMap { + function getProbableSymlinks(): ReadonlyMap { if (host.getSymlinks) { return host.getSymlinks(); } @@ -3481,9 +3481,9 @@ namespace ts { } function updateHostForUseSourceOfProjectReferenceRedirect(host: HostForUseSourceOfProjectReferenceRedirect) { - let mapOfDeclarationDirectories: Map | undefined; - let symlinkedDirectories: Map | undefined; - let symlinkedFiles: Map | undefined; + let mapOfDeclarationDirectories: Map | undefined; + let symlinkedDirectories: Map | undefined; + let symlinkedFiles: Map | undefined; const originalFileExists = host.compilerHost.fileExists; const originalDirectoryExists = host.compilerHost.directoryExists; diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 547a8f4c9d243..d97cc49c48b69 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -13,7 +13,7 @@ namespace ts { invalidateResolutionOfFile(filePath: Path): void; removeResolutionsOfFile(filePath: Path): void; removeResolutionsFromProjectReferenceRedirects(filePath: Path): void; - setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map): void; + setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map): void; createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution; hasChangedAutomaticTypeDirectiveNames(): boolean; @@ -143,8 +143,8 @@ namespace ts { export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string | undefined, logChangesWhenResolvingModule: boolean): ResolutionCache { let filesWithChangedSetOfUnresolvedImports: Path[] | undefined; - let filesWithInvalidatedResolutions: Map | undefined; - let filesWithInvalidatedNonRelativeUnresolvedImports: ReadonlyMap | undefined; + let filesWithInvalidatedResolutions: Map | undefined; + let filesWithInvalidatedNonRelativeUnresolvedImports: ReadonlyMap | undefined; const nonRelativeExternalModuleResolutions = createMultiMap(); const resolutionsWithFailedLookups: ResolutionWithFailedLookupLocations[] = []; @@ -161,8 +161,8 @@ namespace ts { // The resolvedModuleNames and resolvedTypeReferenceDirectives are the cache of resolutions per file. // The key in the map is source file's path. // The values are Map of resolutions with key being name lookedup. - const resolvedModuleNames = createMap>(); - const perDirectoryResolvedModuleNames: CacheWithRedirects> = createCacheWithRedirects(); + const resolvedModuleNames = createMap>(); + const perDirectoryResolvedModuleNames: CacheWithRedirects> = createCacheWithRedirects(); const nonRelativeModuleNameCache: CacheWithRedirects = createCacheWithRedirects(); const moduleResolutionCache = createModuleResolutionCacheWithMaps( perDirectoryResolvedModuleNames, @@ -171,8 +171,8 @@ namespace ts { resolutionHost.getCanonicalFileName ); - const resolvedTypeReferenceDirectives = createMap>(); - const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects> = createCacheWithRedirects(); + const resolvedTypeReferenceDirectives = createMap>(); + const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects> = createCacheWithRedirects(); /** * These are the extensions that failed lookup files will have by default, @@ -334,8 +334,8 @@ namespace ts { names: readonly string[]; containingFile: string; redirectedReference: ResolvedProjectReference | undefined; - cache: Map>; - perDirectoryCacheWithRedirects: CacheWithRedirects>; + cache: Map>; + perDirectoryCacheWithRedirects: CacheWithRedirects>; loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference) => T; getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName; shouldRetryResolution: (t: T) => boolean; @@ -684,7 +684,7 @@ namespace ts { } function removeResolutionsOfFileFromCache( - cache: Map>, + cache: Map>, filePath: Path, getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName, ) { @@ -741,7 +741,7 @@ namespace ts { } } - function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: ReadonlyMap) { + function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: ReadonlyMap) { Debug.assert(filesWithInvalidatedNonRelativeUnresolvedImports === filesMap || filesWithInvalidatedNonRelativeUnresolvedImports === undefined); filesWithInvalidatedNonRelativeUnresolvedImports = filesMap; } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index b578db78ae1f5..ab2a37032ced2 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -330,7 +330,7 @@ namespace ts { lookupInUnicodeMap(code, unicodeES3IdentifierPart); } - function makeReverseMap(source: Map): string[] { + function makeReverseMap(source: Map): string[] { const result: string[] = []; source.forEach((value, name) => { result[value] = name; diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 5fae21601a388..8cac9cde667cf 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -16,7 +16,7 @@ namespace ts { let sourcesContent: (string | null)[] | undefined; const names: string[] = []; - let nameToNameIndexMap: Map | undefined; + let nameToNameIndexMap: Map | undefined; let mappings = ""; // Last recorded and encoded mappings diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 725d2330d214a..0132af7223bda 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -538,7 +538,7 @@ namespace ts { }; } - type InvokeMap = Map; + type InvokeMap = Map; function invokeCallbacks(dirPath: Path, fileName: string): void; function invokeCallbacks(dirPath: Path, invokeMap: InvokeMap, fileNames: string[] | undefined): void; function invokeCallbacks(dirPath: Path, fileNameOrInvokeMap: string | InvokeMap, fileNames?: string[]) { diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index ba4c01beb8ba8..d9a7c8e1f6fc9 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -58,9 +58,9 @@ namespace ts { let needsScopeFixMarker = false; let resultHasScopeMarker = false; let enclosingDeclaration: Node; - let necessaryTypeReferences: Map | undefined; + let necessaryTypeReferences: Map | undefined; let lateMarkedStatements: LateVisibilityPaintedStatement[] | undefined; - let lateStatementReplacementMap: Map>; + let lateStatementReplacementMap: Map>; let suppressNewDiagnosticContexts: boolean; let exportedModulesFromDeclarationEmit: Symbol[] | undefined; @@ -81,8 +81,8 @@ namespace ts { let errorNameNode: DeclarationName | undefined; let currentSourceFile: SourceFile; - let refs: Map; - let libs: Map; + let refs: Map; + let libs: Map; let emittedImports: readonly AnyImportSyntax[] | undefined; // must be declared in container so it can be `undefined` while transformer's first pass const resolver = context.getEmitResolver(); const options = context.getCompilerOptions(); @@ -402,7 +402,7 @@ namespace ts { } } - function collectReferences(sourceFile: SourceFile | UnparsedSource, ret: Map) { + function collectReferences(sourceFile: SourceFile | UnparsedSource, ret: Map) { if (noResolve || (!isUnparsedSource(sourceFile) && isSourceFileJS(sourceFile))) return ret; forEach(sourceFile.referencedFiles, f => { const elem = host.getSourceFileFromReference(sourceFile, f); @@ -413,7 +413,7 @@ namespace ts { return ret; } - function collectLibs(sourceFile: SourceFile | UnparsedSource, ret: Map) { + function collectLibs(sourceFile: SourceFile | UnparsedSource, ret: Map) { forEach(sourceFile.libReferenceDirectives, ref => { const lib = host.getLibFileFromReference(ref); if (lib) { diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index dc4078caa2433..f9d94f3ee5976 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -72,15 +72,15 @@ namespace ts { * set of labels that occurred inside the converted loop * used to determine if labeled jump can be emitted as is or it should be dispatched to calling code */ - labels?: Map; + labels?: Map; /* * collection of labeled jumps that transfer control outside the converted loop. * maps store association 'label -> labelMarker' where * - label - value of label as it appear in code * - label marker - return value that should be interpreted by calling code as 'jump to