From 7fa1fd0f36558bc1f802bd5e052f4c6453defa09 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 12 Sep 2023 16:42:49 -0400 Subject: [PATCH] rudimentary parallel parse --- ThirdPartyNoticeText.txt | 72 + package.json | 1 + scripts/build/options.mjs | 10 +- scripts/build/tests.mjs | 7 + scripts/dtsBundler.mjs | 2 +- src/compiler/_namespaces/ts.ts | 30 + src/compiler/checker.ts | 12 +- src/compiler/commandLineParser.ts | 30 +- src/compiler/core.ts | 84 +- src/compiler/debug.ts | 57 +- src/compiler/diagnosticMessages.json | 8 + src/compiler/factory/baseNodeFactory.ts | 12 +- src/compiler/factory/nodeFactory.ts | 88 +- src/compiler/parser.ts | 303 +- src/compiler/program.ts | 931 ++++- src/compiler/sharing/collections/hashData.ts | 356 ++ .../sharing/collections/sharedLinkedList.ts | 218 ++ src/compiler/sharing/collections/sharedMap.ts | 59 + .../collections/sharedResizableArray.ts | 47 + src/compiler/sharing/collections/sharedSet.ts | 55 + src/compiler/sharing/collections/xxhash32.ts | 86 + src/compiler/sharing/sharedDiagnostics.ts | 132 + src/compiler/sharing/sharedNode.ts | 3420 +++++++++++++++++ src/compiler/sharing/sharedNodeAdapter.ts | 2545 ++++++++++++ src/compiler/sharing/sharedNodeArray.ts | 29 + src/compiler/sharing/sharedObjectAllocator.ts | 37 + src/compiler/sharing/sharedParserState.ts | 51 + src/compiler/sharing/sharedSymbol.ts | 36 + .../sharing/structs/fakeSharedStruct.ts | 145 + .../sharing/structs/identifiableStruct.ts | 52 + src/compiler/sharing/structs/shareable.ts | 54 + src/compiler/sharing/structs/sharedStruct.ts | 333 ++ .../sharing/structs/sharedStructsGlobals.ts | 124 + src/compiler/sharing/structs/taggedStruct.ts | 68 + src/compiler/symbolDisposeShim.ts | 12 + src/compiler/symbolMetadataShim.ts | 12 + src/compiler/sys.ts | 40 +- src/compiler/threading/condition.ts | 110 + src/compiler/threading/countdownEvent.ts | 118 + src/compiler/threading/lockable.ts | 10 + src/compiler/threading/manualResetEvent.ts | 41 + src/compiler/threading/mutex.ts | 154 + src/compiler/threading/scopedLock.ts | 108 + src/compiler/threading/sharedLock.ts | 108 + src/compiler/threading/sharedLockable.ts | 6 + src/compiler/threading/sharedMutex.ts | 277 ++ src/compiler/threading/threadPool.ts | 231 ++ src/compiler/threading/uniqueLock.ts | 118 + src/compiler/tsbuildPublic.ts | 25 +- src/compiler/tsconfig.json | 3 +- src/compiler/types.ts | 28 +- src/compiler/utilities.ts | 290 +- src/compiler/utilitiesPublic.ts | 8 +- src/compiler/watch.ts | 35 +- src/compiler/watchPublic.ts | 11 +- src/compiler/worker.ts | 73 + src/compiler/workerThreads.ts | 169 + src/executeCommandLine/executeCommandLine.ts | 91 +- src/harness/fakesHosts.ts | 4 + src/services/services.ts | 2 + src/testRunner/tests.ts | 3 + src/testRunner/unittests/sharing/hashData.ts | 15 + .../unittests/sharing/resizableArray.ts | 9 + src/testRunner/unittests/utf8.ts | 55 + src/tsc/tsc.ts | 9 +- .../reference/api/tsserverlibrary.d.ts | 3 + tests/baselines/reference/api/typescript.d.ts | 3 + thread.log | 0 68 files changed, 11288 insertions(+), 387 deletions(-) create mode 100644 src/compiler/sharing/collections/hashData.ts create mode 100644 src/compiler/sharing/collections/sharedLinkedList.ts create mode 100644 src/compiler/sharing/collections/sharedMap.ts create mode 100644 src/compiler/sharing/collections/sharedResizableArray.ts create mode 100644 src/compiler/sharing/collections/sharedSet.ts create mode 100644 src/compiler/sharing/collections/xxhash32.ts create mode 100644 src/compiler/sharing/sharedDiagnostics.ts create mode 100644 src/compiler/sharing/sharedNode.ts create mode 100644 src/compiler/sharing/sharedNodeAdapter.ts create mode 100644 src/compiler/sharing/sharedNodeArray.ts create mode 100644 src/compiler/sharing/sharedObjectAllocator.ts create mode 100644 src/compiler/sharing/sharedParserState.ts create mode 100644 src/compiler/sharing/sharedSymbol.ts create mode 100644 src/compiler/sharing/structs/fakeSharedStruct.ts create mode 100644 src/compiler/sharing/structs/identifiableStruct.ts create mode 100644 src/compiler/sharing/structs/shareable.ts create mode 100644 src/compiler/sharing/structs/sharedStruct.ts create mode 100644 src/compiler/sharing/structs/sharedStructsGlobals.ts create mode 100644 src/compiler/sharing/structs/taggedStruct.ts create mode 100644 src/compiler/symbolDisposeShim.ts create mode 100644 src/compiler/symbolMetadataShim.ts create mode 100644 src/compiler/threading/condition.ts create mode 100644 src/compiler/threading/countdownEvent.ts create mode 100644 src/compiler/threading/lockable.ts create mode 100644 src/compiler/threading/manualResetEvent.ts create mode 100644 src/compiler/threading/mutex.ts create mode 100644 src/compiler/threading/scopedLock.ts create mode 100644 src/compiler/threading/sharedLock.ts create mode 100644 src/compiler/threading/sharedLockable.ts create mode 100644 src/compiler/threading/sharedMutex.ts create mode 100644 src/compiler/threading/threadPool.ts create mode 100644 src/compiler/threading/uniqueLock.ts create mode 100644 src/compiler/worker.ts create mode 100644 src/compiler/workerThreads.ts create mode 100644 src/testRunner/unittests/sharing/hashData.ts create mode 100644 src/testRunner/unittests/sharing/resizableArray.ts create mode 100644 src/testRunner/unittests/utf8.ts create mode 100644 thread.log diff --git a/ThirdPartyNoticeText.txt b/ThirdPartyNoticeText.txt index a857fb3ce77..2ba3565770c 100644 --- a/ThirdPartyNoticeText.txt +++ b/ThirdPartyNoticeText.txt @@ -189,5 +189,77 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. ------------------------------------------------------ +------------------ esfx ---------------------------- +Copyright 2019 Ron Buckton + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +------------------------------------------------------ + +----------------- .NET Core ---------------------------------- + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------ + +------------------ xxHash Library ---------------------------- + Copyright (c) 2012-2021 Yann Collet + All rights reserved. + + BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------ + ------------- End of ThirdPartyNotices ------------------------------------------- */ diff --git a/package.json b/package.json index cc39f6cba39..ef44e19861d 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "test:eslint-rules": "hereby run-eslint-rules-tests", "build": "npm run build:compiler && npm run build:tests", "build:compiler": "hereby local", + "build:tsc": "hereby tsc", "build:tests": "hereby tests", "build:tests:notypecheck": "hereby tests --no-typecheck", "start": "node lib/tsc", diff --git a/scripts/build/options.mjs b/scripts/build/options.mjs index ce2aa1330cb..e96f14bf158 100644 --- a/scripts/build/options.mjs +++ b/scripts/build/options.mjs @@ -4,7 +4,7 @@ import os from "os"; const ci = ["1", "true"].includes(process.env.CI ?? ""); const parsed = minimist(process.argv.slice(2), { - boolean: ["dirty", "light", "colors", "lkg", "soft", "fix", "failed", "keepFailed", "force", "built", "ci", "bundle", "typecheck", "lint", "coverage"], + boolean: ["dirty", "light", "colors", "lkg", "soft", "fix", "failed", "keepFailed", "force", "built", "ci", "bundle", "typecheck", "lint", "coverage", "structs"], string: ["browser", "tests", "break", "host", "reporter", "stackTraceLimit", "timeout", "shards", "shardId"], alias: { /* eslint-disable quote-props */ @@ -43,6 +43,7 @@ const parsed = minimist(process.argv.slice(2), { typecheck: true, lint: true, coverage: false, + structs: false, } }); @@ -57,6 +58,12 @@ if (!options.bundle && !options.typecheck) { throw new Error("--no-typecheck cannot be passed when bundling is disabled"); } +// TODO(rbuckton): remove this when esbuild supports `using` and decorators. +if (options.structs) { + options.bundle = false; + options.lkg = true; +} + export default options; @@ -90,5 +97,6 @@ export default options; * @property {boolean} typecheck * @property {boolean} lint * @property {boolean} coverage + * @property {boolean} structs */ void 0; diff --git a/scripts/build/tests.mjs b/scripts/build/tests.mjs index 205076f51dd..d5e2f085988 100644 --- a/scripts/build/tests.mjs +++ b/scripts/build/tests.mjs @@ -35,6 +35,7 @@ export async function runConsoleTests(runJs, defaultReporter, runInParallel, opt const shards = +cmdLineOptions.shards || undefined; const shardId = +cmdLineOptions.shardId || undefined; const coverage = cmdLineOptions.coverage; + const structs = cmdLineOptions.structs; if (!cmdLineOptions.dirty) { if (options.watching) { console.log(chalk.yellowBright(`[watch] cleaning test directories...`)); @@ -119,6 +120,12 @@ export async function runConsoleTests(runJs, defaultReporter, runInParallel, opt args.push(runJs); } + if (structs) { + args.unshift("--harmony-struct"); + args.unshift("--shared-string-table"); + args.unshift("--enable-source-map"); + } + /** @type {number | undefined} */ let errorStatus; diff --git a/scripts/dtsBundler.mjs b/scripts/dtsBundler.mjs index 305172d3199..5ba680a85a2 100644 --- a/scripts/dtsBundler.mjs +++ b/scripts/dtsBundler.mjs @@ -331,7 +331,7 @@ function verifyMatchingSymbols(decl) { * @param {ts.Symbol} moduleSymbol */ function emitAsNamespace(name, moduleSymbol) { - assert(moduleSymbol.flags & ts.SymbolFlags.ValueModule, "moduleSymbol is not a module"); + assert(moduleSymbol.flags & ts.SymbolFlags.ValueModule, `moduleSymbol '${moduleSymbol.name}' is not a module, was a ${ts.Debug.formatSymbolFlags(moduleSymbol.flags)} instead`); scopeStack.push(new Map()); const currentScope = scopeStack[scopeStack.length - 1]; diff --git a/src/compiler/_namespaces/ts.ts b/src/compiler/_namespaces/ts.ts index 34d731e8806..d1c778bc373 100644 --- a/src/compiler/_namespaces/ts.ts +++ b/src/compiler/_namespaces/ts.ts @@ -9,6 +9,35 @@ export * from "../perfLogger"; export * from "../tracing"; export * from "../types"; export * from "../sys"; +export * from "../workerThreads"; +export * from "../threading/condition"; +export * from "../threading/countdownEvent"; +export * from "../threading/lockable"; +export * from "../threading/manualResetEvent"; +export * from "../threading/mutex"; +export * from "../threading/scopedLock"; +export * from "../threading/sharedLockable"; +export * from "../threading/sharedMutex"; +export * from "../threading/threadPool"; +export * from "../threading/uniqueLock"; +export * from "../sharing/collections/hashData"; +export * from "../sharing/collections/sharedLinkedList"; +export * from "../sharing/collections/sharedMap"; +export * from "../sharing/collections/sharedResizableArray"; +export * from "../sharing/collections/sharedSet"; +export * from "../sharing/collections/xxhash32"; +export * from "../sharing/structs/fakeSharedStruct"; +export * from "../sharing/structs/identifiableStruct"; +export * from "../sharing/structs/shareable"; +export * from "../sharing/structs/sharedStruct"; +export * from "../sharing/structs/taggedStruct"; +export * from "../sharing/sharedDiagnostics"; +export * from "../sharing/sharedNode"; +export * from "../sharing/sharedNodeAdapter"; +export * from "../sharing/sharedNodeArray"; +export * from "../sharing/sharedObjectAllocator"; +export * from "../sharing/sharedParserState"; +export * from "../sharing/sharedSymbol"; export * from "../path"; export * from "../diagnosticInformationMap.generated"; export * from "../scanner"; @@ -68,6 +97,7 @@ export * from "../builder"; export * from "../builderPublic"; export * from "../resolutionCache"; export * from "../watch"; +export * from "../worker"; export * from "../watchPublic"; export * from "../tsbuild"; export * from "../tsbuildPublic"; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c43435bc001..cb9bc8c9a22 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -736,6 +736,7 @@ import { isUMDExportSymbol, isValidBigIntString, isValidESSymbolDeclaration, + isValidNumberString, isValidTypeOnlyAliasUseSite, isValueSignatureDeclaration, isVariableDeclaration, @@ -24322,17 +24323,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { sourceEnd.slice(sourceEnd.length - endLen) !== targetEnd.slice(targetEnd.length - endLen); } - /** - * Tests whether the provided string can be parsed as a number. - * @param s The string to test. - * @param roundTripOnly Indicates the resulting number matches the input when converted back to a string. - */ - function isValidNumberString(s: string, roundTripOnly: boolean): boolean { - if (s === "") return false; - const n = +s; - return isFinite(n) && (!roundTripOnly || "" + n === s); - } - /** * @param text a valid bigint string excluding a trailing `n`, but including a possible prefix `-`. Use `isValidBigIntString(text, roundTripOnly)` before calling this function. */ diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 4004f0260ee..2e0fb7b408e 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -506,6 +506,18 @@ export const commonOptionsWithBuild: CommandLineOption[] = [ description: Diagnostics.Set_the_language_of_the_messaging_from_TypeScript_This_does_not_affect_emit, defaultValueDescription: Diagnostics.Platform_specific }, + { + name: "maxCpuCount", + type: "number", + category: Diagnostics.Command_line_Options, + isCommandLineOnly: false, + description: Diagnostics.Sets_the_maximum_number_of_worker_threads_to_use_during_build, + defaultValueDescription: Diagnostics.Platform_specific, + defaultIfValueMissing: () => sys.cpuCount?.(), + extraValidation: value => typeof value === "number" && value <= 0 ? + [Diagnostics.Option_maxCpuCount_must_be_set_to_1_or_greater] : + undefined + } ]; /** @internal */ @@ -1882,16 +1894,26 @@ function parseOptionValue( } else { // Check to see if no argument was provided (e.g. "--locale" is the last command-line argument). - if (!args[i] && opt.type !== "boolean") { + if (!args[i] && opt.type !== "boolean" && !(opt.type === "number" && opt.defaultIfValueMissing)) { errors.push(createCompilerDiagnostic(diagnostics.optionTypeMismatchDiagnostic, opt.name, getCompilerOptionValueTypeString(opt))); } if (args[i] !== "null") { switch (opt.type) { - case "number": - options[opt.name] = validateJsonOptionValue(opt, parseInt(args[i]), errors); - i++; + case "number": { + let value: number | undefined; + if (args[i]) { + value = parseInt(args[i]); + } + if ((isNullOrUndefined(value) || isNaN(value) || !isFinite(value)) && opt.defaultIfValueMissing) { + value = opt.defaultIfValueMissing(); + } + else { + i++; + } + options[opt.name] = validateJsonOptionValue(opt, value, errors); break; + } case "boolean": // boolean flag has optional value true, false, others const optValue = args[i]; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index f5d60b04591..12f61f0dcec 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -5,13 +5,18 @@ import { Comparison, Debug, EqualityComparer, + isTaggedStruct, isWhiteSpaceLike, MapLike, Queue, + SharedNodeArray, SortedArray, SortedReadonlyArray, TextSpan, } from "./_namespaces/ts"; +import { SharedNodeBase } from "./sharing/sharedNode"; +import { isShareableNonPrimitive, isSharedArray } from "./sharing/structs/shareable"; +import { Tag } from "./sharing/structs/taggedStruct"; /** @internal */ @@ -660,21 +665,22 @@ export function mapEntries(map: ReadonlyMap | undefined, } /** @internal */ -export function some(array: readonly T[] | undefined): array is readonly T[]; +export function some(array: readonly T[] | SharedNodeArray> | undefined): array is readonly T[] | SharedNodeArray>; /** @internal */ -export function some(array: readonly T[] | undefined, predicate: (value: T) => boolean): boolean; +export function some(array: readonly T[] | SharedNodeArray> | undefined, predicate: (value: T) => boolean): boolean; /** @internal */ -export function some(array: readonly T[] | undefined, predicate?: (value: T) => boolean): boolean { +export function some(array: readonly T[] | SharedNodeArray> | undefined, predicate?: (value: T) => boolean): boolean { if (array) { if (predicate) { - for (const v of array) { + const iterable = isTaggedStruct(array, Tag.NodeArray) ? SharedNodeArray.values(array) : array; + for (const v of iterable) { if (predicate(v)) { return true; } } } else { - return array.length > 0; + return (array instanceof SharedNodeArray ? array.items.length : array.length) > 0; } } return false; @@ -1841,12 +1847,12 @@ export function isArray(value: any): value is readonly unknown[] { } /** @internal */ -export function toArray(value: T | T[]): T[]; +export function toArray(value: T | T[] | SharedArray>): T[]; /** @internal */ -export function toArray(value: T | readonly T[]): readonly T[]; +export function toArray(value: T | readonly T[] | SharedArray>): readonly T[]; /** @internal */ -export function toArray(value: T | T[]): T[] { - return isArray(value) ? value : [value]; +export function toArray(value: T | T[] | SharedArray>): T[] { + return isSharedArray(value) ? Array.from(value) : isArray(value) ? value : [value]; } /** @@ -1857,11 +1863,17 @@ export function toArray(value: T | T[]): T[] { export function isString(text: unknown): text is string { return typeof text === "string"; } + /** @internal */ export function isNumber(x: unknown): x is number { return typeof x === "number"; } +/** @internal */ +export function isNull(x: unknown): x is null { // eslint-disable-line no-null/no-null + return x === null; // eslint-disable-line no-null/no-null +} + /** @internal */ export function tryCast(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut | undefined { return value !== undefined && test(value) ? value : undefined; @@ -1871,7 +1883,24 @@ export function tryCast(value: TIn | undefined, tes export function cast(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut { if (value !== undefined && test(value)) return value; - return Debug.fail(`Invalid cast. The supplied value ${value} did not pass the test '${Debug.getFunctionName(test)}'.`); + let valueArg: unknown = value; + if (isShareableNonPrimitive(valueArg)) { + if ("__tag__" in valueArg) { + const tag = valueArg.__tag__ as Tag; + if (tag === Tag.Node) { + const kind = (valueArg as SharedNodeBase).kind; + valueArg = `[object SharedNodeBase(${Debug.formatSyntaxKind(kind)})]`; + } + else { + valueArg = `[object TaggedStruct(${Debug.formatTag(tag)})]`; + } + } + else { + valueArg = "[object SharedStruct]"; + } + } + + return Debug.fail(`Invalid cast. The supplied value ${valueArg} did not pass the test '${Debug.getFunctionName(test)}'.`); } /** @@ -2883,3 +2912,38 @@ export function isNodeLikeSystem(): boolean { && !(process as any).browser && typeof module === "object"; } + +/** @internal */ +export class Lazy { + private static circular = (): any => { throw new Error("Lazy instantiation was circular"); }; + + private _value: T | (() => T); + private _hasValue: boolean; + + constructor(valueFactory: () => T) { + this._value = valueFactory; + this._hasValue = false; + } + + get hasValue() { + return this._value; + } + + get value() { + if (this._hasValue) { + return this._value as T; + } + const valueFactory = this._value as (() => T); + try { + this._value = Lazy.circular; + this._value = valueFactory(); + this._hasValue = true; + return this._value; + } + finally { + if (!this._hasValue) { + this._value = valueFactory; + } + } + } +} diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts index 21d7fa6129b..abe3f11303b 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -93,6 +93,8 @@ import { VarianceFlags, zipWith, } from "./_namespaces/ts"; +import { isShareableNonPrimitive } from "./sharing/structs/shareable"; +import { Tag } from "./sharing/structs/taggedStruct"; /** @internal */ export enum LogLevel { @@ -106,6 +108,7 @@ export enum LogLevel { /** @internal */ export interface LoggingHost { log(level: LogLevel, s: string): void; + format?(...args: readonly unknown[]): string; } /** @internal */ @@ -123,31 +126,41 @@ export namespace Debug { return currentLogLevel <= level; } - function logMessage(level: LogLevel, s: string): void { + function logMessage(level: LogLevel, ...args: unknown[]): void { if (loggingHost && shouldLog(level)) { + const s = loggingHost.format?.(...args) ?? args.join(" "); loggingHost.log(level, s); } } - export function log(s: string): void { - logMessage(LogLevel.Info, s); + export function format(...args: unknown[]) { + const text = loggingHost?.format?.(...args); + if (text !== undefined) { + return text; + } + args = args.map(arg => isShareableNonPrimitive(arg) ? "[object SharedStruct]" : arg); + return args.join(" "); + } + + export function log(...args: unknown[]): void { + logMessage(LogLevel.Info, ...args); } export namespace log { - export function error(s: string): void { - logMessage(LogLevel.Error, s); + export function error(...args: unknown[]): void { + logMessage(LogLevel.Error, ...args); } - export function warn(s: string): void { - logMessage(LogLevel.Warning, s); + export function warn(...args: unknown[]): void { + logMessage(LogLevel.Warning, ...args); } - export function log(s: string): void { - logMessage(LogLevel.Info, s); + export function log(...args: unknown[]): void { + logMessage(LogLevel.Info, ...args); } - export function trace(s: string): void { - logMessage(LogLevel.Verbose, s); + export function trace(...args: unknown[]): void { + logMessage(LogLevel.Verbose, ...args); } } @@ -192,13 +205,18 @@ export namespace Debug { return true; } + export function captureStackTrace(e: T, stackCrawlMark: AnyFunction) { + if ((Error as any).captureStackTrace) { + (Error as any).captureStackTrace(e, stackCrawlMark || fail); + } + return e; + } + export function fail(message?: string, stackCrawlMark?: AnyFunction): never { // eslint-disable-next-line no-debugger debugger; const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure."); - if ((Error as any).captureStackTrace) { - (Error as any).captureStackTrace(e, stackCrawlMark || fail); - } + captureStackTrace(e, stackCrawlMark ?? fail); throw e; } @@ -433,6 +451,10 @@ export namespace Debug { return sorted; } + export function formatTag(tag: Tag | undefined): string { + return formatEnum(tag, (ts as any).Tag, /*isFlags*/ false); + } + export function formatSyntaxKind(kind: SyntaxKind | undefined): string { return formatEnum(kind, (ts as any).SyntaxKind, /*isFlags*/ false); } @@ -548,7 +570,7 @@ export namespace Debug { } } - let nodeArrayProto: NodeArray | undefined; + const weakNodeArrayPrototype = new WeakMap>(); function attachNodeArrayDebugInfoWorker(array: NodeArray) { if (!("__tsDebuggerDisplay" in array)) { // eslint-disable-line local/no-in-operator @@ -574,8 +596,11 @@ export namespace Debug { if (typeof Object.setPrototypeOf === "function") { // if we're in es2015, attach the method to a shared prototype for `NodeArray` // so the method doesn't show up in the watch window. + const prototype = Object.getPrototypeOf(array); + let nodeArrayProto = weakNodeArrayPrototype.get(prototype); if (!nodeArrayProto) { - nodeArrayProto = Object.create(Array.prototype) as NodeArray; + nodeArrayProto = Object.create(prototype) as NodeArray; + weakNodeArrayPrototype.set(prototype, nodeArrayProto); attachNodeArrayDebugInfoWorker(nodeArrayProto); } Object.setPrototypeOf(array, nodeArrayProto); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8d925784fc1..e4a921b4f3d 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4373,6 +4373,10 @@ "category": "Error", "code": 5110 }, + "Option 'maxCpuCount' must be set to 1 or greater.": { + "category": "Error", + "code": 5111 + }, "Generates a sourcemap for each corresponding '.d.ts' file.": { "category": "Message", @@ -6151,6 +6155,10 @@ "category": "Message", "code": 6804 }, + "Sets the maximum number of worker threads to use during build.": { + "category": "Message", + "code": 6805 + }, "one of:": { "category": "Message", diff --git a/src/compiler/factory/baseNodeFactory.ts b/src/compiler/factory/baseNodeFactory.ts index 5641e25703e..a504345368b 100644 --- a/src/compiler/factory/baseNodeFactory.ts +++ b/src/compiler/factory/baseNodeFactory.ts @@ -1,7 +1,8 @@ import { Node, + NodeArray, objectAllocator, - SyntaxKind, + SyntaxKind } from "../_namespaces/ts"; /** @@ -11,6 +12,7 @@ import { * @internal */ export interface BaseNodeFactory { + createBaseNodeArray(items: readonly T[], hasTrailingComma?: boolean): NodeArray; createBaseSourceFileNode(kind: SyntaxKind.SourceFile): Node; createBaseIdentifierNode(kind: SyntaxKind.Identifier): Node; createBasePrivateIdentifierNode(kind: SyntaxKind.PrivateIdentifier): Node; @@ -24,6 +26,7 @@ export interface BaseNodeFactory { * @internal */ export function createBaseNodeFactory(): BaseNodeFactory { + let NodeArrayContructor: new (elements: readonly T[], hasTrailingComma?: boolean) => NodeArray; let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; let IdentifierConstructor: new (kind: SyntaxKind.Identifier, pos: number, end: number) => Node; @@ -35,7 +38,8 @@ export function createBaseNodeFactory(): BaseNodeFactory { createBaseIdentifierNode, createBasePrivateIdentifierNode, createBaseTokenNode, - createBaseNode + createBaseNode, + createBaseNodeArray, }; function createBaseSourceFileNode(kind: SyntaxKind.SourceFile): Node { @@ -57,4 +61,8 @@ export function createBaseNodeFactory(): BaseNodeFactory { function createBaseNode(kind: SyntaxKind): Node { return new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, /*pos*/ -1, /*end*/ -1); } + + function createBaseNodeArray(elements: readonly T[], hasTrailingComma?: boolean): NodeArray { + return new (NodeArrayContructor || (NodeArrayContructor = objectAllocator.getNodeArrayConstructor()))(elements, hasTrailingComma); + } } diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 5a33d53ed4d..3f2323c50aa 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -475,6 +475,9 @@ import { WithStatement, YieldExpression, } from "../_namespaces/ts"; +import { SharedNodeBase } from "../sharing/sharedNode"; +import { SharedNodeArray } from "../sharing/sharedNodeArray"; +import { isShareableNonPrimitive } from "../sharing/structs/shareable"; let nextAutoGenerateId = 0; @@ -903,7 +906,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode createJsxText, updateJsxText, createJsxOpeningFragment, - createJsxJsxClosingFragment, + createJsxJsxClosingFragment: createJsxClosingFragment, + createJsxClosingFragment, updateJsxFragment, createJsxAttribute, updateJsxAttribute, @@ -1053,12 +1057,10 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode // This *was* a `NodeArray`, but the `hasTrailingComma` option differs. Recreate the // array with the same elements, text range, and transform flags but with the updated // value for `hasTrailingComma` - const array = elements.slice() as MutableNodeArray; + const array = baseFactory.createBaseNodeArray(elements.slice(), hasTrailingComma) as MutableNodeArray; array.pos = elements.pos; array.end = elements.end; - array.hasTrailingComma = hasTrailingComma; array.transformFlags = elements.transformFlags; - Debug.attachNodeArrayDebugInfo(array); return array; } @@ -1066,13 +1068,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode // repeatedly calling push(), the list may not have the optimal memory layout. We invoke slice() for // small arrays (1 to 4 elements) to give the VM a chance to allocate an optimal representation. const length = elements.length; - const array = (length >= 1 && length <= 4 ? elements.slice() : elements) as MutableNodeArray; - array.pos = -1; - array.end = -1; - array.hasTrailingComma = !!hasTrailingComma; - array.transformFlags = TransformFlags.None; + const array = baseFactory.createBaseNodeArray(length >= 1 && length <= 4 ? elements.slice() : elements, !!hasTrailingComma) as MutableNodeArray; aggregateChildrenFlags(array); - Debug.attachNodeArrayDebugInfo(array); return array; } @@ -3768,6 +3765,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode node.transformFlags |= propagateChildrenFlags(node.modifiers) | propagateChildFlags(node.declarationList); + if (modifiersToFlags(node.modifiers) & ModifierFlags.Ambient) { node.transformFlags = TransformFlags.ContainsTypeScript; } @@ -5591,7 +5589,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function createJsxJsxClosingFragment() { + function createJsxClosingFragment() { const node = createBaseNode(SyntaxKind.JsxClosingFragment); node.transformFlags |= TransformFlags.ContainsJsx; return node; @@ -5931,35 +5929,37 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode propagateChildrenFlags(node.statements) | propagateChildFlags(node.endOfFileToken); - node.locals = undefined; // initialized by binder (LocalsContainer) - node.nextContainer = undefined; // initialized by binder (LocalsContainer) - node.endFlowNode = undefined; + if (!isShareableNonPrimitive(node)) { + node.locals = undefined; // initialized by binder (LocalsContainer) + node.nextContainer = undefined; // initialized by binder (LocalsContainer) + node.endFlowNode = undefined; + node.nodeCount = 0; + node.identifierCount = 0; + node.symbolCount = 0; + node.parseDiagnostics = undefined!; + node.bindDiagnostics = undefined!; + node.bindSuggestionDiagnostics = undefined; + node.lineMap = undefined!; + node.externalModuleIndicator = undefined; + node.setExternalModuleIndicator = undefined; + node.pragmas = undefined!; + node.checkJsDirective = undefined; + node.referencedFiles = undefined!; + node.typeReferenceDirectives = undefined!; + node.libReferenceDirectives = undefined!; + node.amdDependencies = undefined!; + node.commentDirectives = undefined; + node.identifiers = undefined!; + node.packageJsonLocations = undefined; + node.packageJsonScope = undefined; + node.imports = undefined!; + node.moduleAugmentations = undefined!; + node.ambientModuleNames = undefined!; + node.resolvedModules = undefined; + node.classifiableNames = undefined; + node.impliedNodeFormat = undefined; + } - node.nodeCount = 0; - node.identifierCount = 0; - node.symbolCount = 0; - node.parseDiagnostics = undefined!; - node.bindDiagnostics = undefined!; - node.bindSuggestionDiagnostics = undefined; - node.lineMap = undefined!; - node.externalModuleIndicator = undefined; - node.setExternalModuleIndicator = undefined; - node.pragmas = undefined!; - node.checkJsDirective = undefined; - node.referencedFiles = undefined!; - node.typeReferenceDirectives = undefined!; - node.libReferenceDirectives = undefined!; - node.amdDependencies = undefined!; - node.commentDirectives = undefined; - node.identifiers = undefined!; - node.packageJsonLocations = undefined; - node.packageJsonScope = undefined; - node.imports = undefined!; - node.moduleAugmentations = undefined!; - node.ambientModuleNames = undefined!; - node.resolvedModules = undefined; - node.classifiableNames = undefined; - node.impliedNodeFormat = undefined; return node; } @@ -5998,7 +5998,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode const node = baseFactory.createBaseSourceFileNode(SyntaxKind.SourceFile) as Mutable; node.flags |= source.flags & ~NodeFlags.Synthesized; for (const p in source) { - if (hasProperty(node, p) || !hasProperty(source, p)) { + if (hasProperty(node, p) || !hasProperty(source, p) || p === "__tag__" || p === "__hash__" || p === "__shared__" || p === "__sharedCache__") { continue; } if (p === "emitNode") { @@ -7182,10 +7182,11 @@ function propagateChildrenFlags(children: NodeArray | undefined): Transfor return children ? children.transformFlags : TransformFlags.None; } -function aggregateChildrenFlags(children: MutableNodeArray) { +function aggregateChildrenFlags(children: MutableNodeArray | SharedNodeArray) { let subtreeFlags = TransformFlags.None; - for (const child of children) { - subtreeFlags |= propagateChildFlags(child); + const values = children instanceof SharedNodeArray ? Array.from(children.items) : children; + for (const child of values) { + subtreeFlags |= propagateChildFlags(child as Node); } children.transformFlags = subtreeFlags; } @@ -7280,6 +7281,7 @@ const syntheticFactory: BaseNodeFactory = { createBasePrivateIdentifierNode: kind => makeSynthetic(baseFactory.createBasePrivateIdentifierNode(kind)), createBaseTokenNode: kind => makeSynthetic(baseFactory.createBaseTokenNode(kind)), createBaseNode: kind => makeSynthetic(baseFactory.createBaseNode(kind)), + createBaseNodeArray: baseFactory.createBaseNodeArray, }; export const factory = createNodeFactory(NodeFactoryFlags.NoIndentationOnFreshPropertyAccess, syntheticFactory); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index bd8081d1267..7b0ad2445fb 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2,9 +2,11 @@ import { AccessorDeclaration, addRange, addRelatedInfo, + AmdDependency, append, ArrayBindingElement, ArrayBindingPattern, + arrayFrom, ArrayLiteralExpression, ArrayTypeNode, ArrowFunction, @@ -34,6 +36,7 @@ import { CaseOrDefaultClause, CatchClause, CharacterCodes, + CheckJsDirective, ClassDeclaration, ClassElement, ClassExpression, @@ -65,8 +68,11 @@ import { Diagnostic, DiagnosticArguments, DiagnosticMessage, + DiagnosticMessageChain, + DiagnosticRelatedInformation, Diagnostics, DiagnosticWithDetachedLocation, + DiagnosticWithLocation, DoStatement, DotDotDotToken, ElementAccessExpression, @@ -88,6 +94,7 @@ import { ExternalModuleReference, fileExtensionIs, fileExtensionIsOneOf, + FileReference, findIndex, firstOrUndefined, forEach, @@ -113,8 +120,10 @@ import { HasJSDoc, hasJSDocNodes, HasModifiers, + hasProperty, HeritageClause, Identifier, + identity, idText, IfStatement, ImportClause, @@ -238,7 +247,6 @@ import { LiteralExpression, LiteralLikeNode, LiteralTypeNode, - map, mapDefined, MappedTypeNode, MemberExpression, @@ -330,6 +338,7 @@ import { setTextRangePos, setTextRangePosEnd, setTextRangePosWidth, + SharedMap, ShorthandPropertyAssignment, skipTrivia, some, @@ -398,6 +407,11 @@ import { YieldExpression, } from "./_namespaces/ts"; import * as performance from "./_namespaces/ts.performance"; +import { SharedDiagnostic, SharedDiagnosticMessageChain, SharedDiagnosticRelatedInformation, SharedDiagnosticWithLocation } from "./sharing/sharedDiagnostics"; +import { SharedAmdDependency, SharedCheckJsDirective, SharedCommentDirective, SharedCommentRange, SharedFileReference, SharedNodeBase, SharedPragma, SharedPragmaArguments, SharedPragmaSpan, SharedSourceFile, SharedTextRange } from "./sharing/sharedNode"; +import { SharedNodeArray } from "./sharing/sharedNodeArray"; +import { isShareableNonPrimitive } from "./sharing/structs/shareable"; +import { Tag, TaggedStruct } from "./sharing/structs/taggedStruct"; const enum SignatureFlags { None = 0, @@ -414,6 +428,7 @@ const enum SpeculationKind { Reparse } +let NodeArrayConstructor: new (elements: readonly T[], hasTrailingComma?: boolean) => NodeArray; let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; let IdentifierConstructor: new (kind: SyntaxKind.Identifier, pos: number, end: number) => Node; @@ -431,6 +446,7 @@ export const parseBaseNodeFactory: BaseNodeFactory = { createBasePrivateIdentifierNode: kind => new (PrivateIdentifierConstructor || (PrivateIdentifierConstructor = objectAllocator.getPrivateIdentifierConstructor()))(kind, -1, -1), createBaseTokenNode: kind => new (TokenConstructor || (TokenConstructor = objectAllocator.getTokenConstructor()))(kind, -1, -1), createBaseNode: kind => new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, -1, -1), + createBaseNodeArray: (elements, hasTrailingComma?) => new (NodeArrayConstructor || (NodeArrayConstructor = objectAllocator.getNodeArrayConstructor()))(elements, hasTrailingComma), }; /** @internal */ @@ -1265,16 +1281,17 @@ export function forEachChildRecursively(rootNode: Node, cbNode: (node: Node, while (queue.length !== 0) { const current = queue.pop()!; const parent = parents.pop()!; - if (isArray(current)) { + if (isArray(current) || current instanceof SharedNodeArray) { + const currentArray = current instanceof SharedNodeArray ? arrayFrom(SharedNodeArray.values(current)) as unknown as NodeArray : current; if (cbNodes) { - const res = cbNodes(current, parent); + const res = cbNodes(currentArray, parent); if (res) { if (res === "skip") continue; return res; } } - for (let i = current.length - 1; i >= 0; --i) { - queue.push(current[i]); + for (let i = currentArray.length - 1; i >= 0; --i) { + queue.push(currentArray[i]); parents.push(parent); } } @@ -1327,6 +1344,9 @@ function setExternalModuleIndicator(sourceFile: SourceFile) { sourceFile.externalModuleIndicator = isFileProbablyExternalModule(sourceFile); } +export function createSourceFile(fileName: string, sourceText: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile; +/** @internal */ +export function createSourceFile(fileName: string, sourceText: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile; export function createSourceFile(fileName: string, sourceText: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { tracing?.push(tracing.Phase.Parse, "createSourceFile", { path: fileName }, /*separateBeginAndEnd*/ true); performance.mark("beforeParse"); @@ -1424,6 +1444,7 @@ namespace Parser { var disallowInAndDecoratorContext = NodeFlags.DisallowInContext | NodeFlags.DecoratorContext; // capture constructors in 'initializeState' to avoid null checks + let NodeArrayConstructor: new (elements: readonly T[], hasTrailingComma?: boolean) => NodeArray; var NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; var TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; var IdentifierConstructor: new (kind: SyntaxKind.Identifier, pos: number, end: number) => Identifier; @@ -1442,7 +1463,8 @@ namespace Parser { createBaseIdentifierNode: kind => countNode(new IdentifierConstructor(kind, /*pos*/ 0, /*end*/ 0)), createBasePrivateIdentifierNode: kind => countNode(new PrivateIdentifierConstructor(kind, /*pos*/ 0, /*end*/ 0)), createBaseTokenNode: kind => countNode(new TokenConstructor(kind, /*pos*/ 0, /*end*/ 0)), - createBaseNode: kind => countNode(new NodeConstructor(kind, /*pos*/ 0, /*end*/ 0)) + createBaseNode: kind => countNode(new NodeConstructor(kind, /*pos*/ 0, /*end*/ 0)), + createBaseNodeArray: (elements, hasTrailingComma?) => new NodeArrayConstructor(elements, hasTrailingComma), }; var factory = createNodeFactory(NodeFactoryFlags.NoParenthesizerRules | NodeFactoryFlags.NoNodeConverters | NodeFactoryFlags.NoOriginalNode, baseNodeFactory); @@ -1702,6 +1724,7 @@ namespace Parser { } function initializeState(_fileName: string, _sourceText: string, _languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor | undefined, _scriptKind: ScriptKind) { + NodeArrayConstructor = objectAllocator.getNodeArrayConstructor(); NodeConstructor = objectAllocator.getNodeConstructor(); TokenConstructor = objectAllocator.getTokenConstructor(); IdentifierConstructor = objectAllocator.getIdentifierConstructor(); @@ -1765,6 +1788,69 @@ namespace Parser { topLevel = true; } + function shareCommentDirective(directive: CommentDirective): SharedCommentDirective { + const sharedRange = new SharedTextRange(directive.range.pos, directive.range.end); + const sharedDirective = new SharedCommentDirective(sharedRange, directive.type); + return sharedDirective; + } + + function shareDiagnosticMessageChain(messageChain: DiagnosticMessageChain): SharedDiagnosticMessageChain { + return new SharedDiagnosticMessageChain( + messageChain.messageText, + messageChain.category, + messageChain.code, + shareArray(messageChain.next, shareDiagnosticMessageChain) + ); + } + + function shareDiagnosticRelatedInformation(related: Diagnostic | DiagnosticRelatedInformation): SharedDiagnostic | SharedDiagnosticRelatedInformation { + if (hasProperty(related, "reportsUnnecessary") || + hasProperty(related, "reportsDeprecated") || + hasProperty(related, "source") || + hasProperty(related, "relatedInformation") || + hasProperty(related, "skippedOn")) { + return shareDiagnostic(related); + } + Debug.assert(related.file === undefined || isShareableNonPrimitive(related.file)); + return new SharedDiagnosticRelatedInformation( + related.category, + related.code, + related.file as unknown as SharedSourceFile | undefined, + related.start, + related.length, + typeof related.messageText === "string" ? related.messageText : shareDiagnosticMessageChain(related.messageText) + ); + } + + function shareDiagnostic(diagnostic: DiagnosticWithLocation): SharedDiagnosticWithLocation; + function shareDiagnostic(diagnostic: Diagnostic): SharedDiagnostic; + function shareDiagnostic(diagnostic: Diagnostic): SharedDiagnostic { + Debug.assert(diagnostic.file === undefined || isShareableNonPrimitive(diagnostic.file)); + const sharedDiagnostic = new SharedDiagnostic( + diagnostic.category, + diagnostic.code, + diagnostic.file as unknown as SharedSourceFile | undefined, + diagnostic.start, + diagnostic.length, + typeof diagnostic.messageText === "string" ? diagnostic.messageText : shareDiagnosticMessageChain(diagnostic.messageText) + ); + + sharedDiagnostic.reportsUnnecessary = diagnostic.reportsUnnecessary === undefined ? undefined : !!diagnostic.reportsUnnecessary; + sharedDiagnostic.reportsDeprecated = diagnostic.reportsDeprecated === undefined ? undefined : !!diagnostic.reportsDeprecated; + sharedDiagnostic.source = diagnostic.source; + sharedDiagnostic.relatedInformation = shareArray(diagnostic.relatedInformation, shareDiagnosticRelatedInformation); + sharedDiagnostic.skippedOn = diagnostic.skippedOn; + return sharedDiagnostic; + } + + function shareMap(map: ReadonlyMap, shareKey: (key: K) => KS, shareValue: (value: V) => VS) { + const sharedMap = new SharedMap(map.size); + for (const [key, value] of map) { + SharedMap.set(sharedMap, shareKey(key), shareValue(value)); + } + return sharedMap; + } + function parseSourceFileWorker(languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind: ScriptKind, setExternalModuleIndicator: (file: SourceFile) => void): SourceFile { const isDeclarationFile = isDeclarationFileName(fileName); if (isDeclarationFile) { @@ -1787,13 +1873,23 @@ namespace Parser { processCommentPragmas(sourceFile as {} as PragmaContext, sourceText); processPragmasIntoFields(sourceFile as {} as PragmaContext, reportPragmaDiagnostic); - sourceFile.commentDirectives = scanner.getCommentDirectives(); sourceFile.nodeCount = nodeCount; sourceFile.identifierCount = identifierCount; - sourceFile.identifiers = identifiers; - sourceFile.parseDiagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile); - if (jsDocDiagnostics) { - sourceFile.jsDocDiagnostics = attachFileToDiagnostics(jsDocDiagnostics, sourceFile); + if (isShareableNonPrimitive(sourceFile)) { + sourceFile.identifiers = shareMap(identifiers, identity, identity) as unknown as Map; + sourceFile.commentDirectives = shareArray(scanner.getCommentDirectives(), shareCommentDirective) as unknown as CommentDirective[]; + sourceFile.parseDiagnostics = shareArray(attachFileToDiagnostics(parseDiagnostics, sourceFile), shareDiagnostic) as unknown as DiagnosticWithLocation[]; + if (jsDocDiagnostics) { + sourceFile.jsDocDiagnostics = shareArray(attachFileToDiagnostics(jsDocDiagnostics, sourceFile), shareDiagnostic) as unknown as DiagnosticWithLocation[]; + } + } + else { + sourceFile.identifiers = identifiers; + sourceFile.commentDirectives = scanner.getCommentDirectives(); + sourceFile.parseDiagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile); + if (jsDocDiagnostics) { + sourceFile.jsDocDiagnostics = attachFileToDiagnostics(jsDocDiagnostics, sourceFile); + } } if (setParentNodes) { @@ -1815,7 +1911,14 @@ namespace Parser { Debug.assert(!node.jsDoc); // Should only be called once per node const jsDoc = mapDefined(getJSDocCommentRanges(node, sourceText), comment => JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos)); - if (jsDoc.length) node.jsDoc = jsDoc; + if (jsDoc.length) { + if (node instanceof SharedNodeBase) { + node.jsDoc = shareArray(jsDoc, n => n as unknown as SharedNodeBase) as unknown as JSDoc[]; + } + else { + node.jsDoc = jsDoc; + } + } if (hasDeprecatedTag) { hasDeprecatedTag = false; (node as Mutable).flags |= NodeFlags.Deprecated; @@ -1965,7 +2068,7 @@ namespace Parser { function setFields(sourceFile: SourceFile) { sourceFile.text = sourceText; - sourceFile.bindDiagnostics = []; + sourceFile.bindDiagnostics = isShareableNonPrimitive(sourceFile) ? undefined! : []; sourceFile.bindSuggestionDiagnostics = undefined; sourceFile.languageVersion = languageVersion; sourceFile.fileName = fileName; @@ -1974,7 +2077,9 @@ namespace Parser { sourceFile.scriptKind = scriptKind; setExternalModuleIndicator(sourceFile); - sourceFile.setExternalModuleIndicator = setExternalModuleIndicator; + if (!isShareableNonPrimitive(sourceFile)) { + sourceFile.setExternalModuleIndicator = setExternalModuleIndicator; + } } } @@ -7366,14 +7471,17 @@ namespace Parser { const pos = getNodePos(); const hasJSDoc = hasPrecedingJSDocComment(); const modifiers = parseModifiers(/*allowDecorators*/ true); - const isAmbient = some(modifiers, isDeclareModifier); + const modifiersArray = + modifiers instanceof SharedNodeArray ? arrayFrom(SharedNodeArray.values(modifiers) as IterableIterator) : + modifiers; + const isAmbient = some(modifiersArray, isDeclareModifier); if (isAmbient) { const node = tryReuseAmbientDeclaration(pos); if (node) { return node; } - for (const m of modifiers!) { + for (const m of modifiersArray!) { (m as Mutable).flags |= NodeFlags.Ambient; } return doInsideOfContext(NodeFlags.Ambient, () => parseDeclarationWorker(pos, hasJSDoc, modifiers)); @@ -9797,7 +9905,7 @@ namespace IncrementalParser { const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, sourceFile.scriptKind, sourceFile.setExternalModuleIndicator); result.commentDirectives = getNewCommentDirectives( sourceFile.commentDirectives, - result.commentDirectives, + result.commentDirectives?.slice(), changeRange.span.start, textSpanEnd(changeRange.span), delta, @@ -9810,7 +9918,7 @@ namespace IncrementalParser { } function getNewCommentDirectives( - oldDirectives: CommentDirective[] | undefined, + oldDirectives: readonly CommentDirective[] | undefined, newDirectives: CommentDirective[] | undefined, changeStart: number, changeRangeOldEnd: number, @@ -10374,41 +10482,131 @@ export function processCommentPragmas(context: PragmaContext, sourceText: string extractPragmas(pragmas, range, comment); } - context.pragmas = new Map() as PragmaMap; + const map = new Map() as PragmaMap; for (const pragma of pragmas) { - if (context.pragmas.has(pragma.name)) { - const currentValue = context.pragmas.get(pragma.name); + if (map.has(pragma.name)) { + const currentValue = map.get(pragma.name); if (currentValue instanceof Array) { currentValue.push(pragma.args); } else { - context.pragmas.set(pragma.name, [currentValue, pragma.args]); + map.set(pragma.name, [currentValue, pragma.args]); } continue; } - context.pragmas.set(pragma.name, pragma.args); + map.set(pragma.name, pragma.args); + } + + if (isShareableNonPrimitive(context)) { + const sharedMap = new SharedMap | SharedPragma>(); + for (const [key, value] of map) { + if (isArray(value)) { + SharedMap.set(sharedMap, key, shareArray(value, sharePragma)); + } + else { + SharedMap.set(sharedMap, key, sharePragma(value)); + } + } + context.pragmas = sharedMap as unknown as PragmaMap; + } + else { + context.pragmas = map as PragmaMap; } } +function sharePragmaValue(span: T): T extends string ? string : T extends object ? SharedPragmaSpan : undefined; +function sharePragmaValue(span: T): SharedPragmaSpan | string | undefined { + if (typeof span === "string" || span === undefined) { + return span; + } + return new SharedPragmaSpan(span.pos, span.end, span.value); +} + +function sharePragmaArguments(args: PragmaPseudoMapEntry["args"]["arguments"]) { + const sharedArgs = new SharedPragmaArguments(); + if ("types" in args) sharedArgs.types = sharePragmaValue(args.types); + if ("lib" in args) sharedArgs.lib = sharePragmaValue(args.lib); + if ("path" in args) sharedArgs.path = sharePragmaValue(args.path); + if ("no-default-lib" in args) sharedArgs["no-default-lib"] = sharePragmaValue(args["no-default-lib"]); + if ("resolution-mode" in args) sharedArgs["resolution-mode"] = sharePragmaValue(args["resolution-mode"]); + if ("name" in args) sharedArgs.name = sharePragmaValue(args.name); + if ("factory" in args) sharedArgs.factory = sharePragmaValue(args.factory); + return sharedArgs; +} + +function shareCommentRange(range: CommentRange) { + const sharedRange = new SharedCommentRange(); + sharedRange.kind = range.kind; + sharedRange.pos = range.pos; + sharedRange.end = range.end; + sharedRange.hasTrailingNewLine = range.hasTrailingNewLine; + return sharedRange; +} + +function sharePragma(pragma: PragmaPseudoMapEntry["args"]) { + return new SharedPragma( + sharePragmaArguments(pragma.arguments), + shareCommentRange(pragma.range) + ); +} + +function shareArray>(array: T[], shareElement: (value: T) => U): SharedArray; +function shareArray>(array: T[] | undefined, shareElement: (value: T) => U): SharedArray | undefined; +function shareArray>(array: T[] | undefined, shareElement: (value: T) => U): SharedArray | undefined { + if (array) { + const sharedArray = new SharedArray(array.length); + for (let i = 0; i < array.length; i++) { + sharedArray[i] = shareElement(array[i]); + } + return sharedArray; + } +} + + /** @internal */ export type PragmaDiagnosticReporter = (pos: number, length: number, message: DiagnosticMessage) => void; +function shareFileReference(ref: FileReference) { + return new SharedFileReference(ref.pos, ref.end, ref.fileName, ref.resolutionMode); +} + +function shareAmdDependency(dep: AmdDependency) { + return new SharedAmdDependency(dep.path, dep.name); +} + +function createFileReference(pos: number, end: number, fileName: string, resolutionMode?: ResolutionMode): FileReference { + return { pos, end, fileName, ...(resolutionMode !== undefined ? { resolutionMode } : {}) }; +} + +function createAmdDependency(path: string, name?: string): AmdDependency { + return { path, name }; +} + +function createCheckJsDirective(pos: number, end: number, enabled: boolean) { + return { pos, end, enabled }; +} + /** @internal */ export function processPragmasIntoFields(context: PragmaContext, reportDiagnostic: PragmaDiagnosticReporter): void { - context.checkJsDirective = undefined; - context.referencedFiles = []; - context.typeReferenceDirectives = []; - context.libReferenceDirectives = []; - context.amdDependencies = []; - context.hasNoDefaultLib = false; - context.pragmas!.forEach((entryOrList, key) => { // TODO: GH#18217 + const referencedFiles: FileReference[] = []; + const typeReferenceDirectives: FileReference[] = []; + const libReferenceDirectives: FileReference[] = []; + const amdDependencies: AmdDependency[] = []; + let checkJsDirective: CheckJsDirective | undefined; + let pragmas; + if (isShareableNonPrimitive(context)) { + const contextPragmas = context.pragmas as unknown as SharedMap | SharedPragma>; + pragmas = SharedMap.entries(contextPragmas); + } + else { + pragmas = context.pragmas!.entries(); + } + + for (const [key, entryOrList] of pragmas) { // TODO: The below should be strongly type-guarded and not need casts/explicit annotations, since entryOrList is related to // key and key is constrained to a union; but it's not (see GH#21483 for at least partial fix) :( switch (key) { case "reference": { - const referencedFiles = context.referencedFiles; - const typeReferenceDirectives = context.typeReferenceDirectives; - const libReferenceDirectives = context.libReferenceDirectives; forEach(toArray(entryOrList) as PragmaPseudoMap["reference"][], arg => { const { types, lib, path, ["resolution-mode"]: res } = arg.arguments; if (arg.arguments["no-default-lib"]) { @@ -10416,13 +10614,13 @@ export function processPragmasIntoFields(context: PragmaContext, reportDiagnosti } else if (types) { const parsed = parseResolutionMode(res, types.pos, types.end, reportDiagnostic); - typeReferenceDirectives.push({ pos: types.pos, end: types.end, fileName: types.value, ...(parsed ? { resolutionMode: parsed } : {}) }); + typeReferenceDirectives.push(createFileReference(types.pos, types.end, types.value, parsed)); } else if (lib) { - libReferenceDirectives.push({ pos: lib.pos, end: lib.end, fileName: lib.value }); + libReferenceDirectives.push(createFileReference(lib.pos, lib.end, lib.value)); } else if (path) { - referencedFiles.push({ pos: path.pos, end: path.end, fileName: path.value }); + referencedFiles.push(createFileReference(path.pos, path.end, path.value)); } else { reportDiagnostic(arg.range.pos, arg.range.end - arg.range.pos, Diagnostics.Invalid_reference_directive_syntax); @@ -10431,9 +10629,9 @@ export function processPragmasIntoFields(context: PragmaContext, reportDiagnosti break; } case "amd-dependency": { - context.amdDependencies = map( - toArray(entryOrList) as PragmaPseudoMap["amd-dependency"][], - x => ({ name: x.arguments.name, path: x.arguments.path })); + for (const x of toArray(entryOrList) as PragmaPseudoMap["amd-dependency"][]) { + amdDependencies.push(createAmdDependency(x.arguments.path, x.arguments.name)); + } break; } case "amd-module": { @@ -10455,12 +10653,8 @@ export function processPragmasIntoFields(context: PragmaContext, reportDiagnosti case "ts-check": { // _last_ of either nocheck or check in a file is the "winner" forEach(toArray(entryOrList), entry => { - if (!context.checkJsDirective || entry.range.pos > context.checkJsDirective.pos) { - context.checkJsDirective = { - enabled: key === "ts-check", - end: entry.range.end, - pos: entry.range.pos - }; + if (!checkJsDirective || entry.range.pos > checkJsDirective.pos) { + checkJsDirective = createCheckJsDirective(entry.range.pos, entry.range.end, key === "ts-check"); } }); break; @@ -10472,7 +10666,24 @@ export function processPragmasIntoFields(context: PragmaContext, reportDiagnosti return; // Accessed directly default: Debug.fail("Unhandled pragma kind"); // Can this be made into an assertNever in the future? } - }); + } + + if (isShareableNonPrimitive(context)) { + if (checkJsDirective) { + context.checkJsDirective = new SharedCheckJsDirective(checkJsDirective.pos, checkJsDirective.end, checkJsDirective.enabled); + } + context.referencedFiles = shareArray(referencedFiles, shareFileReference) as unknown as FileReference[]; + context.typeReferenceDirectives = shareArray(typeReferenceDirectives, shareFileReference) as unknown as FileReference[]; + context.libReferenceDirectives = shareArray(libReferenceDirectives, shareFileReference) as unknown as FileReference[]; + context.amdDependencies = shareArray(amdDependencies, shareAmdDependency) as unknown as AmdDependency[]; + } + else { + context.checkJsDirective = checkJsDirective; + context.referencedFiles = referencedFiles; + context.typeReferenceDirectives = typeReferenceDirectives; + context.libReferenceDirectives = libReferenceDirectives; + context.amdDependencies = amdDependencies; + } } const namedArgRegExCache = new Map(); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 227d3b5a398..c103a885c4d 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -104,16 +104,13 @@ import { forEachEntry, forEachKey, forEachPropertyAssignment, - forEachResolvedProjectReference as ts_forEachResolvedProjectReference, forEachTsConfigPropArray, FunctionLikeDeclaration, getAllowJSCompilerOption, getAutomaticTypeDirectiveNames, getBaseFileName, GetCanonicalFileName, - getCommonSourceDirectory as ts_getCommonSourceDirectory, getCommonSourceDirectoryOfConfig, - getDeclarationDiagnostics as ts_getDeclarationDiagnostics, getDefaultLibFileName, getDirectoryPath, getEmitDeclarations, @@ -129,6 +126,7 @@ import { getLineStarts, getMatchedFileSpec, getMatchedIncludeSpec, + getMaxCpuCount, getNewLineCharacter, getNormalizedAbsolutePath, getNormalizedAbsolutePathWithoutRoot, @@ -242,6 +240,8 @@ import { normalizePath, notImplementedResolver, noTransformers, + ObjectAllocator, + objectAllocator, ObjectLiteralExpression, OperationCanceledException, optionsHaveChanges, @@ -287,6 +287,7 @@ import { SatisfiesExpression, ScriptKind, ScriptTarget, + setObjectAllocator, setParent, setParentRecursive, setResolvedModule, @@ -314,12 +315,16 @@ import { sys, System, targetOptionDeclaration, + ThreadPool, toFileNameLowerCase, tokenToString, - toPath as ts_toPath, trace, tracing, trimStringEnd, + forEachResolvedProjectReference as ts_forEachResolvedProjectReference, + getCommonSourceDirectory as ts_getCommonSourceDirectory, + getDeclarationDiagnostics as ts_getDeclarationDiagnostics, + toPath as ts_toPath, TsConfigSourceFile, TypeChecker, typeDirectiveIsEqualTo, @@ -330,12 +335,19 @@ import { Version, versionMajorMinor, walkUpParenthesizedExpressions, + WorkerThreadsHost, WriteFileCallback, WriteFileCallbackData, writeFileEnsuringDirectories, - zipToModeAwareCache, + zipToModeAwareCache } from "./_namespaces/ts"; import * as performance from "./_namespaces/ts.performance"; +import { SharedMap } from "./sharing/collections/sharedMap"; +import { adoptSharedSourceFile } from "./sharing/sharedNodeAdapter"; +import { SharedParserState, SharedSourceFileEntry } from "./sharing/sharedParserState"; +import { Condition } from "./threading/condition"; +import { SharedLock } from "./threading/sharedLock"; +import { UniqueLock } from "./threading/uniqueLock"; export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string | undefined { return forEachAncestorDirectory(searchPath, ancestor => { @@ -404,7 +416,8 @@ export function createCompilerHost(options: CompilerOptions, setParentNodes?: bo export function createGetSourceFile( readFile: ProgramHost["readFile"], getCompilerOptions: () => CompilerOptions, - setParentNodes: boolean | undefined + setParentNodes: boolean | undefined, + overrideObjectAllocator: ObjectAllocator | undefined, ): CompilerHost["getSourceFile"] { return (fileName, languageVersionOrOptions, onError) => { let text: string | undefined; @@ -420,7 +433,20 @@ export function createGetSourceFile( } text = ""; } - return text !== undefined ? createSourceFile(fileName, text, languageVersionOrOptions, setParentNodes) : undefined; + if (text === undefined) { + return undefined; + } + if (overrideObjectAllocator) { + const savedObjectAllocator = { ...objectAllocator }; + try { + setObjectAllocator(overrideObjectAllocator); + return createSourceFile(fileName, text, languageVersionOrOptions, setParentNodes); + } + finally { + setObjectAllocator(savedObjectAllocator); + } + } + return createSourceFile(fileName, text, languageVersionOrOptions, setParentNodes); }; } @@ -458,9 +484,29 @@ export function createWriteFileMeasuringIO( } /** @internal */ -export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system: System = sys): CompilerHost { +export function createRequestSourceFile(threadPool: ThreadPool, setParentNodes: boolean | undefined): CompilerHost["requestSourceFile"] { + return (parserState, fileName, languageVersionOrOptions, shouldCreateNewSourceFile, setFileVersion = false) => { + using _ = new UniqueLock(parserState.sharedMutex); + if (!SharedMap.has(parserState.files, fileName)) { + const entry = new SharedSourceFileEntry( + parserState, + setFileVersion, + !!setParentNodes, + fileName, + typeof languageVersionOrOptions === "object" ? languageVersionOrOptions.languageVersion : languageVersionOrOptions, + typeof languageVersionOrOptions === "object" ? languageVersionOrOptions.impliedNodeFormat : undefined, + shouldCreateNewSourceFile); + SharedMap.set(parserState.files, fileName, entry); + threadPool.queueWorkItem("Program.requestSourceFile", entry); + } + }; +} + +/** @internal */ +export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system: System = sys, workerThreads?: WorkerThreadsHost, threadPool?: ThreadPool, overideObjectAllocator?: ObjectAllocator): CompilerHost { const existingDirectories = new Map(); const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames); + function directoryExists(directoryPath: string): boolean { if (existingDirectories.has(directoryPath)) { return true; @@ -479,7 +525,8 @@ export function createCompilerHostWorker(options: CompilerOptions, setParentNode const newLine = getNewLineCharacter(options); const realpath = system.realpath && ((path: string) => system.realpath!(path)); const compilerHost: CompilerHost = { - getSourceFile: createGetSourceFile(fileName => compilerHost.readFile(fileName), () => options, setParentNodes), + requestSourceFile: threadPool && createRequestSourceFile(threadPool, setParentNodes), + getSourceFile: createGetSourceFile(fileName => compilerHost.readFile(fileName), () => options, setParentNodes, overideObjectAllocator), getDefaultLibLocation, getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), writeFile: createWriteFileMeasuringIO( @@ -500,7 +547,9 @@ export function createCompilerHostWorker(options: CompilerOptions, setParentNode realpath, readDirectory: (path, extensions, include, exclude, depth) => system.readDirectory(path, extensions, include, exclude, depth), createDirectory: d => system.createDirectory(d), - createHash: maybeBind(system, system.createHash) + createHash: maybeBind(system, system.createHash), + workerThreads, + threadPool, }; return compilerHost; } @@ -1520,7 +1569,6 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // As all these operations happen - and are nested - within the createProgram call, they close over the below variables. // The current resolution depth is tracked by incrementing/decrementing as the depth first search progresses. const maxNodeModuleJsDepth = typeof options.maxNodeModuleJsDepth === "number" ? options.maxNodeModuleJsDepth : 0; - let currentNodeModulesDepth = 0; // If a module has some of its imports skipped due to being at the depth limit under node_modules, then track // this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed. @@ -1533,6 +1581,9 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg performance.mark("beforeProgram"); const host = createProgramOptions.host || createCompilerHost(options); + const parserState = getMaxCpuCount(options) > 1 && host.threadPool && host.requestSourceFile && new SharedParserState(); + + const parseInParallel = getMaxCpuCount(options) > 0 && !!host.workerThreads && typeof SharedStructType === "function"; const configParsingHost = parseConfigHostFromCompilerHostLike(host); let skipDefaultLib = options.noLib; @@ -1547,6 +1598,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg const currentDirectory = host.getCurrentDirectory(); const supportedExtensions = getSupportedExtensions(options); const supportedExtensionsWithJsonIfResolveJsonModule = getSupportedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions); + const queue: ParserTask[] = []; // Map storing if there is emit blocking diagnostics for given input const hasEmitBlockingDiagnostics = new Map(); @@ -1662,7 +1714,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg * - false if sourceFile missing for source of project reference redirect * - undefined otherwise */ - const filesByName = new Map(); + let filesByName = new Map(); let missingFilePaths: readonly Path[] | undefined; // stores 'filename -> file association' ignoring case // used to track cases when two file names differ only in casing @@ -1696,6 +1748,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg tracing?.push(tracing.Phase.Program, "tryReuseStructureFromOldProgram", {}); structureIsReused = tryReuseStructureFromOldProgram(); tracing?.pop(); + if (structureIsReused !== StructureIsReused.Completely) { processingDefaultLibFiles = []; processingOtherFiles = []; @@ -1704,36 +1757,40 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg if (!resolvedProjectReferences) { resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile); } - if (rootNames.length) { - resolvedProjectReferences?.forEach((parsedRef, index) => { - if (!parsedRef) return; + if (rootNames.length && resolvedProjectReferences) { + for (let index = 0; index < resolvedProjectReferences.length; index++) { + const parsedRef = resolvedProjectReferences[index]; + if (!parsedRef) continue; const out = outFile(parsedRef.commandLine.options); if (useSourceOfProjectReferenceRedirect) { if (out || getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) { for (const fileName of parsedRef.commandLine.fileNames) { - processProjectReferenceFile(fileName, { kind: FileIncludeKind.SourceFromProjectReference, index }); + exec(enqueue(processProjectReferenceFile(fileName, { kind: FileIncludeKind.SourceFromProjectReference, index }, /*currentNodeModulesDepth*/ 0))); } } } else { if (out) { - processProjectReferenceFile(changeExtension(out, ".d.ts"), { kind: FileIncludeKind.OutputFromProjectReference, index }); + exec(enqueue(processProjectReferenceFile(changeExtension(out, ".d.ts"), { kind: FileIncludeKind.OutputFromProjectReference, index }, /*currentNodeModulesDepth*/ 0))); } else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) { const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(parsedRef.commandLine, !host.useCaseSensitiveFileNames())); for (const fileName of parsedRef.commandLine.fileNames) { if (!isDeclarationFileName(fileName) && !fileExtensionIs(fileName, Extension.Json)) { - processProjectReferenceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory), { kind: FileIncludeKind.OutputFromProjectReference, index }); + exec(enqueue(processProjectReferenceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory), { kind: FileIncludeKind.OutputFromProjectReference, index }, /*currentNodeModulesDepth*/ 0))); } } } } - }); + } } } tracing?.push(tracing.Phase.Program, "processRootFiles", { count: rootNames.length }); - forEach(rootNames, (name, index) => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.RootFile, index })); + for (let index = 0; index < rootNames.length; index++) { + const name = rootNames[index]; + exec(enqueue(processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.RootFile, index }, /*currentNodeModulesDepth*/ 0))); + } tracing?.pop(); // load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders @@ -1748,7 +1805,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg for (let i = 0; i < automaticTypeDirectiveNames.length; i++) { // under node16/nodenext module resolution, load `types`/ata include names as cjs resolution results by passing an `undefined` mode automaticTypeDirectiveResolutions.set(automaticTypeDirectiveNames[i], /*mode*/ undefined, resolutions[i]); - processTypeReferenceDirective( + exec(enqueue(processTypeReferenceDirective( automaticTypeDirectiveNames[i], /*mode*/ undefined, resolutions[i], @@ -1757,11 +1814,15 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg typeReference: automaticTypeDirectiveNames[i], packageId: resolutions[i]?.resolvedTypeReferenceDirective?.packageId, }, - ); + /*currentNodeModulesDepth*/ 0 + ))); } tracing?.pop(); } + // we need to finish parsing all pending files before we can parse default libs, so process the queue. + join(); + // Do not process the default library if: // - The '--noLib' flag is used. // - A 'no-default-lib' reference comment is encountered in @@ -1771,13 +1832,21 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // otherwise, using options specified in '--lib' instead of '--target' default library file const defaultLibraryFileName = getDefaultLibraryFileName(); if (!options.lib && defaultLibraryFileName) { - processRootFile(defaultLibraryFileName, /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile }); + exec(enqueue(processRootFile(defaultLibraryFileName, /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile }, /*currentNodeModulesDepth*/ 0))); } - else { - forEach(options.lib, (libFileName, index) => { - processRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile, index }); - }); + else if (options.lib) { + for (let index = 0; index < options.lib.length; index++) { + const libFileName = options.lib[index]; + exec(enqueue(processRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile, index }, /*currentNodeModulesDepth*/ 0))); + } } + + // wait for all default libs to complete + join(); + } + + if (parseInParallel) { + fixFileOrder(); } missingFilePaths = arrayFrom(mapDefinedIterator(filesByName.entries(), ([path, file]) => file === undefined ? path as Path : undefined)); @@ -1892,6 +1961,9 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg getFileIncludeReasons: () => fileReasons, structureIsReused, writeFile, + + workerThreads: host.workerThreads, + threadPool: host.threadPool, }; onProgramCreateComplete(); @@ -2691,7 +2763,22 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } function getTypeChecker() { - return typeChecker || (typeChecker = createTypeChecker(program)); + if (!typeChecker) { + // if (host.threadPool) { + // const savedObjectAllocator = { ...objectAllocator }; + // setObjectAllocator(getSharedObjectAllocator()); + // try { + // typeChecker = createTypeChecker(program); + // } + // finally { + // setObjectAllocator(savedObjectAllocator); + // } + // } + // else { + typeChecker = createTypeChecker(program); + // } + } + return typeChecker; } function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnly?: boolean | EmitOnly, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult { @@ -2738,6 +2825,10 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return emitResult; } + function* getSourceFileGen(fileName: string): Generator { // eslint-disable-line require-yield + return getSourceFile(fileName); + } + function getSourceFile(fileName: string): SourceFile | undefined { return getSourceFileByPath(toPath(fileName)); } @@ -2892,7 +2983,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg * Creates a map of comment directives along with the diagnostics immediately preceded by one of them. * Comments that match to any of those diagnostics are marked as used. */ - function getDiagnosticsWithPrecedingDirectives(sourceFile: SourceFile, commentDirectives: CommentDirective[], flatDiagnostics: Diagnostic[]) { + function getDiagnosticsWithPrecedingDirectives(sourceFile: SourceFile, commentDirectives: readonly CommentDirective[], flatDiagnostics: Diagnostic[]) { // Diagnostics are only reported if there is no comment directive preceding them // This will modify the directives map by marking "used" ones with a corresponding diagnostic const directives = createCommentDirectivesMap(sourceFile, commentDirectives); @@ -3251,8 +3342,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return configFileParsingDiagnostics || emptyArray; } - function processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason) { - processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined, reason); + function* processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, currentNodeModulesDepth: number) { + yield* processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined, reason, currentNodeModulesDepth); } function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean { @@ -3307,7 +3398,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } for (const node of file.statements) { - collectModuleReferences(node, /*inAmbientModule*/ false); + collectModuleReferences(node, /*inAmbientModule*/ false, /*currentNodeModulesDepth*/ 0); } const shouldProcessRequires = isJavaScriptFile && shouldResolveJsRequire(options); @@ -3321,7 +3412,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return; - function collectModuleReferences(node: Statement, inAmbientModule: boolean): void { + function collectModuleReferences(node: Statement, inAmbientModule: boolean, currentNodeModulesDepth: number): void { if (isAnyImportOrReExport(node)) { const moduleNameExpr = getExternalModuleName(node); // TypeScript 1.0 spec (April 2014): 12.1.6 @@ -3361,7 +3452,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg const body = (node as ModuleDeclaration).body as ModuleBlock; if (body) { for (const statement of body.statements) { - collectModuleReferences(statement, /*inAmbientModule*/ true); + collectModuleReferences(statement, /*inAmbientModule*/ true, currentNodeModulesDepth); } } } @@ -3415,74 +3506,89 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg /** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */ function getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined { - return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), getSourceFile); + return exec(getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), getSourceFileGen)); } - function getSourceFileFromReferenceWorker( + function* getSourceFileFromReferenceWorker( fileName: string, - getSourceFile: (fileName: string) => SourceFile | undefined, + getSourceFile: (fileName: string) => Generator, fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void, - reason?: FileIncludeReason): SourceFile | undefined { + reason?: FileIncludeReason): Generator { if (hasExtension(fileName)) { const canonicalFileName = host.getCanonicalFileName(fileName); if (!options.allowNonTsExtensions && !forEach(flatten(supportedExtensionsWithJsonIfResolveJsonModule), extension => fileExtensionIs(canonicalFileName, extension))) { if (fail) { if (hasJSFileExtension(canonicalFileName)) { + yield; // yield before recording any state changes fail(Diagnostics.File_0_is_a_JavaScript_file_Did_you_mean_to_enable_the_allowJs_option, fileName); } else { + yield; // yield before recording any state changes fail(Diagnostics.File_0_has_an_unsupported_extension_The_only_supported_extensions_are_1, fileName, "'" + flatten(supportedExtensions).join("', '") + "'"); } } return undefined; } - const sourceFile = getSourceFile(fileName); + const sourceFile = yield* getSourceFile(fileName); if (fail) { if (!sourceFile) { const redirect = getProjectReferenceRedirect(fileName); if (redirect) { + yield; // yield before recording any state changes fail(Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, redirect, fileName); } else { + yield; // yield before recording any state changes fail(Diagnostics.File_0_not_found, fileName); } } else if (isReferencedFile(reason) && canonicalFileName === host.getCanonicalFileName(getSourceFileByPath(reason.file)!.fileName)) { + yield; // yield before recording any state changes fail(Diagnostics.A_file_cannot_have_a_reference_to_itself); } } return sourceFile; } else { - const sourceFileNoExtension = options.allowNonTsExtensions && getSourceFile(fileName); + const sourceFileNoExtension = options.allowNonTsExtensions && (yield* getSourceFile(fileName)); if (sourceFileNoExtension) return sourceFileNoExtension; if (fail && options.allowNonTsExtensions) { + yield; // yield before recording any state changes fail(Diagnostics.File_0_not_found, fileName); return undefined; } // Only try adding extensions from the first supported group (which should be .ts/.tsx/.d.ts) - const sourceFileWithAddedExtension = forEach(supportedExtensions[0], extension => getSourceFile(fileName + extension)); - if (fail && !sourceFileWithAddedExtension) fail(Diagnostics.Could_not_resolve_the_path_0_with_the_extensions_Colon_1, fileName, "'" + flatten(supportedExtensions).join("', '") + "'"); + let sourceFileWithAddedExtension; + for (const extension of supportedExtensions[0]) { + sourceFileWithAddedExtension = yield* getSourceFile(fileName + extension); + if (sourceFileWithAddedExtension) { + break; + } + } + if (fail && !sourceFileWithAddedExtension) { + yield; // yield before recording any state changes + fail(Diagnostics.Could_not_resolve_the_path_0_with_the_extensions_Colon_1, fileName, "'" + flatten(supportedExtensions).join("', '") + "'"); + } return sourceFileWithAddedExtension; } } /** This has side effects through `findSourceFile`. */ - function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, reason: FileIncludeReason): void { - getSourceFileFromReferenceWorker( + function* processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, reason: FileIncludeReason, currentNodeModulesDepth: number) { + yield* getSourceFileFromReferenceWorker( fileName, - fileName => findSourceFile(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId), // TODO: GH#18217 + fileName => findSourceFile(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId, currentNodeModulesDepth), // TODO: GH#18217 (diagnostic, ...args) => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, diagnostic, args), reason ); } - function processProjectReferenceFile(fileName: string, reason: ProjectReferenceFile) { - return processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, reason); + function* processProjectReferenceFile(fileName: string, reason: ProjectReferenceFile, currentNodeModulesDepth: number) { + return yield* processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, reason, currentNodeModulesDepth); } function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFile: SourceFile, reason: FileIncludeReason): void { @@ -3495,7 +3601,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } } - function createRedirectedSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path, resolvedPath: Path, originalFileName: string, sourceFileOptions: CreateSourceFileOptions): SourceFile { + function createRedirectedSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path, resolvedPath: Path, originalFileName: string, sourceFileOptions: CreateSourceFileOptions, currentNodeModulesDepth: number): SourceFile { const redirect = parseNodeFactory.createRedirectedSourceFile({ redirectTarget, unredirected }); redirect.fileName = fileName; redirect.path = path; @@ -3508,13 +3614,13 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } // Get source file from normalized fileName - function findSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined { + function* findSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined, currentNodeModulesDepth: number): Generator { tracing?.push(tracing.Phase.Program, "findSourceFile", { fileName, isDefaultLib: isDefaultLib || undefined, fileIncludeKind: (FileIncludeKind as any)[reason.kind], }); - const result = findSourceFileWorker(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId); + const result = yield* findSourceFileWorker(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId, currentNodeModulesDepth); tracing?.pop(); return result; } @@ -3531,7 +3637,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg { languageVersion, impliedNodeFormat: result, setExternalModuleIndicator }; } - function findSourceFileWorker(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined { + function* findSourceFileWorker(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined, currentNodeModulesDepth: number): Generator { const path = toPath(fileName); if (useSourceOfProjectReferenceRedirect) { let source = getSourceOfProjectReferenceRedirect(path); @@ -3549,15 +3655,21 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } if (source) { const file = isString(source) ? - findSourceFile(source, isDefaultLib, ignoreNoDefaultLib, reason, packageId) : + yield* findSourceFile(source, isDefaultLib, ignoreNoDefaultLib, reason, packageId, currentNodeModulesDepth) : undefined; if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined); return file; } } const originalFileName = fileName; + + // Request a source file from the host and then yield. This allows us to spin up a number of requests to parse + // source files in background threads in parallel, and we will resume once all parallel requests have started. + yield* requestFile(fileName); + if (filesByName.has(path)) { const file = filesByName.get(path); + addFileIncludeReason(file || undefined, reason); // try to check if we've already seen this file but with a different casing in path // NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected @@ -3580,24 +3692,25 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth === 0) { sourceFilesFoundSearchingNodeModules.set(file.path, false); if (!options.noResolve) { - processReferencedFiles(file, isDefaultLib); - processTypeReferenceDirectives(file); + yield* processReferencedFiles(file, isDefaultLib, currentNodeModulesDepth); + yield* processTypeReferenceDirectives(file, currentNodeModulesDepth); } if (!options.noLib) { - processLibReferenceDirectives(file); + yield* processLibReferenceDirectives(file, currentNodeModulesDepth); } - modulesWithElidedImports.set(file.path, false); - processImportedModules(file); + yield* processImportedModules(file, currentNodeModulesDepth); } // See if we need to reprocess the imports due to prior skipped imports else if (file && modulesWithElidedImports.get(file.path)) { if (currentNodeModulesDepth < maxNodeModuleJsDepth) { modulesWithElidedImports.set(file.path, false); - processImportedModules(file); + yield* processImportedModules(file, currentNodeModulesDepth); } } + yield; + return file || undefined; } @@ -3622,12 +3735,39 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // We haven't looked for this file, do so now and cache result const sourceFileOptions = getCreateSourceFileOptions(fileName, moduleResolutionCache, host, options); - const file = host.getSourceFile( - fileName, - sourceFileOptions, - hostErrorMessage => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_read_file_0_Colon_1, [fileName, hostErrorMessage]), - shouldCreateNewSourceFile, - ); + + let fileParsedInBackground = false; + let file: SourceFile | undefined; + if (parserState) { + let sharedFileEntry: SharedSourceFileEntry | undefined; + { + using _ = new SharedLock(parserState.sharedMutex); + sharedFileEntry = SharedMap.get(parserState.files, fileName); + } + if (sharedFileEntry) { + using lck = new UniqueLock(sharedFileEntry.fileMutex); + Condition.wait(sharedFileEntry.fileCondition, lck, () => sharedFileEntry!.done || sharedFileEntry!.error); + if (sharedFileEntry.error) { + host.threadPool?.abort(); + sys.exit(-1); + } + file = sharedFileEntry.file && adoptSharedSourceFile(sharedFileEntry.file, parseNodeFactory); + if (file) { + file.bindDiagnostics = []; + } + fileParsedInBackground = true; + } + } + + if (!fileParsedInBackground) { + file = host.getSourceFile( + fileName, + sourceFileOptions, + hostErrorMessage => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_read_file_0_Colon_1, [fileName, hostErrorMessage]), + shouldCreateNewSourceFile, + ); + } + if (packageId) { const packageIdKey = packageIdToString(packageId); @@ -3635,7 +3775,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg if (fileFromPackageId) { // Some other SourceFile already exists with this package name and version. // Instead of creating a duplicate, just redirect to the existing one. - const dupFile = createRedirectedSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName, sourceFileOptions); + const dupFile = createRedirectedSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName, sourceFileOptions, currentNodeModulesDepth); redirectTargetsMap.add(fileFromPackageId.path, fileName); addFileToFilesByName(dupFile, path, redirectedPath); addFileIncludeReason(dupFile, reason); @@ -3676,16 +3816,21 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg skipDefaultLib = skipDefaultLib || (file.hasNoDefaultLib && !ignoreNoDefaultLib); if (!options.noResolve) { - processReferencedFiles(file, isDefaultLib); - processTypeReferenceDirectives(file); + yield* processReferencedFiles(file, isDefaultLib, currentNodeModulesDepth); + // yield* processTypeReferenceDirectives(file, currentNodeModulesDepth); } if (!options.noLib) { - processLibReferenceDirectives(file); + yield* processLibReferenceDirectives(file, currentNodeModulesDepth); } - // always process imported modules to record module name resolutions - processImportedModules(file); + yield* processImportedModules(file, currentNodeModulesDepth); + + if (!options.noResolve) { + yield* processTypeReferenceDirectives(file, currentNodeModulesDepth); + } + + yield; if (isDefaultLib) { processingDefaultLibFiles!.push(file); @@ -3797,19 +3942,21 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return projectReferenceRedirects.get(projectReferencePath) || undefined; } - function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) { - forEach(file.referencedFiles, (ref, index) => { - processSourceFile( + function* processReferencedFiles(file: SourceFile, isDefaultLib: boolean, currentNodeModulesDepth: number) { + for (let index = 0; index < file.referencedFiles.length; index++) { + const ref = file.referencedFiles[index]; + yield* enqueue(processSourceFile( resolveTripleslashReference(ref.fileName, file.fileName), isDefaultLib, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, - { kind: FileIncludeKind.ReferenceFile, file: file.path, index, } - ); - }); + { kind: FileIncludeKind.ReferenceFile, file: file.path, index, }, + currentNodeModulesDepth + )); + } } - function processTypeReferenceDirectives(file: SourceFile) { + function* processTypeReferenceDirectives(file: SourceFile, currentNodeModulesDepth: number) { const typeDirectives = file.typeReferenceDirectives; if (!typeDirectives.length) { file.resolvedTypeReferenceDirectiveNames = undefined; @@ -3824,7 +3971,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg const fileName = toFileNameLowerCase(ref.fileName); setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective, getModeForFileReference(ref, file.impliedNodeFormat)); const mode = ref.resolutionMode || file.impliedNodeFormat; - if (mode && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Node16 && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeNext) { + + // TODO(rbuckton): temporary join until I fix queuing here + yield; + const reportError = mode && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Node16 && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeNext; + if (reportError) { (fileProcessingDiagnostics ??= []).push({ kind: FilePreprocessingDiagnosticsKind.ResolutionDiagnostics, diagnostics: [ @@ -3832,33 +3983,47 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg ] }); } - processTypeReferenceDirective(fileName, mode, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, }); + yield* processTypeReferenceDirective(fileName, mode, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, }, currentNodeModulesDepth); } } - function processTypeReferenceDirective( + function* processTypeReferenceDirective( typeReferenceDirective: string, mode: ResolutionMode, resolution: ResolvedTypeReferenceDirectiveWithFailedLookupLocations, - reason: FileIncludeReason - ): void { + reason: FileIncludeReason, + currentNodeModulesDepth: number + ) { tracing?.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolution.resolvedTypeReferenceDirective, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined }); - processTypeReferenceDirectiveWorker(typeReferenceDirective, mode, resolution, reason); + yield* processTypeReferenceDirectiveWorker(typeReferenceDirective, mode, resolution, reason, currentNodeModulesDepth); tracing?.pop(); } - function processTypeReferenceDirectiveWorker( + function* processTypeReferenceDirectiveWorker( typeReferenceDirective: string, mode: ResolutionMode, resolution: ResolvedTypeReferenceDirectiveWithFailedLookupLocations, - reason: FileIncludeReason - ): void { - addResolutionDiagnostics(resolution); + reason: FileIncludeReason, + currentNodeModulesDepth: number + ) { + let hasRecordedResolutionDiagnostics = false; + function* recordResolutionDiagnosticsIfNeeded(needsYield = true) { + if (!hasRecordedResolutionDiagnostics) { + hasRecordedResolutionDiagnostics = true; + if (needsYield) { + yield; + } + addResolutionDiagnostics(resolution); + } + } + // If we already found this library as a primary reference - nothing to do const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective, mode)?.resolvedTypeReferenceDirective; if (previousResolution && previousResolution.primary) { + yield* recordResolutionDiagnosticsIfNeeded(); return; } + let saveResolution = true; const { resolvedTypeReferenceDirective } = resolution; if (resolvedTypeReferenceDirective) { @@ -3866,7 +4031,13 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg if (resolvedTypeReferenceDirective.primary) { // resolved from the primary path - processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); // TODO: GH#18217 + const gen = processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason, currentNodeModulesDepth); // TODO: GH#18217 + // start the next task, then yield resolution diagnostics before continuing. + const result = gen.next(); + if (!result.done) { + yield* recordResolutionDiagnosticsIfNeeded(/*needsYield*/ false); + yield* gen; + } } else { // If we already resolved to this file, it must have been a secondary reference. Check file contents @@ -3877,6 +4048,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName!); const existingFile = getSourceFile(previousResolution.resolvedFileName!)!; if (otherFileText !== existingFile.text) { + yield* recordResolutionDiagnosticsIfNeeded(); addFilePreprocessingFileExplainingDiagnostic( existingFile, reason, @@ -3890,17 +4062,25 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } else { // First resolution of this library - processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); + const gen = processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason, currentNodeModulesDepth); + // start the next task, then yield resolution diagnostics before continuing. + const result = gen.next(); + if (!result.done) { + yield* recordResolutionDiagnosticsIfNeeded(/*needsYield*/ false); + yield* gen; + } } } if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--; } else { + yield* recordResolutionDiagnosticsIfNeeded(); addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_find_type_definition_file_for_0, [typeReferenceDirective]); } if (saveResolution) { + yield* recordResolutionDiagnosticsIfNeeded(); resolvedTypeReferenceDirectives.set(typeReferenceDirective, mode, resolution); } } @@ -3958,33 +4138,37 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return result; } - function processLibReferenceDirectives(file: SourceFile) { - forEach(file.libReferenceDirectives, (libReference, index) => { + function* processLibReferenceDirectives(file: SourceFile, currentNodeModulesDepth: number) { + for (let index = 0; index < file.libReferenceDirectives.length; index++) { + const libReference = file.libReferenceDirectives[index]; const { libName, libFileName } = getLibFileNameFromLibReference(libReference); if (libFileName) { // we ignore any 'no-default-lib' reference set on this file. - processRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, }); + yield* enqueue(processRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, }, currentNodeModulesDepth)); } else { const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts"); const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity); const diagnostic = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0; const args = suggestion ? [libName, suggestion] : [libName]; - (fileProcessingDiagnostics ||= []).push({ - kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic, - reason: { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, }, - diagnostic, - args, - }); + yield* enqueue(function* (): ParserTask { + yield; + (fileProcessingDiagnostics ||= []).push({ + kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic, + reason: { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, }, + diagnostic, + args, + }); + }()); } - }); + } } function getCanonicalFileName(fileName: string): string { return host.getCanonicalFileName(fileName); } - function processImportedModules(file: SourceFile) { + function* processImportedModules(file: SourceFile, currentNodeModulesDepth: number): ParserTask { collectExternalModuleReferences(file); if (file.imports.length || file.moduleAugmentations.length) { // Because global augmentation doesn't have string literal name, we can check for global augmentation as such. @@ -3993,57 +4177,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg Debug.assert(resolutions.length === moduleNames.length); const optionsForFile = (useSourceOfProjectReferenceRedirect ? getRedirectReferenceForResolution(file)?.commandLine.options : undefined) || options; for (let index = 0; index < moduleNames.length; index++) { - const resolution = resolutions[index].resolvedModule; - const moduleName = moduleNames[index].text; - const mode = getModeForUsageLocation(file, moduleNames[index]); - setResolvedModule(file, moduleName, resolutions[index], mode); - addResolutionDiagnosticsFromResolutionOrCache(file, moduleName, resolutions[index], mode); - - if (!resolution) { - continue; - } - - const isFromNodeModulesSearch = resolution.isExternalLibraryImport; - const isJsFile = !resolutionExtensionIsTSOrJson(resolution.extension); - const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile; - const resolvedFileName = resolution.resolvedFileName; - - if (isFromNodeModulesSearch) { - currentNodeModulesDepth++; - } - - // add file to program only if: - // - resolution was successful - // - noResolve is falsy - // - module name comes from the list of imports - // - it's not a top level JavaScript module that exceeded the search max - const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth; - // Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs') - // This may still end up being an untyped module -- the file won't be included but imports will be allowed. - const shouldAddFile = resolvedFileName - && !getResolutionDiagnostic(optionsForFile, resolution, file) - && !optionsForFile.noResolve - && index < file.imports.length - && !elideImport - && !(isJsFile && !getAllowJSCompilerOption(optionsForFile)) - && (isInJSFile(file.imports[index]) || !(file.imports[index].flags & NodeFlags.JSDoc)); - - if (elideImport) { - modulesWithElidedImports.set(file.path, true); - } - else if (shouldAddFile) { - findSourceFile( - resolvedFileName, - /*isDefaultLib*/ false, - /*ignoreNoDefaultLib*/ false, - { kind: FileIncludeKind.Import, file: file.path, index, }, - resolution.packageId, - ); - } - - if (isFromNodeModulesSearch) { - currentNodeModulesDepth--; - } + yield* enqueue(processImportedModule(file, moduleNames, resolutions, index, optionsForFile, currentNodeModulesDepth)); } } else { @@ -4052,6 +4186,76 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } } + function* processImportedModule(file: SourceFile, moduleNames: readonly StringLiteralLike[], resolutions: readonly ResolvedModuleWithFailedLookupLocations[], index: number, optionsForFile: CompilerOptions, currentNodeModulesDepth: number) { + const resolution = resolutions[index].resolvedModule; + const moduleName = moduleNames[index].text; + const mode = getModeForUsageLocation(file, moduleNames[index]); + + if (!resolution) { + yield; // wait for prior operations to complete before performing updates + setResolvedModule(file, moduleName, resolutions[index], mode); + addResolutionDiagnosticsFromResolutionOrCache(file, moduleName, resolutions[index], mode); + return; + } + + const isFromNodeModulesSearch = resolution.isExternalLibraryImport; + const isJsFile = !resolutionExtensionIsTSOrJson(resolution.extension); + const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile; + const resolvedFileName = resolution.resolvedFileName; + + if (isFromNodeModulesSearch) { + currentNodeModulesDepth++; + } + + // add file to program only if: + // - resolution was successful + // - noResolve is falsy + // - module name comes from the list of imports + // - it's not a top level JavaScript module that exceeded the search max + const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth; + // Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs') + // This may still end up being an untyped module -- the file won't be included but imports will be allowed. + const shouldAddFile = resolvedFileName + && !getResolutionDiagnostic(optionsForFile, resolution, file) + && !optionsForFile.noResolve + && index < file.imports.length + && !elideImport + && !(isJsFile && !getAllowJSCompilerOption(optionsForFile)) + && (isInJSFile(file.imports[index]) || !(file.imports[index].flags & NodeFlags.JSDoc)); + + if (elideImport) { + yield; // wait for prior operations to complete before performing updates + setResolvedModule(file, moduleName, resolutions[index], mode); + addResolutionDiagnosticsFromResolutionOrCache(file, moduleName, resolutions[index], mode); + modulesWithElidedImports.set(file.path, true); + } + else if (shouldAddFile) { + const gen = findSourceFile( + resolvedFileName, + /*isDefaultLib*/ false, + /*ignoreNoDefaultLib*/ false, + { kind: FileIncludeKind.Import, file: file.path, index, }, + resolution.packageId, + currentNodeModulesDepth + ); + // wait for the file request to yield before performing updates + const result = gen.next(); + if (!result.done) { + yield; + } + + setResolvedModule(file, moduleName, resolutions[index], mode); + addResolutionDiagnosticsFromResolutionOrCache(file, moduleName, resolutions[index], mode); + if (!result.done) { + yield* gen; + } + } + + if (isFromNodeModulesSearch) { + currentNodeModulesDepth--; + } + } + function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean { let allFilesBelongToPath = true; const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory)); @@ -4945,8 +5149,433 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } return symlinks; } + + function* requestFile(fileName: string) { + if (parserState && host.requestSourceFile) { + host.requestSourceFile?.(parserState, fileName, getEmitScriptTarget(options)); + yield; + } + } + + function* enqueue(gen: ParserTask): ParserTask { + if (parseInParallel) { + const result = gen.next(); + if (!result.done) { + queue.push(gen); + } + } + else { + return yield* gen; + } + } + + function join() { + while (queue.length) { + const startingFileCount = filesByName.size; + const chunk = queue.splice(0, queue.length); + for (const gen of chunk) { + exec(gen); + } + const newFileCount = filesByName.size - startingFileCount; + Debug.log(`join() processed ${chunk.length} items and parsed ${newFileCount} file(s) in this chunk`); + } + } + + function fixFileOrder() { + // fix up the insertion order for `processingOtherFiles`, `processingDefaultLibFiles`, `filesByName`, etc. + // to match the expected order when parsing single-threaded. The algorithm here is intended to mirror + // the algorithm steps of `createProgram`. + // TODO(rbuckton): There are better ways to do this, as this could quickly beocme out of sync as we make changes + // to the file processing algorithm above. For now this is just a quick hack to unblock other + // work. + + const fixedFilesByName = new Map(); + const fixedProcessingDefaultLibFiles: SourceFile[] = []; + const fixedProcessingOtherFiles: SourceFile[] = []; + + const sourceFilesFoundSearchingNodeModules = new Map(); + const resolvedTypeReferenceDirectives = createModeAwareCache(); + const packageIdToSourceFile = new Map(); + + if (rootNames.length && resolvedProjectReferences) { + for (let index = 0; index < resolvedProjectReferences.length; index++) { + const parsedRef = resolvedProjectReferences[index]; + if (!parsedRef) continue; + const out = outFile(parsedRef.commandLine.options); + if (useSourceOfProjectReferenceRedirect) { + if (out || getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) { + for (const fileName of parsedRef.commandLine.fileNames) { + fixOrderOfProjectReferenceFile(fileName, { kind: FileIncludeKind.SourceFromProjectReference, index }, /*currentNodeModulesDepth*/ 0); + } + } + } + else { + if (out) { + fixOrderOfProjectReferenceFile(changeExtension(out, ".d.ts"), { kind: FileIncludeKind.OutputFromProjectReference, index }, /*currentNodeModulesDepth*/ 0); + } + else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) { + const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(parsedRef.commandLine, !host.useCaseSensitiveFileNames())); + for (const fileName of parsedRef.commandLine.fileNames) { + if (!isDeclarationFileName(fileName) && !fileExtensionIs(fileName, Extension.Json)) { + fixOrderOfProjectReferenceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory), { kind: FileIncludeKind.OutputFromProjectReference, index }, /*currentNodeModulesDepth*/ 0); + } + } + } + } + } + } + + for (let index = 0; index < rootNames.length; index++) { + const name = rootNames[index]; + fixOrderOfRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.RootFile, index }, /*currentNodeModulesDepth*/ 0); + } + + if (automaticTypeDirectiveNames?.length) { + for (let i = 0; i < automaticTypeDirectiveNames.length; i++) { + const resolution = automaticTypeDirectiveResolutions.get(automaticTypeDirectiveNames[i], /*mode*/ undefined)!; + fixOrderOfTypeReferenceDirective( + automaticTypeDirectiveNames[i], + /*mode*/ undefined, + resolution, + { + kind: FileIncludeKind.AutomaticTypeDirectiveFile, + typeReference: automaticTypeDirectiveNames[i], + packageId: resolution?.resolvedTypeReferenceDirective?.packageId, + }, + /*currentNodeModulesDepth*/ 0 + ); + } + } + + if (rootNames.length && !skipDefaultLib) { + const defaultLibraryFileName = getDefaultLibraryFileName(); + if (!options.lib && defaultLibraryFileName) { + fixOrderOfRootFile(defaultLibraryFileName, /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile }, /*currentNodeModulesDepth*/ 0); + } + else if (options.lib) { + for (let index = 0; index < options.lib.length; index++) { + const libFileName = options.lib[index]; + fixOrderOfRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile, index }, /*currentNodeModulesDepth*/ 0); + } + } + } + + filesByName = fixedFilesByName; + processingDefaultLibFiles = fixedProcessingDefaultLibFiles; + processingOtherFiles = fixedProcessingOtherFiles; + + function fixOrderOfProjectReferenceFile(fileName: string, reason: ProjectReferenceFile, currentNodeModulesDepth: number) { + fixOrderOfSourceFileFromReference(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, reason, currentNodeModulesDepth); + } + + function fixOrderOfRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, currentNodeModulesDepth: number) { + fixOrderOfSourceFileFromReference(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined, reason, currentNodeModulesDepth); + } + + function fixOrderOfSourceFileFromReference(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, reason: FileIncludeReason, currentNodeModulesDepth: number) { + if (hasExtension(fileName)) { + const canonicalFileName = host.getCanonicalFileName(fileName); + if (!options.allowNonTsExtensions && !forEach(flatten(supportedExtensionsWithJsonIfResolveJsonModule), extension => fileExtensionIs(canonicalFileName, extension))) { + return undefined; + } + + return fixOrderOfSourceFile(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId, currentNodeModulesDepth); + } + else { + const sourceFileNoExtension = options.allowNonTsExtensions && fixOrderOfSourceFile(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId, currentNodeModulesDepth); + if (sourceFileNoExtension) return sourceFileNoExtension; + if (options.allowNonTsExtensions) return undefined; + let sourceFileWithAddedExtension; + for (const extension of supportedExtensions[0]) { + sourceFileWithAddedExtension = fixOrderOfSourceFile(fileName + extension, isDefaultLib, ignoreNoDefaultLib, reason, packageId, currentNodeModulesDepth); + if (sourceFileWithAddedExtension) { + break; + } + } + } + } + + function fixOrderOfSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined, currentNodeModulesDepth: number): SourceFile | undefined { + const path = toPath(fileName); + if (useSourceOfProjectReferenceRedirect) { + let source = getSourceOfProjectReferenceRedirect(path); + // If preserveSymlinks is true, module resolution wont jump the symlink + // but the resolved real path may be the .d.ts from project reference + // Note:: Currently we try the real path only if the + // file is from node_modules to avoid having to run real path on all file paths + if (!source && + host.realpath && + options.preserveSymlinks && + isDeclarationFileName(fileName) && + stringContains(fileName, nodeModulesPathPart)) { + const realPath = toPath(host.realpath(fileName)); + if (realPath !== path) source = getSourceOfProjectReferenceRedirect(realPath); + } + if (source) { + const file = isString(source) ? + fixOrderOfSourceFile(source, isDefaultLib, ignoreNoDefaultLib, reason, packageId, currentNodeModulesDepth) : + undefined; + if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined); + return file; + } + } + + if (fixedFilesByName.has(path)) { + const file = fixedFilesByName.get(path); + if (file && !(options.forceConsistentCasingInFileNames === false)) { + const checkedName = file.fileName; + const isRedirect = toPath(checkedName) !== toPath(fileName); + if (isRedirect) { + fileName = getProjectReferenceRedirect(fileName) || fileName; + } + } + + // If the file was previously found via a node_modules search, but is now being processed as a root file, + // then everything it sucks in may also be marked incorrectly, and needs to be checked again. + if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth === 0) { + sourceFilesFoundSearchingNodeModules.set(file.path, false); + if (!options.noResolve) { + fixOrderOfReferencedFiles(file, isDefaultLib, currentNodeModulesDepth); + fixOrderOfTypeReferenceDirectives(file, currentNodeModulesDepth); + } + if (!options.noLib) { + fixOrderOfLibReferenceDirectives(file, currentNodeModulesDepth); + } + modulesWithElidedImports.set(file.path, false); + fixOrderOfImportedModules(file, currentNodeModulesDepth); + } + // See if we need to reprocess the imports due to prior skipped imports + else if (file && modulesWithElidedImports.get(file.path)) { + if (currentNodeModulesDepth < maxNodeModuleJsDepth) { + modulesWithElidedImports.set(file.path, false); + fixOrderOfImportedModules(file, currentNodeModulesDepth); + } + } + + return file || undefined; + } + + let redirectedPath: Path | undefined; + if (isReferencedFile(reason) && !useSourceOfProjectReferenceRedirect) { + const redirectProject = getProjectReferenceRedirectProject(fileName); + if (redirectProject) { + if (outFile(redirectProject.commandLine.options)) { + return undefined; + } + const redirect = getProjectReferenceOutputName(redirectProject, fileName); + fileName = redirect; + redirectedPath = toPath(redirect); + } + } + + let file = filesByName.get(path) || undefined; + + if (packageId) { + const packageIdKey = packageIdToString(packageId); + const fileFromPackageId = packageIdToSourceFile.get(packageIdKey); + if (fileFromPackageId) { + Debug.assert(file); + addFileToFilesByName(file, path, redirectedPath); + fixedProcessingOtherFiles.push(file!); + return file; + } + else if (file) { + packageIdToSourceFile.set(packageIdKey, file); + } + } + addFileToFilesByName(file, path, redirectedPath); + + if (file) { + sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); + + if (!options.noResolve) { + fixOrderOfReferencedFiles(file, isDefaultLib, currentNodeModulesDepth); + fixOrderOfTypeReferenceDirectives(file, currentNodeModulesDepth); + } + if (!options.noLib) { + fixOrderOfLibReferenceDirectives(file, currentNodeModulesDepth); + } + + fixOrderOfImportedModules(file, currentNodeModulesDepth); + + if (!options.noResolve) { + fixOrderOfTypeReferenceDirectives(file, currentNodeModulesDepth); + } + + if (isDefaultLib) { + fixedProcessingDefaultLibFiles.push(file); + } + else { + fixedProcessingOtherFiles.push(file); + } + } + return file; + } + + function fixOrderOfReferencedFiles(file: SourceFile, isDefaultLib: boolean, currentNodeModulesDepth: number) { + for (let index = 0; index < file.referencedFiles.length; index++) { + const ref = file.referencedFiles[index]; + fixOrderOfSourceFileFromReference( + resolveTripleslashReference(ref.fileName, file.fileName), + isDefaultLib, + /*ignoreNoDefaultLib*/ false, + /*packageId*/ undefined, + { kind: FileIncludeKind.ReferenceFile, file: file.path, index, }, + currentNodeModulesDepth + ); + } + } + + function fixOrderOfTypeReferenceDirectives(file: SourceFile, currentNodeModulesDepth: number) { + const typeDirectives = file.typeReferenceDirectives; + if (!typeDirectives.length) { + return; + } + + for (let index = 0; index < typeDirectives.length; index++) { + const ref = file.typeReferenceDirectives[index]; + // store resolved type directive on the file + const fileName = toFileNameLowerCase(ref.fileName); + const resolvedTypeReferenceDirective = file.resolvedTypeReferenceDirectiveNames!.get(fileName, getModeForFileReference(ref, file.impliedNodeFormat))!; + const mode = ref.resolutionMode || file.impliedNodeFormat; + fixOrderOfTypeReferenceDirective(fileName, mode, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, }, currentNodeModulesDepth); + } + } + + function fixOrderOfTypeReferenceDirective( + typeReferenceDirective: string, + mode: ResolutionMode, + resolution: ResolvedTypeReferenceDirectiveWithFailedLookupLocations, + reason: FileIncludeReason, + currentNodeModulesDepth: number + ) { + // If we already found this library as a primary reference - nothing to do + const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective, mode)?.resolvedTypeReferenceDirective; + if (previousResolution && previousResolution.primary) { + return; + } + + let saveResolution = true; + const { resolvedTypeReferenceDirective } = resolution; + if (resolvedTypeReferenceDirective) { + if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth++; + + if (resolvedTypeReferenceDirective.primary) { + fixOrderOfSourceFileFromReference(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason, currentNodeModulesDepth); // TODO: GH#18217 + } + else { + if (previousResolution) { + saveResolution = false; + } + else { + fixOrderOfSourceFileFromReference(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason, currentNodeModulesDepth); + } + } + + if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--; + } + + if (saveResolution) { + resolvedTypeReferenceDirectives.set(typeReferenceDirective, mode, resolution); + } + } + + function fixOrderOfLibReferenceDirectives(file: SourceFile, currentNodeModulesDepth: number) { + for (let index = 0; index < file.libReferenceDirectives.length; index++) { + const libReference = file.libReferenceDirectives[index]; + const { libFileName } = getLibFileNameFromLibReference(libReference); + if (libFileName) { + // we ignore any 'no-default-lib' reference set on this file. + fixOrderOfRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, }, currentNodeModulesDepth); + } + } + } + + function fixOrderOfImportedModules(file: SourceFile, currentNodeModulesDepth: number) { + collectExternalModuleReferences(file); + if (file.imports.length || file.moduleAugmentations.length) { + // Because global augmentation doesn't have string literal name, we can check for global augmentation as such. + const moduleNames = getModuleNames(file); + const resolutions = resolveModuleNamesReusingOldState(moduleNames, file); + Debug.assert(resolutions.length === moduleNames.length); + const optionsForFile = (useSourceOfProjectReferenceRedirect ? getRedirectReferenceForResolution(file)?.commandLine.options : undefined) || options; + for (let index = 0; index < moduleNames.length; index++) { + fixOrderOfImportedModule(file, resolutions, index, optionsForFile, currentNodeModulesDepth); + } + } + } + + function fixOrderOfImportedModule(file: SourceFile, resolutions: readonly ResolvedModuleWithFailedLookupLocations[], index: number, optionsForFile: CompilerOptions, currentNodeModulesDepth: number) { + const resolution = resolutions[index].resolvedModule; + + if (!resolution) { + return; + } + + const isFromNodeModulesSearch = resolution.isExternalLibraryImport; + const isJsFile = !resolutionExtensionIsTSOrJson(resolution.extension); + const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile; + const resolvedFileName = resolution.resolvedFileName; + + if (isFromNodeModulesSearch) { + currentNodeModulesDepth++; + } + + // add file to program only if: + // - resolution was successful + // - noResolve is falsy + // - module name comes from the list of imports + // - it's not a top level JavaScript module that exceeded the search max + const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth; + // Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs') + // This may still end up being an untyped module -- the file won't be included but imports will be allowed. + const shouldAddFile = resolvedFileName + && !getResolutionDiagnostic(optionsForFile, resolution, file) + && !optionsForFile.noResolve + && index < file.imports.length + && !elideImport + && !(isJsFile && !getAllowJSCompilerOption(optionsForFile)) + && (isInJSFile(file.imports[index]) || !(file.imports[index].flags & NodeFlags.JSDoc)); + + if (shouldAddFile) { + fixOrderOfSourceFile( + resolvedFileName, + /*isDefaultLib*/ false, + /*ignoreNoDefaultLib*/ false, + { kind: FileIncludeKind.Import, file: file.path, index, }, + resolution.packageId, + currentNodeModulesDepth + ); + } + + if (isFromNodeModulesSearch) { + currentNodeModulesDepth--; + } + } + + function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) { + if (redirectedPath) { + fixedFilesByName.set(redirectedPath, file); + fixedFilesByName.set(path, file || false); + } + else { + fixedFilesByName.set(path, file); + } + } + } } +function exec(gen: ParserTask) { + while (true) { + const result = gen.next(); + if (result.done) { + return result.value; + } + } +} + +type ParserTask = Generator; + interface HostForUseSourceOfProjectReferenceRedirect { compilerHost: CompilerHost; getSymlinkCache: () => SymlinkCache; diff --git a/src/compiler/sharing/collections/hashData.ts b/src/compiler/sharing/collections/hashData.ts new file mode 100644 index 00000000000..e91106131d8 --- /dev/null +++ b/src/compiler/sharing/collections/hashData.ts @@ -0,0 +1,356 @@ +import { hasProperty } from "../../core"; +import { Debug } from "../../debug"; +import { sys } from "../../sys"; +import { IdentifiableStruct } from "../structs/identifiableStruct"; +import { Shared, SharedStructBase } from "../structs/sharedStruct"; +import { xxh32string } from "./xxhash32"; + +const seed = sys.stringSeed ?? Math.floor(Math.random() * 0xffffffff) >>> 0; + +function hash(value: Shareable) { + if (value === undefined || value === null) { // eslint-disable-line no-null/no-null -- necessary comparison + return 0; + } + switch (typeof value) { + case "number": + return value >> 0; + case "boolean": + return value ? 1 : 0; + case "string": + return xxh32string(value, seed); + case "object": + if (hasProperty(value, "__hash__")) { + return (value as IdentifiableStruct).__hash__ >> 0; + } + return 0; + default: + Debug.assertNever(value); + } +} + +// Portions of the following are derived from esfx and .NET Core. See ThirdPartyNoticeText.txt in the root of this +// repository for their respective license notices. + +const MAX_INT32 = (2 ** 31) - 1; +const maxPrimeArrayLength = 2146435069; +const hashPrime = 101; + +// Table of prime numbers to use as hash table sizes. +// A typical resize algorithm would pick the smallest prime number in this array +// that is larger than twice the previous capacity. +// Suppose our Hashtable currently has capacity x and enough elements are added +// such that a resize needs to occur. Resizing first computes 2x then finds the +// first prime in the table greater than 2x, i.e. if primes are ordered +// p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n. +// Doubling is important for preserving the asymptotic complexity of the +// hashtable operations such as add. Having a prime guarantees that double +// hashing does not lead to infinite loops. IE, your hash function will be +// h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime. +// We prefer the low computation costs of higher prime numbers over the increased +// memory allocation of a fixed prime number i.e. when right sizing a HashSet. +const primes: readonly number[] = [ + 3, 7, 11, 17, + 23, 29, 37, 47, + 59, 71, 89, 107, + 131, 163, 197, 239, + 293, 353, 431, 521, + 631, 761, 919, 1103, + 1327, 1597, 1931, 2333, + 2801, 3371, 4049, 4861, + 5839, 7013, 8419, 10103, + 12143, 14591, 17519, 21023, + 25229, 30293, 36353, 43627, + 52361, 62851, 75431, 90523, + 108631, 130363, 156437, 187751, + 225307, 270371, 324449, 389357, + 467237, 560689, 672827, 807403, + 968897, 1162687, 1395263, 1674319, + 2009191, 2411033, 2893249, 3471899, + 4166287, 4999559, 5999471, 7199369 +]; + +function isPrime(candidate: number) { + if (candidate & 1) { + const limit = Math.sqrt(candidate) | 0; + for (let divisor = 3; divisor <= limit; divisor += 2) { + if (!(candidate % divisor)) return false; + } + return true; + } + return candidate === 2; +} + +function getPrime(min: number) { + if (min < 0) throw new RangeError(); + for (const prime of primes) { + if (prime >= min) return prime; + } + for (let i = min | 1; i < MAX_INT32; i += 2) { + if (isPrime(i) && (i - 1) % hashPrime) { + return i; + } + } + return min; +} + +function expandPrime(oldSize: number) { + const newSize = 2 * oldSize; + // Allow the hashtables to grow to maximum possible size (~2G elements) before encountering capacity overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if (newSize > maxPrimeArrayLength && maxPrimeArrayLength > oldSize) { + return maxPrimeArrayLength; + } + return getPrime(newSize); +} + +/** @internal */ +@Shared() +export class HashEntry extends SharedStructBase { + @Shared() next = 0; + @Shared() prevEntry: HashEntry | undefined; + @Shared() nextEntry: HashEntry | undefined; + @Shared() skipNextEntry = false; + @Shared() hashCode = 0; + @Shared() key!: K; + @Shared() value!: V; +} + +/** @internal */ +@Shared() +export class HashData extends SharedStructBase { + @Shared() buckets: SharedArray; + @Shared() entries: SharedArray>; + @Shared() freeSize = 0; + @Shared() freeList = -1; + @Shared() size = 0; + @Shared() head: HashEntry; + @Shared() tail: HashEntry; + + constructor(capacity: number) { + super(); + capacity = getPrime(capacity); + this.buckets = HashData.fill(new SharedArray(capacity), 0); + this.entries = new SharedArray(capacity); + this.head = new HashEntry(); + this.tail = this.head; + } + + private static fill(array: SharedArray, value: T) { + for (let i = 0; i < array.length; i++) { + array[i] = value; + } + return array; + } + + private static resizeSharedArray(array: SharedArray, newSize: number) { + if (array.length === newSize) return array; + const newArray = new SharedArray(newSize); + const minSize = Math.min(array.length, newSize); + for (let i = 0; i < minSize; i++) { + newArray[i] = array[i]; + } + return newArray; + } + + private static resizeHashData(hashData: HashData, newSize: number) { + const size = hashData.size; + const buckets = HashData.fill(new SharedArray(newSize), 0); + const entries = HashData.resizeSharedArray(hashData.entries, newSize); + for (let i = 0; i < size; i++) { + const entry = entries[i]; + if (entry && entry.hashCode >= 0) { + const bucket = entry.hashCode % newSize; + // Value in buckets is 1-based + entry.next = buckets[bucket] - 1; + // Value in buckets is 1-based + buckets[bucket] = i + 1; + } + } + hashData.buckets = buckets; + hashData.entries = entries; + } + + static findEntryIndex(hashData: HashData, key: K) { + const hashCode = hash(key) & MAX_INT32; + // Value in _buckets is 1-based + let i = hashData.buckets[hashCode % hashData.buckets.length] - 1; + const length = hashData.entries.length; + while ((i >>> 0) < length) { + const entry = hashData.entries[i]; + if (entry.hashCode === hashCode && entry.key === key) { + break; + } + i = entry.next; + } + return i; + } + + static findEntryValue(hashData: HashData, key: K) { + const index = HashData.findEntryIndex(hashData, key); + return index >= 0 ? hashData.entries[index].value : undefined; + } + + static insertEntry(hashData: HashData, key: K, value: V) { + const hashCode = hash(key) & MAX_INT32; + let bucket = hashCode % hashData.buckets.length; + // Value in _buckets is 1-based + let i = hashData.buckets[bucket] - 1; + while ((i >>> 0) < hashData.entries.length) { + const entry = hashData.entries[i]; + if (entry.hashCode === hashCode && entry.key === key) { + entry.value = value; + return; + } + i = entry.next; + } + let updateFreeList = false; + let index: number; + if (hashData.freeSize > 0) { + index = hashData.freeList; + updateFreeList = true; + hashData.freeSize--; + } + else { + const size = hashData.size; + if (size === hashData.entries.length) { + HashData.resizeHashData(hashData, expandPrime(hashData.size)); + bucket = hashCode % hashData.buckets.length; + } + index = size; + hashData.size = size + 1; + } + const entry = hashData.entries[index] ??= new HashEntry(); + if (updateFreeList) hashData.freeList = entry.next; + entry.hashCode = hashCode; + // Value in _buckets is 1-based + entry.next = hashData.buckets[bucket] - 1; + entry.key = key; + entry.value = value; + entry.skipNextEntry = false; + const tail = hashData.tail; + tail.nextEntry = entry; + entry.prevEntry = tail; + hashData.tail = entry; + // Value in _buckets is 1-based + hashData.buckets[bucket] = index + 1; + } + + static deleteEntry(hashData: HashData, key: K) { + const hashCode = hash(key) & MAX_INT32; + const bucket = hashCode % hashData.buckets.length; + let last = -1; + let entry: HashEntry | undefined; + // Value in _buckets is 1-based + for (let i = hashData.buckets[bucket] - 1; i >= 0; i = entry.next) { + entry = hashData.entries[i]; + if (entry.hashCode === hashCode && entry.key === key) { + if (last < 0) { + // Value in _buckets is 1-based + hashData.buckets[bucket] = entry.next + 1; + } + else { + hashData.entries[last]!.next = entry.next; + } + + const prevEntry = entry.prevEntry!; + prevEntry.nextEntry = entry.nextEntry; + if (prevEntry.nextEntry) { + prevEntry.nextEntry.prevEntry = prevEntry; + } + if (hashData.tail === entry) { + hashData.tail = prevEntry; + } + entry.hashCode = -1; + entry.next = hashData.freeList; + entry.key = undefined!; + entry.value = undefined!; + entry.prevEntry = undefined; + entry.nextEntry = prevEntry; + entry.skipNextEntry = true; + hashData.freeList = i; + hashData.freeSize++; + return true; + } + last = i; + } + return false; + } + + static clearEntries(hashData: HashData) { + const size = hashData.size; + if (size > 0) { + for (let i = 0; i < hashData.buckets.length; i++) hashData.buckets[i] = 0; + for (let i = 0; i < hashData.entries.length; i++) hashData.entries[i] = undefined!; + let currentEntry = hashData.head.nextEntry; + while (currentEntry) { + const nextEntry = currentEntry.nextEntry; + currentEntry.prevEntry = undefined; + currentEntry.nextEntry = hashData.head; + currentEntry.skipNextEntry = true; + currentEntry = nextEntry; + } + hashData.head.nextEntry = undefined; + hashData.tail = hashData.head; + hashData.size = 0; + hashData.freeList = -1; + hashData.freeSize = 0; + } + } + + static ensureCapacity(hashData: HashData, capacity: number) { + if (capacity < 0) throw new RangeError(); + const existingCapacity = hashData.entries.length; + if (existingCapacity >= capacity) return existingCapacity; + const newCapacity = getPrime(capacity); + HashData.resizeHashData(hashData, newCapacity); + return newCapacity; + } + + static trimExcessEntries(hashData: HashData, capacity = hashData.size - hashData.freeSize) { + if (capacity < hashData.size) throw new RangeError(); // TODO + const newCapacity = getPrime(capacity); + const existingEntries = hashData.entries; + if (newCapacity >= existingEntries.length) return; + const oldSize = hashData.size; + hashData.freeList = -1; + hashData.buckets = new SharedArray(newCapacity); + hashData.entries = new SharedArray(newCapacity); + let newSize = 0; + for (let i = 0; i < oldSize; i++) { + const hashCode = existingEntries[i].hashCode; + if (hashCode >= 0) { + const bucket = hashCode % newCapacity; + hashData.entries[newSize] = existingEntries[i]; + // Value in _buckets is 1-based + hashData.entries[newSize].next = hashData.buckets[bucket] - 1; + // Value in _buckets is 1-based + hashData.buckets[bucket] = newSize + 1; + newSize++; + } + } + hashData.size = newSize; + hashData.freeSize = 0; + } + + static * iterateEntries(head: HashEntry, selector: (entry: HashEntry) => R) { + let currentEntry: HashEntry | undefined = head; + while (currentEntry) { + const skipNextEntry = currentEntry.skipNextEntry; + currentEntry = currentEntry.nextEntry; + if (skipNextEntry) continue; + if (currentEntry) yield selector(currentEntry); + } + } + + static selectEntryKey(entry: HashEntry) { + return entry.key; + } + + static selectEntryValue(entry: HashEntry) { + return entry.value; + } + + static selectEntryEntry(entry: HashEntry) { + return [entry.key, entry.value] as [K, V]; + } +} diff --git a/src/compiler/sharing/collections/sharedLinkedList.ts b/src/compiler/sharing/collections/sharedLinkedList.ts new file mode 100644 index 00000000000..ad3ce3b60ec --- /dev/null +++ b/src/compiler/sharing/collections/sharedLinkedList.ts @@ -0,0 +1,218 @@ +import { Debug } from "../../debug"; +import { Shared, SharedStructBase } from "../structs/sharedStruct"; + +/** + * A shareable data structure that represents a doubly-linked list. + * @internal + */ +@Shared() +export class SharedLinkedList extends SharedStructBase { + @Shared() head: SharedLinkedListNode | undefined; + @Shared() size: number; + + constructor(iterable?: Iterable) { + super(); + this.head = undefined; + this.size = 0; + if (iterable) { + for (const value of iterable) { + SharedLinkedList.push(this, value); + } + } + } + + static size(list: SharedLinkedList) { + return list.size; + } + + static firstNode(list: SharedLinkedList) { + return list.head; + } + + static lastNode(list: SharedLinkedList) { + return list.head?.prev; + } + + static previousNode(node: SharedLinkedListNode) { + if (node.list && node !== node.list.head) { + return node.prev; + } + } + + static nextNode(node: SharedLinkedListNode) { + if (node.list && node !== node.list.head?.prev) { + return node.next; + } + } + + static insertBeforeNode(list: SharedLinkedList, contextNode: SharedLinkedListNode | undefined, newNode: SharedLinkedListNode) { + Debug.assert(!contextNode || contextNode.list === list); + Debug.assert(!newNode.list); + newNode.list = list; + if (list.head === undefined) { + newNode.next = newNode; + newNode.prev = newNode; + list.head = newNode; + } + else { + if (contextNode === undefined) { + contextNode = list.head; + list.head = newNode; + } + else if (contextNode === list.head) { + list.head = newNode; + } + newNode.next = contextNode; + newNode.prev = contextNode.prev; + contextNode.prev.next = newNode; + contextNode.prev = newNode; + } + list.size++; + return newNode; + } + + static insertAfterNode(list: SharedLinkedList, contextNode: SharedLinkedListNode | undefined, newNode: SharedLinkedListNode) { + Debug.assert(!contextNode || contextNode.list === list); + Debug.assert(!newNode.list); + newNode.list = list; + if (list.head === undefined) { + newNode.next = newNode; + newNode.prev = newNode; + list.head = newNode; + } + else { + if (contextNode === undefined) { + contextNode = list.head.prev!; + } + newNode.prev = contextNode; + newNode.next = contextNode.next; + contextNode.next.prev = newNode; + contextNode.next = newNode; + } + list.size++; + return newNode; + } + + static pushNode(list: SharedLinkedList, node: SharedLinkedListNode) { + Debug.assert(!node.list); + return this.insertAfterNode(list, /*contextNode*/ undefined, node); + } + + static push(list: SharedLinkedList, value: T) { + return this.pushNode(list, new SharedLinkedListNode(value)); + } + + static popNode(list: SharedLinkedList): SharedLinkedListNode | undefined { + const node = list.head?.prev; + if (node && this.deleteNode(list, node)) { + return node; + } + } + + static pop(list: SharedLinkedList): T | undefined { + return this.popNode(list)?.value; + } + + static unshiftNode(list: SharedLinkedList, node: SharedLinkedListNode) { + Debug.assert(!node.list); + return this.insertBeforeNode(list, /*contextNode*/ undefined, node); + } + + static unshift(list: SharedLinkedList, value: T) { + return this.unshiftNode(list, new SharedLinkedListNode(value)); + } + + static shiftNode(list: SharedLinkedList): SharedLinkedListNode | undefined { + const node = list.head; + if (node && this.deleteNode(list, node)) { + return node; + } + } + + static shift(list: SharedLinkedList): T | undefined { + return this.shiftNode(list)?.value; + } + + static deleteNode(list: SharedLinkedList, node: SharedLinkedListNode) { + if (node.list !== list) { + return undefined; + } + node.list = undefined; + if (node.next === node) { + Debug.assert(list.head === node); + list.head = undefined; + } + else { + node.next.prev = node.prev; + node.prev.next = node.next; + if (list.head === node) { + list.head = node.next; + } + } + node.next = node; + node.prev = node; + list.size--; + return node; + } + + static clear(list: SharedLinkedList) { + while (list.head?.prev) { + this.deleteNode(list, list.head.prev); + } + } + + static * nodes(list: SharedLinkedList) { + let node: SharedLinkedListNode; + let next = list.head; + if (next) { + do { + node = next; + next = node.next; + yield node; + } + while (next !== list.head); + } + } + + static * values(list: SharedLinkedList) { + let node: SharedLinkedListNode; + let next = list.head; + if (next) { + do { + node = next; + next = node.next; + yield node.value; + } + while (next !== list.head); + } + } +} + +/** @internal */ +@Shared() +export class SharedLinkedListNode extends SharedStructBase { + @Shared() list: SharedLinkedList | undefined; + @Shared() prev: SharedLinkedListNode; + @Shared() next: SharedLinkedListNode; + @Shared() value: T; + + constructor(value: T) { + super(); + this.list = undefined; + this.prev = this; + this.next = this; + this.value = value; + } + + static previous(node: SharedLinkedListNode) { + if (node.list && node !== node.list.head) { + return node.prev; + } + } + + static next(node: SharedLinkedListNode) { + if (node.list && node !== node.list.head?.prev) { + return node.next; + } + } +} diff --git a/src/compiler/sharing/collections/sharedMap.ts b/src/compiler/sharing/collections/sharedMap.ts new file mode 100644 index 00000000000..fab52f53eaa --- /dev/null +++ b/src/compiler/sharing/collections/sharedMap.ts @@ -0,0 +1,59 @@ +import { Identifiable } from "../structs/identifiableStruct"; +import { Shared, SharedStructBase } from "../structs/sharedStruct"; +import { Tag, Tagged } from "../structs/taggedStruct"; +import { HashData } from "./hashData"; + +@Shared() +export class SharedMap extends Identifiable(Tagged(SharedStructBase, Tag.Map)) { + @Shared() private _hashData: HashData; + + constructor(capacity = 0) { + super(); + this._hashData = new HashData(capacity); + } + + static size(self: SharedMap) { + return self._hashData.size - self._hashData.freeSize; + } + + static has(self: SharedMap, key: K) { + return HashData.findEntryIndex(self._hashData, key) >= 0; + } + + static get(self: SharedMap, key: K) { + return HashData.findEntryValue(self._hashData, key); + } + + static set(self: SharedMap, key: K, value: V) { + HashData.insertEntry(self._hashData, key, value); + return self; + } + + static delete(self: SharedMap, key: K) { + return HashData.deleteEntry(self._hashData, key); + } + + static clear(self: SharedMap) { + HashData.clearEntries(self._hashData); + } + + static ensureCapacity(self: SharedMap, capacity: number) { + return HashData.ensureCapacity(self._hashData, capacity); + } + + static trimExcess(self: SharedMap, capacity?: number) { + HashData.trimExcessEntries(self._hashData, capacity); + } + + static keys(self: SharedMap) { + return HashData.iterateEntries(self._hashData.head, HashData.selectEntryKey); + } + + static values(self: SharedMap) { + return HashData.iterateEntries(self._hashData.head, HashData.selectEntryValue); + } + + static entries(self: SharedMap) { + return HashData.iterateEntries(self._hashData.head, HashData.selectEntryEntry); + } +} diff --git a/src/compiler/sharing/collections/sharedResizableArray.ts b/src/compiler/sharing/collections/sharedResizableArray.ts new file mode 100644 index 00000000000..e81b510d806 --- /dev/null +++ b/src/compiler/sharing/collections/sharedResizableArray.ts @@ -0,0 +1,47 @@ +import { Identifiable } from "../structs/identifiableStruct"; +import { Shared, SharedStructBase } from "../structs/sharedStruct"; +import { Tag, Tagged } from "../structs/taggedStruct"; + +// TODO: consider constructing ropes of shared arrays rather than reallocating and copying. + +/** + * The default `SharedArray` implementation depends on a fixed-length array. A ResizableSharedArray is an abstraction + * over `SharedArray` that allows us to emulate resizing. + * @internal + */ +@Shared() +export class SharedResizableArray extends Identifiable(Tagged(SharedStructBase, Tag.ResizableArray)) { + @Shared() items: SharedArray; + + constructor(initialSize = 0) { + super(); + this.items = new SharedArray(initialSize); + } + + static size(self: SharedResizableArray) { + return self.items.length; + } + + static get(self: SharedResizableArray, index: number): T { + return self.items[index]; + } + + static set(self: SharedResizableArray, index: number, value: T): SharedResizableArray { + if (index >= self.items.length) { + this.resize(self, index + 1); + } + self.items[index] = value; + return self; + } + + static resize(self: SharedResizableArray, newLength: number) { + if (self.items.length !== newLength) { + const newArray = new SharedArray(newLength); + const minSize = Math.min(self.items.length, newLength); + for (let i = 0; i < minSize; i++) { + newArray[i] = self.items[i]; + } + } + return self; + } +} diff --git a/src/compiler/sharing/collections/sharedSet.ts b/src/compiler/sharing/collections/sharedSet.ts new file mode 100644 index 00000000000..4187ebbd7bd --- /dev/null +++ b/src/compiler/sharing/collections/sharedSet.ts @@ -0,0 +1,55 @@ +import { Identifiable } from "../structs/identifiableStruct"; +import { Shared, SharedStructBase } from "../structs/sharedStruct"; +import { Tag, Tagged } from "../structs/taggedStruct"; +import { HashData } from "./hashData"; + +@Shared() +export class SharedSet extends Identifiable(Tagged(SharedStructBase, Tag.Set)) { + @Shared() private _hashData: HashData; + + constructor(capacity = 0) { + super(); + this._hashData = new HashData(capacity); + } + + static size(self: SharedSet) { + return self._hashData.size - self._hashData.freeSize; + } + + static has(self: SharedSet, value: T) { + return HashData.findEntryIndex(self._hashData, value) >= 0; + } + + static add(self: SharedSet, value: T) { + HashData.insertEntry(self._hashData, value, value); + return self; + } + + static delete(self: SharedSet, value: T) { + return HashData.deleteEntry(self._hashData, value); + } + + static clear(self: SharedSet) { + HashData.clearEntries(self._hashData); + } + + static ensureCapacity(self: SharedSet, capacity: number) { + return HashData.ensureCapacity(self._hashData, capacity); + } + + static trimExcess(self: SharedSet, capacity?: number) { + HashData.trimExcessEntries(self._hashData, capacity); + } + + static keys(self: SharedSet) { + return HashData.iterateEntries(self._hashData.head, HashData.selectEntryKey); + } + + static values(self: SharedSet) { + return HashData.iterateEntries(self._hashData.head, HashData.selectEntryValue); + } + + static entries(self: SharedSet) { + return HashData.iterateEntries(self._hashData.head, HashData.selectEntryEntry); + } +} diff --git a/src/compiler/sharing/collections/xxhash32.ts b/src/compiler/sharing/collections/xxhash32.ts new file mode 100644 index 00000000000..bd39a7d637b --- /dev/null +++ b/src/compiler/sharing/collections/xxhash32.ts @@ -0,0 +1,86 @@ +import { utf8encodeInto } from "../../utilities"; + +const PRIME32_1 = 0x9e3779b1; +const PRIME32_2 = 0x85ebca77; +const PRIME32_3 = 0xc2b2ae3d; +const PRIME32_4 = 0x27d4eb2f; +const PRIME32_5 = 0x165667b1; + +/** + * Fast non-cryptographic hashing algorithm with good avalanche properties. + * https://xxhash.com/ + * @internal + */ +export function xxh32(buffer: ArrayBuffer, inputPtr: number, inputLength: number, seed: number): number { + if (inputPtr % 4) throw new TypeError("Pointer not aligned"); + const u32buffer = new Uint32Array(buffer); + const end = inputPtr + inputLength; + let acc: number; + let limit: number; + let v1: number; + let v2: number; + let v3: number; + let v4: number; + // translate ptr to u32 offset + inputPtr >>= 2; + if (inputLength >= 16) { + limit = (end - 16) >> 2; + v1 = (((seed + PRIME32_1) | 0) + PRIME32_2) | 0; + v2 = (seed + PRIME32_2) | 0; + v3 = (seed + 0) | 0; + v4 = (seed + PRIME32_1) | 0; + do { + v1 = (v1 + (u32buffer[inputPtr++] * PRIME32_2) | 0) | 0; + v1 = (((v1 << 13) | (v1 >>> 19)) * PRIME32_1) | 0; + v2 = (v2 + (u32buffer[inputPtr++] * PRIME32_2) | 0) | 0; + v2 = (((v2 << 13) | (v2 >>> 19)) * PRIME32_1) | 0; + v3 = (v3 + (u32buffer[inputPtr++] * PRIME32_2) | 0) | 0; + v3 = (((v3 << 13) | (v3 >>> 19)) * PRIME32_1) | 0; + v4 = (v4 + (u32buffer[inputPtr++] * PRIME32_2) | 0) | 0; + v4 = (((v4 << 13) | (v4 >>> 19)) * PRIME32_1) | 0; + } + while (inputPtr <= limit); + acc = (v1 << 1 | v1 >>> 31) + (v2 << 7 | v2 >>> 25) | (v3 << 12 | v3 >>> 20) | (v4 << 18 | v4 >>> 14); + } + else { + acc = (seed + PRIME32_5) | 0; + } + acc = (acc + inputLength) | 0; + limit = (end - 4) >> 2; + while (inputPtr <= limit) { + acc = (acc + (u32buffer[inputPtr++] * PRIME32_3) | 0) | 0; + acc = ((acc << 17 | acc >>> 15) * PRIME32_4) | 0; + } + // translate ptr to byte offset + inputPtr = inputPtr << 2; + if (inputPtr < end) { + const u8buffer = new Uint8Array(u32buffer.buffer); + do { + acc = (acc + (u8buffer[inputPtr++] * PRIME32_5) | 0) | 0; + acc = ((acc << 11 | acc >>> 21) * PRIME32_1) | 0; + } + while (inputPtr < end); + } + acc = ((acc ^ (acc >>> 15)) * PRIME32_2) | 0; + acc = ((acc ^ (acc >>> 13)) * PRIME32_3) | 0; + acc = acc ^ (acc >>> 16); + return acc >>> 0; +} + +let memory: Uint8Array | undefined; + +function ensureCapacity(size: number) { + if (!memory || memory.byteLength < size) { + memory = new Uint8Array(size + (65536 - size % 65536)); + } + return memory; +} + +/** + * @internal + */ +export function xxh32string(x: string, seed: number) { + const memory = ensureCapacity(x.length * 3); + const written = utf8encodeInto(x, memory); + return xxh32(memory.buffer, 0, written, seed); +} diff --git a/src/compiler/sharing/sharedDiagnostics.ts b/src/compiler/sharing/sharedDiagnostics.ts new file mode 100644 index 00000000000..2b42a144927 --- /dev/null +++ b/src/compiler/sharing/sharedDiagnostics.ts @@ -0,0 +1,132 @@ +import { CompilerOptions, DiagnosticCategory } from "../types"; +import { SharedSourceFile } from "./sharedNode"; +import { Shared, SharedStructBase } from "./structs/sharedStruct"; +import { Tag, Tagged } from "./structs/taggedStruct"; + +/** @internal */ +@Shared() +export class SharedDiagnosticMessage extends SharedStructBase { + @Shared() key: string; + @Shared() category: DiagnosticCategory; + @Shared() code: number; + @Shared() message: string; + @Shared() reportsUnnecessary?: boolean; + @Shared() reportsDeprecated?: boolean; + @Shared() elidedInCompatabilityPyramid?: boolean | undefined; + + constructor( + key: string, + category: DiagnosticCategory, + code: number, + message: string, + reportsUnnecessary?: {}, + reportsDeprecated?: {}, + elidedInCompatabilityPyramid?: boolean | undefined + ) { + super(); + this.key = key; + this.category = category; + this.code = code; + this.message = message; + this.reportsUnnecessary = !!reportsUnnecessary; + this.reportsDeprecated = !!reportsDeprecated; + this.elidedInCompatabilityPyramid = elidedInCompatabilityPyramid; + } +} + +/** @internal */ +@Shared() +export class SharedDiagnosticMessageChain extends Tagged(SharedStructBase, Tag.DiagnosticMessageChain) { + @Shared() messageText: string; + @Shared() category: DiagnosticCategory; + @Shared() code: number; + @Shared() next?: SharedArray; + // does this need to be shared somehow? + // repopulateInfo?: () => RepopulateDiagnosticChainInfo; + + constructor(messageText: string, category: DiagnosticCategory, code: number, next?: SharedArray) { + super(); + this.messageText = messageText; + this.category = category; + this.code = code; + this.next = next; + } +} + +/** @internal */ +@Shared() +export class SharedDiagnosticRelatedInformation extends Tagged(SharedStructBase, Tag.DiagnosticRelatedInformation) { + @Shared() category: DiagnosticCategory; + @Shared() code: number; + @Shared() file: SharedSourceFile | undefined; + @Shared() start: number | undefined; + @Shared() length: number | undefined; + @Shared() messageText: string | SharedDiagnosticMessageChain; + + constructor( + category: DiagnosticCategory, + code: number, + file: SharedSourceFile | undefined, + start: number | undefined, + length: number | undefined, + messageText: string | SharedDiagnosticMessageChain + ) { + super(); + this.category = category; + this.code = code; + this.file = file; + this.start = start; + this.length = length; + this.messageText = messageText; + } +} + +/** @internal */ +@Shared() +export class SharedDiagnostic extends Tagged(SharedDiagnosticRelatedInformation, Tag.Diagnostic) { + @Shared() reportsUnnecessary?: boolean; + @Shared() reportsDeprecated?: boolean; + @Shared() source?: string; + @Shared() relatedInformation?: SharedArray; + @Shared() skippedOn?: keyof CompilerOptions; +} + +/** @internal */ +@Shared() +export class SharedDiagnosticAndArguments extends SharedStructBase { + @Shared() message: SharedDiagnosticMessage; + @Shared() args: SharedArray; + + constructor(message: SharedDiagnosticMessage, args: SharedArray) { + super(); + this.message = message; + this.args = args; + } +} + +/** @internal */ +export interface SharedDiagnosticWithLocation extends SharedDiagnostic { + file: SharedSourceFile; + start: number; + length: number; +} + +/** @internal */ +@Shared() +export class SharedDiagnosticWithDetachedLocation extends SharedDiagnostic { + @Shared() fileName: string; + declare file: undefined; + + constructor( + category: DiagnosticCategory, + code: number, + file: SharedSourceFile | undefined, + start: number | undefined, + length: number | undefined, + messageText: string | SharedDiagnosticMessageChain, + fileName: string + ) { + super(category, code, file, start, length, messageText); + this.fileName = fileName; + } +} diff --git a/src/compiler/sharing/sharedNode.ts b/src/compiler/sharing/sharedNode.ts new file mode 100644 index 00000000000..0bf9c4a2320 --- /dev/null +++ b/src/compiler/sharing/sharedNode.ts @@ -0,0 +1,3420 @@ +import { hasProperty } from "../core"; +import { Debug } from "../debug"; +import { + __String, + BinaryOperator, + CommentDirectiveType, + CommentKind, + KeywordTypeSyntaxKind, + LanguageVariant, + NodeFlags, + Path, + PostfixUnaryOperator, + PrefixUnaryOperator, + ResolutionMode, + ScriptKind, + ScriptTarget, + SyntaxKind, + TokenFlags, + TokenSyntaxKind, + TransformFlags +} from "../types"; +import { isTokenKind } from "../utilitiesPublic"; +import { SharedMap } from "./collections/sharedMap"; +import { SharedDiagnosticWithLocation } from "./sharedDiagnostics"; +import { SharedNodeArray } from "./sharedNodeArray"; +import { Identifiable } from "./structs/identifiableStruct"; +import { isShareableNonPrimitive } from "./structs/shareable"; +import { Shared, SharedStructBase } from "./structs/sharedStruct"; +import { isTaggedStructObject, Tag, Tagged } from "./structs/taggedStruct"; + +/** @internal */ +@Shared() +export class SharedTextRange extends Tagged(SharedStructBase, Tag.TextRange) { + @Shared() pos: number; + @Shared() end: number; + constructor(pos = -1, end = -1) { + super(); + this.pos = pos; + this.end = end; + } +} + +/** @internal */ +@Shared({ abstract: true }) +export abstract class SharedNodeBase extends Identifiable(Tagged(SharedTextRange, Tag.Node)) { + @Shared() kind!: Kind; + @Shared() flags = NodeFlags.None; + @Shared() transformFlags = TransformFlags.None; + @Shared() parent: SharedNode | undefined; + + static [Symbol.hasInstance](value: unknown) { + return isShareableNonPrimitive(value) && isTaggedStructObject(value) && value.__tag__ === Tag.Node; + } +} + +/** @internal */ +@Shared() +export class SharedToken extends SharedNodeBase { + declare kind: Kind; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && isTokenKind(value.kind); + } +} + +/** @internal */ +@Shared() +export class SharedEndOfFileToken extends HasJSDoc(SharedToken) { + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedToken && value.kind === SyntaxKind.EndOfFileToken && hasProperty(value, "jsDoc"); + } +} + +/** @internal */ +@Shared() +export class SharedThisExpression extends HasFlowNode(SharedToken) { + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedToken && value.kind === SyntaxKind.ThisKeyword && hasProperty(value, "flowNode"); + } +} + +/** @internal */ +@Shared() +export class SharedSuperExpression extends HasFlowNode(SharedToken) { + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedToken && value.kind === SyntaxKind.SuperKeyword && hasProperty(value, "flowNode"); + } +} + +/** @internal */ +export type SharedModifier = + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + ; + +/** @internal */ +export type SharedModifierLike = + | SharedModifier + | SharedDecorator + ; + +/** @internal */ +@Shared() +export class SharedIdentifier extends HasFlowNode(HasSymbol(HasJSDoc(SharedNodeBase))) { + @Shared() escapedText!: __String; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.Identifier; + } +} + +/** @internal */ +@Shared() +export class SharedQualifiedName extends HasFlowNode(SharedNodeBase) { + @Shared() left!: SharedEntityName; + @Shared() right!: SharedIdentifier; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.QualifiedName; + } +} + +/** @internal */ +export type SharedEntityName = + | SharedIdentifier + | SharedQualifiedName + ; + +/** @internal */ +export type SharedBindingName = + | SharedIdentifier + | SharedBindingPattern + ; + +/** @internal */ +export type SharedPropertyName = + | SharedIdentifier + | SharedStringLiteral + | SharedNumericLiteral + | SharedComputedPropertyName + | SharedPrivateIdentifier + ; + +/** @internal */ +export type SharedMemberName = + | SharedIdentifier + | SharedPrivateIdentifier + ; + +/** @internal */ +@Shared() +export class SharedComputedPropertyName extends SharedNodeBase { + @Shared() expression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ComputedPropertyName; + } +} + +/** @internal */ +@Shared() +export class SharedPrivateIdentifier extends SharedNodeBase { + @Shared() escapedText!: __String; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.PrivateIdentifier; + } +} + +/** @internal */ +@Shared() +export class SharedTypeParameterDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers: SharedNodeArray | undefined; + @Shared() name!: SharedIdentifier; + @Shared() constraint: SharedTypeNode | undefined; + @Shared() default: SharedTypeNode | undefined; + @Shared() expression: SharedExpression | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TypeParameter; + } +} + +/** @internal */ +@Shared() +export class SharedParameterDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() dotDotDotToken!: SharedToken | undefined; + @Shared() name!: SharedBindingName; + @Shared() questionToken!: SharedToken | undefined; + @Shared() type!: SharedTypeNode | undefined; + @Shared() initializer!: SharedExpression | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.Parameter; + } +} + +/** @internal */ +@Shared() +export class SharedDecorator extends SharedNodeBase { + @Shared() expression!: SharedLeftHandSideExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.Decorator; + } +} + +/** @internal */ +@Shared() +export class SharedPropertySignature extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedPropertyName; + @Shared() questionToken!: SharedToken | undefined; + @Shared() type!: SharedTypeNode | undefined; + @Shared() initializer!: SharedExpression | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.PropertySignature; + } +} + +/** @internal */ +@Shared() +export class SharedCallSignatureDeclaration extends HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))) { + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode | undefined; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.CallSignature; + } +} + +/** @internal */ +@Shared() +export class SharedConstructSignatureDeclaration extends HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))) { + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode | undefined; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ConstructSignature; + } +} + +/** @internal */ +@Shared() +export class SharedVariableDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() name!: SharedBindingName; + @Shared() exclamationToken!: SharedToken | undefined; + @Shared() type!: SharedTypeNode | undefined; + @Shared() initializer!: SharedExpression | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.VariableDeclaration; + } +} + +/** @internal */ +@Shared() +export class SharedVariableDeclarationList extends SharedNodeBase { + @Shared() declarations!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.VariableDeclarationList; + } +} + +/** @internal */ +@Shared() +export class SharedBindingElement extends HasFlowNode(HasSymbol(SharedNodeBase)) { + @Shared() propertyName!: SharedPropertyName | undefined; + @Shared() dotDotDotToken!: SharedToken | undefined; + @Shared() name!: SharedBindingName; + @Shared() initializer!: SharedExpression | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.BindingElement; + } +} + +/** @internal */ +@Shared() +export class SharedPropertyDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedPropertyName; + @Shared() questionToken!: SharedToken | undefined; + @Shared() exclamationToken!: SharedToken | undefined; + @Shared() type!: SharedTypeNode | undefined; + @Shared() initializer!: SharedExpression | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.PropertyDeclaration; + } +} + +/** @internal */ +@Shared() +export class SharedPropertyAssignment extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() name!: SharedPropertyName; + @Shared() initializer!: SharedExpression; + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() questionToken!: SharedToken | undefined; + @Shared() exclamationToken!: SharedToken | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.PropertyAssignment; + } +} + +/** @internal */ +@Shared() +export class SharedShorthandPropertyAssignment extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() name!: SharedIdentifier; + @Shared() equalsToken!: SharedToken | undefined; + @Shared() objectAssignmentInitializer!: SharedExpression | undefined; + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() questionToken!: SharedToken | undefined; + @Shared() exclamationToken!: SharedToken | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ShorthandPropertyAssignment; + } +} + +/** @internal */ +@Shared() +export class SharedSpreadAssignment extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() expression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.SpreadAssignment; + } +} + +/** @internal */ +export type SharedBindingPattern = + | SharedObjectBindingPattern + | SharedArrayBindingPattern + ; + +/** @internal */ +export type SharedArrayBindingElement = + | SharedBindingElement + | SharedOmittedExpression + ; + +/** @internal */ +@Shared() +export class SharedObjectBindingPattern extends SharedNodeBase { + @Shared() elements!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ObjectBindingPattern; + } +} + +/** @internal */ +@Shared() +export class SharedArrayBindingPattern extends SharedNodeBase { + @Shared() elements!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ArrayBindingPattern; + } +} + +/** @internal */ +@Shared() +export class SharedFunctionDeclaration extends HasFunctionFlow(HasLocals(HasSymbol(HasJSDoc(SharedNodeBase)))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() asteriskToken!: SharedToken | undefined; + @Shared() name!: SharedIdentifier | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode | undefined; + @Shared() body!: SharedFunctionBody | undefined; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.FunctionDeclaration; + } +} + +/** @internal */ +@Shared() +export class SharedMethodSignature extends HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedPropertyName; + @Shared() questionToken!: SharedToken | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode | undefined; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.MethodSignature; + } +} + +/** @internal */ +@Shared() +export class SharedMethodDeclaration extends HasFunctionFlow(HasFlowNode(HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() asteriskToken!: SharedToken | undefined; + @Shared() name!: SharedPropertyName; + @Shared() questionToken!: SharedToken | undefined; + @Shared() exclamationToken!: SharedToken | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode | undefined; + @Shared() body!: SharedFunctionBody | undefined; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.MethodDeclaration; + } +} + +/** @internal */ +@Shared() +export class SharedConstructorDeclaration extends HasFunctionFlow(HasLocals(HasSymbol(HasJSDoc(SharedNodeBase)))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() body!: SharedFunctionBody | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode | undefined; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.Constructor; + } +} + +/** @internal */ +@Shared() +export class SharedSemicolonClassElement extends HasJSDoc(SharedNodeBase) { + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.SemicolonClassElement; + } +} + +/** @internal */ +@Shared() +export class SharedGetAccessorDeclaration extends HasFunctionFlow(HasFlowNode(HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedPropertyName; + @Shared() body!: SharedFunctionBody | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode | undefined; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.GetAccessor; + } +} + +/** @internal */ +@Shared() +export class SharedSetAccessorDeclaration extends HasFunctionFlow(HasFlowNode(HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedPropertyName; + @Shared() body!: SharedFunctionBody | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode | undefined; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.SetAccessor; + } +} + +/** @internal */ +export type SharedAccessorDeclaration = + | SharedGetAccessorDeclaration + | SharedSetAccessorDeclaration + ; + +/** @internal */ +@Shared() +export class SharedIndexSignatureDeclaration extends HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.IndexSignature; + } +} + +/** @internal */ +@Shared() +export class SharedClassStaticBlockDeclaration extends HasFunctionFlow(HasLocals(HasSymbol(HasJSDoc(SharedNodeBase)))) { + @Shared() body!: SharedBlock; + @Shared() modifiers!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ClassStaticBlockDeclaration; + } +} + +/** @internal */ +export type SharedTypeNode = + | SharedToken + | SharedImportTypeNode + | SharedThisTypeNode + | SharedFunctionTypeNode + | SharedConstructorTypeNode + | SharedTypeReferenceNode + | SharedTypePredicateNode + | SharedTypeQueryNode + | SharedTypeLiteralNode + | SharedArrayTypeNode + | SharedTupleTypeNode + | SharedNamedTupleMember + | SharedOptionalTypeNode + | SharedRestTypeNode + | SharedUnionTypeNode + | SharedIntersectionTypeNode + | SharedConditionalTypeNode + | SharedInferTypeNode + | SharedParenthesizedTypeNode + | SharedTypeOperatorNode + | SharedIndexedAccessTypeNode + | SharedMappedTypeNode + | SharedLiteralTypeNode + | SharedTemplateLiteralTypeNode + | SharedTemplateLiteralTypeSpan + | SharedJSDocTypeExpression + | SharedJSDocType + ; + +/** @internal */ +@Shared() +export class SharedImportTypeAssertionContainer extends SharedNodeBase { + @Shared() assertClause!: SharedAssertClause; + @Shared() multiLine!: boolean | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ImportTypeAssertionContainer; + } +} + +/** @internal */ +@Shared() +export class SharedImportTypeNode extends SharedNodeBase { + @Shared() isTypeOf!: boolean; + @Shared() argument!: SharedTypeNode; + @Shared() assertions!: SharedImportTypeAssertionContainer | undefined; + @Shared() qualifier!: SharedEntityName | undefined; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ImportType; + } +} + +/** @internal */ +@Shared() +export class SharedThisTypeNode extends SharedNodeBase { + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ThisType; + } +} + +/** @internal */ +@Shared() +export class SharedFunctionTypeNode extends HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))) { + @Shared() modifiers!: undefined | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.FunctionType; + } +} + +/** @internal */ +@Shared() +export class SharedConstructorTypeNode extends HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ConstructorType; + } +} + +/** @internal */ +@Shared() +export class SharedTypeReferenceNode extends SharedNodeBase { + @Shared() typeName!: SharedEntityName; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TypeReference; + } +} + +/** @internal */ +@Shared() +export class SharedTypePredicateNode extends SharedNodeBase { + @Shared() assertsModifier!: SharedToken | undefined; + @Shared() parameterName!: SharedIdentifier | SharedThisTypeNode; + @Shared() type!: SharedTypeNode | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TypePredicate; + } +} + +/** @internal */ +@Shared() +export class SharedTypeQueryNode extends SharedNodeBase { + @Shared() exprName!: SharedEntityName; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TypeQuery; + } +} + +/** @internal */ +@Shared() +export class SharedTypeLiteralNode extends HasSymbol(SharedNodeBase) { + @Shared() members!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TypeLiteral; + } +} + +/** @internal */ +@Shared() +export class SharedArrayTypeNode extends SharedNodeBase { + @Shared() elementType!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ArrayType; + } +} + +/** @internal */ +@Shared() +export class SharedTupleTypeNode extends SharedNodeBase { + @Shared() elements!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TupleType; + } +} + +/** @internal */ +@Shared() +export class SharedNamedTupleMember extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() dotDotDotToken!: SharedToken | undefined; + @Shared() name!: SharedIdentifier; + @Shared() questionToken!: SharedToken | undefined; + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.NamedTupleMember; + } +} + +/** @internal */ +@Shared() +export class SharedOptionalTypeNode extends SharedNodeBase { + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.OptionalType; + } +} + +/** @internal */ +@Shared() +export class SharedRestTypeNode extends SharedNodeBase { + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.RestType; + } +} + +/** @internal */ +@Shared() +export class SharedUnionTypeNode extends SharedNodeBase { + @Shared() types!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.UnionType; + } +} + +/** @internal */ +@Shared() +export class SharedIntersectionTypeNode extends SharedNodeBase { + @Shared() types!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.IntersectionType; + } +} + +/** @internal */ +@Shared() +export class SharedConditionalTypeNode extends HasLocals(SharedNodeBase) { + @Shared() checkType!: SharedTypeNode; + @Shared() extendsType!: SharedTypeNode; + @Shared() trueType!: SharedTypeNode; + @Shared() falseType!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ConditionalType; + } +} + +/** @internal */ +@Shared() +export class SharedInferTypeNode extends SharedNodeBase { + @Shared() typeParameter!: SharedTypeParameterDeclaration; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.InferType; + } +} + +/** @internal */ +@Shared() +export class SharedParenthesizedTypeNode extends SharedNodeBase { + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ParenthesizedType; + } +} + +/** @internal */ +@Shared() +export class SharedTypeOperatorNode extends SharedNodeBase { + @Shared() operator!: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword; + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TypeOperator; + } +} + +/** @internal */ +@Shared() +export class SharedIndexedAccessTypeNode extends SharedNodeBase { + @Shared() objectType!: SharedTypeNode; + @Shared() indexType!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.IndexedAccessType; + } +} + +/** @internal */ +@Shared() +export class SharedMappedTypeNode extends HasLocals(HasSymbol(SharedNodeBase)) { + @Shared() readonlyToken!: SharedToken | SharedToken | SharedToken | undefined; + @Shared() typeParameter!: SharedTypeParameterDeclaration; + @Shared() nameType!: SharedTypeNode | undefined; + @Shared() questionToken!: SharedToken | SharedToken | SharedToken | undefined; + @Shared() type!: SharedTypeNode | undefined; + @Shared() members!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.MappedType; + } +} + +/** @internal */ +@Shared() +export class SharedLiteralTypeNode extends SharedNodeBase { + @Shared() literal!: SharedToken | SharedToken | SharedToken | SharedStringLiteral | SharedNumericLiteral | SharedBigIntLiteral | SharedPrefixUnaryExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.LiteralType; + } +} + +/** @internal */ +@Shared() +export class SharedStringLiteral extends HasSymbol(SharedNodeBase) { + @Shared() text!: string; + @Shared() singleQuote!: boolean | undefined; + @Shared() isUnterminated!: boolean; + @Shared() hasExtendedUnicodeEscape!: boolean; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.StringLiteral; + } +} + +/** @internal */ +@Shared() +export class SharedTemplateLiteralTypeNode extends SharedNodeBase { + @Shared() head!: SharedTemplateHead; + @Shared() templateSpans!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TemplateLiteralType; + } +} + +/** @internal */ +@Shared() +export class SharedTemplateLiteralTypeSpan extends SharedNodeBase { + @Shared() type!: SharedTypeNode; + @Shared() literal!: SharedTemplateMiddle | SharedTemplateTail; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TemplateLiteralTypeSpan; + } +} + +/** @internal */ +export type SharedLiteral = + | SharedNumericLiteral + | SharedBigIntLiteral + | SharedStringLiteral + | SharedJsxText + | SharedRegularExpressionLiteral + | SharedNoSubstitutionTemplateLiteral + ; + +/** @internal */ +export type SharedExpression = + | SharedOmittedExpression + | SharedYieldExpression + | SharedBinaryExpression + | SharedUpdateExpression + | SharedConditionalExpression + | SharedArrowFunction + | SharedSpreadElement + | SharedAsExpression + | SharedSatisfiesExpression + | SharedJsxOpeningElement + | SharedJsxOpeningFragment + | SharedJsxClosingFragment + | SharedJsxExpression + ; + +/** @internal */ +export type SharedUpdateExpression = + | SharedUnaryExpression + | SharedPrefixUnaryExpression + | SharedPostfixUnaryExpression + ; + +/** @internal */ +export type SharedUnaryExpression = + | SharedLeftHandSideExpression + | SharedDeleteExpression + | SharedTypeOfExpression + | SharedVoidExpression + | SharedAwaitExpression + | SharedTypeAssertion + ; + +/** @internal */ +export type SharedLeftHandSideExpression = + | SharedMemberExpression + | SharedCallExpression + | SharedNewExpression + | SharedNonNullExpression + ; + +/** @internal */ +export type SharedMemberExpression = + | SharedPrimaryExpression + | SharedPropertyAccessExpression + | SharedElementAccessExpression + | SharedExpressionWithTypeArguments + | SharedTaggedTemplateExpression + ; + +/** @internal */ +export type SharedPrimaryExpression = + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedToken + | SharedIdentifier + | SharedPrivateIdentifier + | SharedFunctionExpression + | SharedClassExpression + | SharedRegularExpressionLiteral + | SharedNoSubstitutionTemplateLiteral + | SharedStringLiteral + | SharedNumericLiteral + | SharedBigIntLiteral + | SharedTemplateExpression + | SharedParenthesizedExpression + | SharedArrayLiteralExpression + | SharedObjectLiteralExpression + | SharedNewExpression + | SharedMetaProperty + | SharedJsxElement + | SharedJsxAttributes + | SharedJsxSelfClosingElement + | SharedJsxFragment + | SharedMissingDeclaration + ; + +/** @internal */ +@Shared() +export class SharedOmittedExpression extends SharedNodeBase { + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.OmittedExpression; + } +} + +/** @internal */ +@Shared() +export class SharedPrefixUnaryExpression extends SharedNodeBase { + @Shared() operator!: PrefixUnaryOperator; + @Shared() operand!: SharedUnaryExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.PrefixUnaryExpression; + } +} + +/** @internal */ +@Shared() +export class SharedPostfixUnaryExpression extends SharedNodeBase { + @Shared() operand!: SharedLeftHandSideExpression; + @Shared() operator!: PostfixUnaryOperator; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.PostfixUnaryExpression; + } +} + +/** @internal */ +@Shared() +export class SharedDeleteExpression extends SharedNodeBase { + @Shared() expression!: SharedUnaryExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.DeleteExpression; + } +} + +/** @internal */ +@Shared() +export class SharedTypeOfExpression extends SharedNodeBase { + @Shared() expression!: SharedUnaryExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TypeOfExpression; + } +} + +/** @internal */ +@Shared() +export class SharedVoidExpression extends SharedNodeBase { + @Shared() expression!: SharedUnaryExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.VoidExpression; + } +} + +/** @internal */ +@Shared() +export class SharedAwaitExpression extends SharedNodeBase { + @Shared() expression!: SharedUnaryExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.AwaitExpression; + } +} + +/** @internal */ +@Shared() +export class SharedYieldExpression extends SharedNodeBase { + @Shared() asteriskToken!: SharedToken | undefined; + @Shared() expression!: SharedExpression | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.YieldExpression; + } +} + +/** @internal */ +@Shared() +export class SharedBinaryExpression extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() left!: SharedExpression; + @Shared() operatorToken!: SharedToken; + @Shared() right!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.BinaryExpression; + } +} + +/** @internal */ +@Shared() +export class SharedConditionalExpression extends SharedNodeBase { + @Shared() condition!: SharedExpression; + @Shared() questionToken!: SharedToken; + @Shared() whenTrue!: SharedExpression; + @Shared() colonToken!: SharedToken; + @Shared() whenFalse!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ConditionalExpression; + } +} + +/** @internal */ +export type SharedFunctionBody = SharedBlock; +/** @internal */ +export type SharedConciseBody = SharedFunctionBody | SharedExpression; + +/** @internal */ +@Shared() +export class SharedFunctionExpression extends HasFunctionFlow(HasFlowNode(HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() asteriskToken!: SharedToken | undefined; + @Shared() name!: SharedIdentifier | undefined; + @Shared() questionToken!: SharedToken | undefined; + @Shared() exclamationToken!: SharedToken | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode | undefined; + @Shared() body!: SharedFunctionBody; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.FunctionExpression; + } +} + +/** @internal */ +@Shared() +export class SharedArrowFunction extends HasFunctionFlow(HasFlowNode(HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() equalsGreaterThanToken!: SharedToken; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode | undefined; + @Shared() body!: SharedConciseBody; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ArrowFunction; + } +} + +/** @internal */ +@Shared() +export class SharedRegularExpressionLiteral extends SharedNodeBase { + @Shared() text!: string; + @Shared() isUnterminated!: boolean | undefined; + @Shared() hasExtendedUnicodeEscape!: boolean | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.RegularExpressionLiteral; + } +} + +/** @internal */ +@Shared() +export class SharedNoSubstitutionTemplateLiteral extends HasSymbol(SharedNodeBase) { + @Shared() text!: string; + @Shared() isUnterminated!: boolean | undefined; + @Shared() hasExtendedUnicodeEscape!: boolean | undefined; + @Shared() rawText!: string | undefined; + @Shared() templateFlags!: TokenFlags | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.NoSubstitutionTemplateLiteral; + } +} + +/** @internal */ +@Shared() +export class SharedNumericLiteral extends HasSymbol(SharedNodeBase) { + @Shared() text!: string; + @Shared() isUnterminated!: boolean | undefined; + @Shared() hasExtendedUnicodeEscape!: boolean | undefined; + @Shared() numericLiteralFlags!: TokenFlags; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.NumericLiteral; + } +} + +/** @internal */ +@Shared() +export class SharedBigIntLiteral extends SharedNodeBase { + @Shared() text!: string; + @Shared() isUnterminated!: boolean | undefined; + @Shared() hasExtendedUnicodeEscape!: boolean | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.BigIntLiteral; + } +} + +/** @internal */ +@Shared() +export class SharedTemplateHead extends SharedNodeBase { + @Shared() text!: string; + @Shared() isUnterminated!: boolean | undefined; + @Shared() hasExtendedUnicodeEscape!: boolean | undefined; + @Shared() rawText!: string | undefined; + @Shared() templateFlags!: TokenFlags | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TemplateHead; + } +} + +/** @internal */ +@Shared() +export class SharedTemplateMiddle extends SharedNodeBase { + @Shared() text!: string; + @Shared() isUnterminated!: boolean | undefined; + @Shared() hasExtendedUnicodeEscape!: boolean | undefined; + @Shared() rawText!: string | undefined; + @Shared() templateFlags!: TokenFlags | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TemplateMiddle; + } +} + +/** @internal */ +@Shared() +export class SharedTemplateTail extends SharedNodeBase { + @Shared() text!: string; + @Shared() isUnterminated!: boolean | undefined; + @Shared() hasExtendedUnicodeEscape!: boolean | undefined; + @Shared() rawText!: string | undefined; + @Shared() templateFlags!: TokenFlags | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TemplateTail; + } +} + +/** @internal */ +@Shared() +export class SharedTemplateExpression extends SharedNodeBase { + @Shared() head!: SharedTemplateHead; + @Shared() templateSpans!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TemplateExpression; + } +} + +/** @internal */ +@Shared() +export class SharedTemplateSpan extends SharedNodeBase { + @Shared() expression!: SharedExpression; + @Shared() literal!: SharedTemplateMiddle | SharedTemplateTail; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TemplateSpan; + } +} + +/** @internal */ +@Shared() +export class SharedParenthesizedExpression extends HasJSDoc(SharedNodeBase) { + @Shared() expression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ParenthesizedExpression; + } +} + +/** @internal */ +@Shared() +export class SharedArrayLiteralExpression extends SharedNodeBase { + @Shared() elements!: SharedNodeArray; + @Shared() multiLine!: boolean | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ArrayLiteralExpression; + } +} + +/** @internal */ +@Shared() +export class SharedSpreadElement extends SharedNodeBase { + @Shared() expression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.SpreadElement; + } +} + +/** @internal */ +export type SharedObjectLiteralElement = + | SharedPropertyAssignment + | SharedShorthandPropertyAssignment + | SharedSpreadAssignment + | SharedMethodDeclaration + | SharedAccessorDeclaration + ; + +/** @internal */ +@Shared() +export class SharedObjectLiteralExpression extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() properties!: SharedNodeArray; + @Shared() multiLine!: boolean | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ObjectLiteralExpression; + } +} + +/** @internal */ +@Shared() +export class SharedPropertyAccessExpression extends HasFlowNode(HasSymbol(HasJSDoc(SharedNodeBase))) { + @Shared() expression!: SharedLeftHandSideExpression; + @Shared() questionDotToken!: SharedToken | undefined; + @Shared() name!: SharedMemberName; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.PropertyAccessExpression; + } +} + +/** @internal */ +@Shared() +export class SharedElementAccessExpression extends HasFlowNode(HasSymbol(HasJSDoc(SharedNodeBase))) { + @Shared() expression!: SharedLeftHandSideExpression; + @Shared() questionDotToken!: SharedToken | undefined; + @Shared() argumentExpression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ElementAccessExpression; + } +} + +/** @internal */ +@Shared() +export class SharedCallExpression extends HasSymbol(SharedNodeBase) { + @Shared() expression!: SharedLeftHandSideExpression; + @Shared() questionDotToken!: SharedToken | undefined; + @Shared() typeArguments!: SharedNodeArray | undefined; + @Shared() arguments!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.CallExpression; + } +} + +/** @internal */ +@Shared() +export class SharedExpressionWithTypeArguments extends SharedNodeBase { + @Shared() expression!: SharedLeftHandSideExpression; + @Shared() typeArguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ExpressionWithTypeArguments; + } +} + +/** @internal */ +@Shared() +export class SharedNewExpression extends HasSymbol(SharedNodeBase) { + @Shared() expression!: SharedLeftHandSideExpression; + @Shared() typeArguments!: SharedNodeArray | undefined; + @Shared() arguments!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.NewExpression; + } +} + +/** @internal */ +export type SharedTemplateLiteral = + | SharedTemplateExpression + | SharedNoSubstitutionTemplateLiteral + ; + +/** @internal */ +@Shared() +export class SharedTaggedTemplateExpression extends SharedNodeBase { + @Shared() tag!: SharedLeftHandSideExpression; + @Shared() typeArguments!: SharedNodeArray | undefined; + @Shared() template!: SharedTemplateLiteral; + @Shared() questionDotToken!: SharedToken | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TaggedTemplateExpression; + } +} + +/** @internal */ +@Shared() +export class SharedAsExpression extends SharedNodeBase { + @Shared() expression!: SharedExpression; + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.AsExpression; + } +} + +/** @internal */ +@Shared() +export class SharedTypeAssertion extends SharedNodeBase { + @Shared() type!: SharedTypeNode; + @Shared() expression!: SharedUnaryExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TypeAssertionExpression; + } +} + +/** @internal */ +@Shared() +export class SharedSatisfiesExpression extends SharedNodeBase { + @Shared() expression!: SharedExpression; + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.SatisfiesExpression; + } +} + +/** @internal */ +@Shared() +export class SharedNonNullExpression extends SharedNodeBase { + @Shared() expression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.NonNullExpression; + } +} + +/** @internal */ +@Shared() +export class SharedMetaProperty extends HasFlowNode(SharedNodeBase) { + @Shared() keywordToken!: SyntaxKind.NewKeyword | SyntaxKind.ImportKeyword; + @Shared() name!: SharedIdentifier; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.MetaProperty; + } +} + +/** @internal */ +@Shared() +export class SharedJsxElement extends SharedNodeBase { + @Shared() openingElement!: SharedJsxOpeningElement; + @Shared() children!: SharedNodeArray; + @Shared() closingElement!: SharedJsxClosingElement; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxElement; + } +} + +/** @internal */ +export type SharedJsxOpeningLikeElement = + | SharedJsxSelfClosingElement + | SharedJsxOpeningElement + ; + +/** @internal */ +export type SharedJsxAttributeLike = + | SharedJsxAttribute + | SharedJsxSpreadAttribute + ; + +/** @internal */ +export type SharedJsxAttributeName = + | SharedIdentifier + | SharedJsxNamespacedName + ; + +/** @internal */ +export type SharedJsxTagNameExpression = + | SharedIdentifier + | SharedToken + | SharedPropertyAccessExpression + | SharedJsxNamespacedName + ; + +/** @internal */ +@Shared() +export class SharedJsxAttributes extends HasSymbol(SharedNodeBase) { + @Shared() properties!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxAttributes; + } +} + +/** @internal */ +@Shared() +export class SharedJsxNamespacedName extends SharedNodeBase { + @Shared() name!: SharedIdentifier; + @Shared() namespace!: SharedIdentifier; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxNamespacedName; + } +} + +/** @internal */ +@Shared() +export class SharedJsxOpeningElement extends SharedNodeBase { + @Shared() tagName!: SharedJsxTagNameExpression; + @Shared() typeArguments!: SharedNodeArray | undefined; + @Shared() attributes!: SharedJsxAttributes; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxOpeningElement; + } +} + +/** @internal */ +@Shared() +export class SharedJsxSelfClosingElement extends SharedNodeBase { + @Shared() tagName!: SharedJsxTagNameExpression; + @Shared() typeArguments!: SharedNodeArray | undefined; + @Shared() attributes!: SharedJsxAttributes; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxSelfClosingElement; + } +} + +/** @internal */ +@Shared() +export class SharedJsxFragment extends SharedNodeBase { + @Shared() openingFragment!: SharedJsxOpeningFragment; + @Shared() children!: SharedNodeArray; + @Shared() closingFragment!: SharedJsxClosingFragment; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxFragment; + } +} + +/** @internal */ +@Shared() +export class SharedJsxOpeningFragment extends SharedNodeBase { + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxOpeningFragment; + } +} + +/** @internal */ +@Shared() +export class SharedJsxClosingFragment extends SharedNodeBase { + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxClosingFragment; + } +} + +/** @internal */ +@Shared() +export class SharedJsxAttribute extends HasSymbol(SharedNodeBase) { + @Shared() name!: SharedJsxAttributeName; + @Shared() initializer!: SharedJsxAttributeValue | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxAttribute; + } +} + +/** @internal */ +export type SharedJsxAttributeValue = + | SharedStringLiteral + | SharedJsxExpression + | SharedJsxElement + | SharedJsxSelfClosingElement + | SharedJsxFragment; + +/** @internal */ +@Shared() +export class SharedJsxSpreadAttribute extends SharedNodeBase { + @Shared() expression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxSpreadAttribute; + } +} + +/** @internal */ +@Shared() +export class SharedJsxClosingElement extends SharedNodeBase { + @Shared() tagName!: SharedJsxTagNameExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxClosingElement; + } +} + +/** @internal */ +@Shared() +export class SharedJsxExpression extends SharedNodeBase { + @Shared() dotDotDotToken!: SharedToken | undefined; + @Shared() expression!: SharedExpression | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxExpression; + } +} + +/** @internal */ +@Shared() +export class SharedJsxText extends SharedNodeBase { + @Shared() text!: string; + @Shared() isUnterminated!: boolean | undefined; + @Shared() hasExtendedUnicodeEscape!: boolean | undefined; + @Shared() containsOnlyTriviaWhiteSpaces!: boolean; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JsxText; + } +} + +/** @internal */ +export type SharedJsxChild = + | SharedJsxText + | SharedJsxExpression + | SharedJsxElement + | SharedJsxSelfClosingElement + | SharedJsxFragment + ; + +/** @internal */ +export type SharedDeclaration = + | never + ; + +/** @internal */ +export type SharedStatement = + | SharedEmptyStatement + | SharedDebuggerStatement + | SharedMissingDeclaration + | SharedBlock + | SharedVariableStatement + | SharedExpressionStatement + | SharedIfStatement + | SharedIterationStatement + | SharedBreakStatement + | SharedContinueStatement + | SharedReturnStatement + | SharedWithStatement + | SharedSwitchStatement + | SharedLabeledStatement + | SharedThrowStatement + | SharedTryStatement + | SharedFunctionDeclaration + | SharedClassDeclaration + | SharedInterfaceDeclaration + | SharedTypeAliasDeclaration + | SharedEnumDeclaration + | SharedModuleDeclaration + | SharedModuleBlock + | SharedImportEqualsDeclaration + | SharedImportDeclaration + | SharedNamespaceExportDeclaration + | SharedExportDeclaration + | SharedExportAssignment + ; + +/** @internal */ +export type SharedIterationStatement = + | SharedDoStatement + | SharedWhileStatement + | SharedForStatement + | SharedForInOrOfStatement + ; + +/** @internal */ +@Shared() +export class SharedEmptyStatement extends HasJSDoc(SharedNodeBase) { + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.EmptyStatement; + } +} + +/** @internal */ +@Shared() +export class SharedDebuggerStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.DebuggerStatement; + } +} + +/** @internal */ +@Shared() +export class SharedMissingDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() name!: SharedIdentifier | undefined; + @Shared() modifiers!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.MissingDeclaration; + } +} + +/** @internal */ +@Shared() +export class SharedBlock extends HasLocals(HasJSDoc(SharedNodeBase)) { + @Shared() statements!: SharedNodeArray; + @Shared() multiLine!: boolean | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.Block; + } +} + +/** @internal */ +@Shared() +export class SharedVariableStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() declarationList!: SharedVariableDeclarationList; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.VariableStatement; + } +} + +/** @internal */ +@Shared() +export class SharedExpressionStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() expression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ExpressionStatement; + } +} + +/** @internal */ +@Shared() +export class SharedIfStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() expression!: SharedExpression; + @Shared() thenStatement!: SharedStatement; + @Shared() elseStatement!: SharedStatement | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.IfStatement; + } +} + +/** @internal */ +@Shared() +export class SharedDoStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() statement!: SharedStatement; + @Shared() expression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.DoStatement; + } +} + +/** @internal */ +@Shared() +export class SharedWhileStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() expression!: SharedExpression; + @Shared() statement!: SharedStatement; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.WhileStatement; + } +} + +/** @internal */ +export type SharedForInitializer = + | SharedVariableDeclarationList + | SharedExpression + ; + +/** @internal */ +@Shared() +export class SharedForStatement extends HasFlowNode(HasLocals(HasJSDoc(SharedNodeBase))) { + @Shared() initializer!: SharedForInitializer | undefined; + @Shared() condition!: SharedExpression | undefined; + @Shared() incrementor!: SharedExpression | undefined; + @Shared() statement!: SharedStatement; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ForStatement; + } +} + +/** @internal */ +export type SharedForInOrOfStatement = + | SharedForInStatement + | SharedForOfStatement + ; + +/** @internal */ +@Shared() +export class SharedForInStatement extends HasFlowNode(HasLocals(HasJSDoc(SharedNodeBase))) { + @Shared() initializer!: SharedForInitializer; + @Shared() expression!: SharedExpression; + @Shared() statement!: SharedStatement; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ForInStatement; + } +} + +/** @internal */ +@Shared() +export class SharedForOfStatement extends HasFlowNode(HasLocals(HasJSDoc(SharedNodeBase))) { + @Shared() awaitModifier!: SharedToken | undefined; + @Shared() initializer!: SharedForInitializer; + @Shared() expression!: SharedExpression; + @Shared() statement!: SharedStatement; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ForOfStatement; + } +} + +/** @internal */ +@Shared() +export class SharedBreakStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() label!: SharedIdentifier | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.BreakStatement; + } +} + +/** @internal */ +@Shared() +export class SharedContinueStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() label!: SharedIdentifier | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ContinueStatement; + } +} + +/** @internal */ +@Shared() +export class SharedReturnStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() expression!: SharedExpression | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ReturnStatement; + } +} + +/** @internal */ +@Shared() +export class SharedWithStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() expression!: SharedExpression; + @Shared() statement!: SharedStatement; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.WithStatement; + } +} + +/** @internal */ +@Shared() +export class SharedSwitchStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() expression!: SharedExpression; + @Shared() caseBlock!: SharedCaseBlock; + @Shared() possiblyExhaustive!: boolean; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.SwitchStatement; + } +} + +/** @internal */ +@Shared() +export class SharedCaseBlock extends HasLocals(SharedNodeBase) { + @Shared() clauses!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.CaseBlock; + } +} + +/** @internal */ +@Shared() +export class SharedCaseClause extends HasJSDoc(SharedNodeBase) { + @Shared() expression!: SharedExpression; + @Shared() statements!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.CaseClause; + } +} + +/** @internal */ +@Shared() +export class SharedDefaultClause extends SharedNodeBase { + @Shared() statements!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.DefaultClause; + } +} + +/** @internal */ +export type SharedCaseOrDefaultClause = + | SharedCaseClause + | SharedDefaultClause + ; + +/** @internal */ +@Shared() +export class SharedLabeledStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() label!: SharedIdentifier; + @Shared() statement!: SharedStatement; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.LabeledStatement; + } +} + +/** @internal */ +@Shared() +export class SharedThrowStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() expression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ThrowStatement; + } +} + +/** @internal */ +@Shared() +export class SharedTryStatement extends HasFlowNode(HasJSDoc(SharedNodeBase)) { + @Shared() tryBlock!: SharedBlock; + @Shared() catchClause!: SharedCatchClause | undefined; + @Shared() finallyBlock!: SharedBlock | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TryStatement; + } +} + +/** @internal */ +@Shared() +export class SharedCatchClause extends HasLocals(SharedNodeBase) { + @Shared() variableDeclaration!: SharedVariableDeclaration | undefined; + @Shared() block!: SharedBlock; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.CatchClause; + } +} + +/** @internal */ +export type SharedClassElement = + | SharedPropertyDeclaration + | SharedMethodDeclaration + | SharedConstructorDeclaration + | SharedSemicolonClassElement + | SharedAccessorDeclaration + | SharedIndexSignatureDeclaration + | SharedClassStaticBlockDeclaration + ; + +/** @internal */ +@Shared() +export class SharedClassDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedIdentifier | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() heritageClauses!: SharedNodeArray | undefined; + @Shared() members!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ClassDeclaration; + } +} + +/** @internal */ +@Shared() +export class SharedClassExpression extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedIdentifier | undefined; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() heritageClauses!: SharedNodeArray | undefined; + @Shared() members!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ClassExpression; + } +} + +/** @internal */ +export type SharedTypeElement = + | SharedCallSignatureDeclaration + | SharedConstructSignatureDeclaration + | SharedMethodSignature + | SharedIndexSignatureDeclaration + | SharedPropertySignature + | SharedAccessorDeclaration + ; + +/** @internal */ +@Shared() +export class SharedInterfaceDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedIdentifier; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() heritageClauses!: SharedNodeArray | undefined; + @Shared() members!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.InterfaceDeclaration; + } +} + +/** @internal */ +@Shared() +export class SharedHeritageClause extends SharedNodeBase { + @Shared() token!: SyntaxKind.ExtendsKeyword | SyntaxKind.ImplementsKeyword; + @Shared() types!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.HeritageClause; + } +} + +/** @internal */ +@Shared() +export class SharedTypeAliasDeclaration extends HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedIdentifier; + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.TypeAliasDeclaration; + } +} + +/** @internal */ +@Shared() +export class SharedEnumMember extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() name!: SharedPropertyName; + @Shared() initializer!: SharedExpression | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.EnumMember; + } +} + +/** @internal */ +@Shared() +export class SharedEnumDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedIdentifier; + @Shared() members!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.EnumDeclaration; + } +} + +/** @internal */ +export type SharedModuleName = + | SharedIdentifier + | SharedStringLiteral + ; + +/** @internal */ +export type SharedModuleBody = + | SharedNamespaceBody + | SharedJSDocNamespaceBody + ; + +/** @internal */ +@Shared() +export class SharedModuleDeclaration extends HasLocals(HasSymbol(HasJSDoc(SharedNodeBase))) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedModuleName; + @Shared() body!: SharedModuleBody | SharedJSDocNamespaceDeclaration | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ModuleDeclaration; + } +} + +/** @internal */ +export type SharedNamespaceBody = + | SharedModuleBlock + | SharedNamespaceDeclaration + ; + +/** @internal */ +export interface SharedNamespaceDeclaration extends SharedModuleDeclaration { + readonly name: SharedIdentifier; + readonly body: SharedNamespaceBody; +} + +/** @internal */ +export type SharedJSDocNamespaceBody = + | SharedIdentifier + | SharedJSDocNamespaceDeclaration + ; + +/** @internal */ +export interface SharedJSDocNamespaceDeclaration extends SharedModuleDeclaration { + readonly name: SharedIdentifier; + readonly body: SharedJSDocNamespaceBody | undefined; +} + +/** @internal */ +@Shared() +export class SharedModuleBlock extends HasJSDoc(SharedNodeBase) { + @Shared() statements!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ModuleBlock; + } +} + +/** @internal */ +export type SharedModuleReference = + | SharedEntityName + | SharedExternalModuleReference + ; + +/** @internal */ +@Shared() +export class SharedImportEqualsDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() name!: SharedIdentifier; + @Shared() isTypeOnly!: boolean; + @Shared() moduleReference!: SharedModuleReference; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ImportEqualsDeclaration; + } +} + +/** @internal */ +@Shared() +export class SharedExternalModuleReference extends SharedNodeBase { + @Shared() expression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ExternalModuleReference; + } +} + +/** @internal */ +@Shared() +export class SharedImportDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() importClause!: SharedImportClause | undefined; + @Shared() moduleSpecifier!: SharedExpression; + @Shared() assertClause!: SharedAssertClause | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ImportDeclaration; + } +} + +/** @internal */ +export type SharedNamedImportBindings = + | SharedNamespaceImport + | SharedNamedImports + ; + +/** @internal */ +export type SharedNamedExportBindings = + | SharedNamespaceExport + | SharedNamedExports + ; + +/** @internal */ +@Shared() +export class SharedImportClause extends HasSymbol(SharedNodeBase) { + @Shared() isTypeOnly!: boolean; + @Shared() name!: SharedIdentifier | undefined; + @Shared() namedBindings!: SharedNamedImportBindings | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ImportClause; + } +} + +/** @internal */ +export type SharedAssertionKey = + | SharedIdentifier + | SharedStringLiteral + ; + +/** @internal */ +@Shared() +export class SharedAssertEntry extends SharedNodeBase { + @Shared() name!: SharedAssertionKey; + @Shared() value!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.AssertEntry; + } +} + +/** @internal */ +@Shared() +export class SharedAssertClause extends SharedNodeBase { + @Shared() elements!: SharedNodeArray; + @Shared() multiLine!: boolean | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.AssertClause; + } +} + +/** @internal */ +@Shared() +export class SharedNamespaceImport extends HasSymbol(SharedNodeBase) { + @Shared() name!: SharedIdentifier; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.NamespaceImport; + } +} + +/** @internal */ +@Shared() +export class SharedNamespaceExport extends HasSymbol(SharedNodeBase) { + @Shared() name!: SharedIdentifier; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.NamespaceExport; + } +} + +/** @internal */ +@Shared() +export class SharedNamespaceExportDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() name!: SharedIdentifier; + @Shared() modifiers!: SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.NamespaceExportDeclaration; + } +} + +/** @internal */ +@Shared() +export class SharedExportDeclaration extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() isTypeOnly!: boolean; + @Shared() exportClause!: SharedNamedExportBindings | undefined; + @Shared() moduleSpecifier!: SharedExpression | undefined; + @Shared() assertClause!: SharedAssertClause | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ExportDeclaration; + } +} + +/** @internal */ +@Shared() +export class SharedNamedImports extends SharedNodeBase { + @Shared() elements!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.NamedImports; + } +} + +/** @internal */ +@Shared() +export class SharedNamedExports extends SharedNodeBase { + @Shared() elements!: SharedNodeArray; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.NamedExports; + } +} + +/** @internal */ +@Shared() +export class SharedImportSpecifier extends HasSymbol(SharedNodeBase) { + @Shared() propertyName!: SharedIdentifier | undefined; + @Shared() name!: SharedIdentifier; + @Shared() isTypeOnly!: boolean; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ImportSpecifier; + } +} + +/** @internal */ +@Shared() +export class SharedExportSpecifier extends HasJSDoc(SharedNodeBase) { + @Shared() isTypeOnly!: boolean; + @Shared() propertyName!: SharedIdentifier | undefined; + @Shared() name!: SharedIdentifier; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ExportSpecifier; + } +} + +/** @internal */ +@Shared() +export class SharedExportAssignment extends HasSymbol(HasJSDoc(SharedNodeBase)) { + @Shared() modifiers!: SharedNodeArray | undefined; + @Shared() isExportEquals!: boolean | undefined; + @Shared() expression!: SharedExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.ExportAssignment; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocTypeExpression extends SharedNodeBase { + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocTypeExpression; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocNameReference extends SharedNodeBase { + @Shared() name!: SharedEntityName | SharedJSDocMemberName; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocNameReference; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocMemberName extends SharedNodeBase { + @Shared() left!: SharedEntityName | SharedJSDocMemberName; + @Shared() right!: SharedIdentifier; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocMemberName; + } +} + +/** @internal */ +export type SharedJSDocType = + | SharedJSDocAllType + | SharedJSDocUnknownType + | SharedJSDocNonNullableType + | SharedJSDocNullableType + | SharedJSDocOptionalType + | SharedJSDocFunctionType + | SharedJSDocVariadicType + | SharedJSDocNamepathType + ; + +/** @internal */ +@Shared() +export class SharedJSDocAllType extends SharedNodeBase { + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocAllType; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocUnknownType extends SharedNodeBase { + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocUnknownType; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocNonNullableType extends SharedNodeBase { + @Shared() type!: SharedTypeNode; + @Shared() postfix!: boolean; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocNonNullableType; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocNullableType extends SharedNodeBase { + @Shared() type!: SharedTypeNode; + @Shared() postfix!: boolean; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocNullableType; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocOptionalType extends SharedNodeBase { + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocOptionalType; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocFunctionType extends HasLocals(HasSymbol(SharedNodeBase)) { + @Shared() typeParameters!: SharedNodeArray | undefined; + @Shared() parameters!: SharedNodeArray; + @Shared() type!: SharedTypeNode | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocFunctionType; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocVariadicType extends SharedNodeBase { + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocVariadicType; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocNamepathType extends SharedNodeBase { + @Shared() type!: SharedTypeNode; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocNamepathType; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocNode extends SharedNodeBase { + @Shared() tags!: SharedNodeArray | undefined; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDoc; + } +} + +/** @internal */ +export type SharedJSDocTag = + | SharedJSDocUnknownTag + | SharedJSDocAugmentsTag + | SharedJSDocImplementsTag + | SharedJSDocAuthorTag + | SharedJSDocDeprecatedTag + | SharedJSDocClassTag + | SharedJSDocPublicTag + | SharedJSDocPrivateTag + | SharedJSDocProtectedTag + | SharedJSDocReadonlyTag + | SharedJSDocOverrideTag + | SharedJSDocEnumTag + | SharedJSDocThisTag + | SharedJSDocTemplateTag + | SharedJSDocSeeTag + | SharedJSDocReturnTag + | SharedJSDocTypeTag + | SharedJSDocTypedefTag + | SharedJSDocCallbackTag + | SharedJSDocOverloadTag + | SharedJSDocThrowsTag + | SharedJSDocPropertyTag + | SharedJSDocParameterTag + | SharedJSDocSatisfiesTag + ; + +/** @internal */ +@Shared() +export class SharedJSDocLink extends SharedNodeBase { + @Shared() name!: SharedEntityName | SharedJSDocMemberName | undefined; + @Shared() text!: string; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocLink; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocLinkCode extends SharedNodeBase { + @Shared() name!: SharedEntityName | SharedJSDocMemberName | undefined; + @Shared() text!: string; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocLinkCode; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocLinkPlain extends SharedNodeBase { + @Shared() name!: SharedEntityName | SharedJSDocMemberName | undefined; + @Shared() text!: string; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocLinkPlain; + } +} + +/** @internal */ +export type SharedJSDocComment = + | SharedJSDocText + | SharedJSDocLink + | SharedJSDocLinkCode + | SharedJSDocLinkPlain + ; + +/** @internal */ +@Shared() +export class SharedJSDocText extends SharedNodeBase { + @Shared() text!: string; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocText; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocUnknownTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocAugmentsTag extends SharedNodeBase { + @Shared() class!: SharedExpressionWithTypeArguments & { readonly expression: SharedIdentifier | SharedPropertyAccessExpression }; + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocAugmentsTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocImplementsTag extends SharedNodeBase { + @Shared() class!: SharedExpressionWithTypeArguments & { readonly expression: SharedIdentifier | SharedPropertyAccessExpression }; + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocImplementsTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocAuthorTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocAuthorTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocDeprecatedTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocDeprecatedTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocClassTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocClassTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocPublicTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocPublicTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocPrivateTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocPrivateTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocProtectedTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocProtectedTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocReadonlyTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocReadonlyTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocOverrideTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocOverrideTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocEnumTag extends HasLocals(HasSymbol(SharedNodeBase)) { + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + @Shared() typeExpression!: SharedJSDocTypeExpression; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocEnumTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocThisTag extends SharedNodeBase { + @Shared() typeExpression!: SharedJSDocTypeExpression; + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocThisTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocTemplateTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() constraint!: SharedJSDocTypeExpression | undefined; + @Shared() typeParameters!: SharedNodeArray; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocTemplateTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocSeeTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() name!: SharedJSDocNameReference | undefined; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocSeeTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocReturnTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() typeExpression!: SharedJSDocTypeExpression | undefined; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocReturnTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocTypeTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() typeExpression!: SharedJSDocTypeExpression; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocTypeTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocTypedefTag extends HasLocals(HasSymbol(SharedNodeBase)) { + @Shared() tagName!: SharedIdentifier; + @Shared() fullName!: SharedJSDocNamespaceDeclaration | SharedIdentifier | undefined; + @Shared() name!: SharedIdentifier | undefined; + @Shared() typeExpression!: SharedJSDocTypeExpression | SharedJSDocTypeLiteral | undefined; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocTypedefTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocCallbackTag extends HasLocals(HasSymbol(SharedNodeBase)) { + @Shared() tagName!: SharedIdentifier; + @Shared() fullName!: SharedJSDocNamespaceDeclaration | SharedIdentifier | undefined; + @Shared() name!: SharedIdentifier | undefined; + @Shared() typeExpression!: SharedJSDocSignature; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocCallbackTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocOverloadTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() typeExpression!: SharedJSDocSignature; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocOverloadTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocThrowsTag extends SharedNodeBase { + @Shared() tagName!: SharedIdentifier; + @Shared() typeExpression!: SharedJSDocTypeExpression | undefined; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocThrowsTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocSignature extends HasLocals(HasSymbol(SharedNodeBase)) { + @Shared() typeParameters!: ReadonlySharedArray | undefined; + @Shared() parameters!: ReadonlySharedArray; + @Shared() type!: SharedJSDocReturnTag | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocSignature; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocPropertyTag extends HasSymbol(SharedNodeBase) { + @Shared() tagName!: SharedIdentifier; + @Shared() name!: SharedEntityName; + @Shared() typeExpression!: SharedJSDocTypeExpression | undefined; + @Shared() isNameFirst!: boolean; + @Shared() isBracketed!: boolean; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocPropertyTag; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocParameterTag extends HasSymbol(SharedNodeBase) { + @Shared() tagName!: SharedIdentifier; + @Shared() name!: SharedEntityName; + @Shared() typeExpression!: SharedJSDocTypeExpression | undefined; + @Shared() isNameFirst!: boolean; + @Shared() isBracketed!: boolean; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocParameterTag; + } +} + +/** @internal */ +export type SharedJSDocPropertyLikeTag = + | SharedJSDocPropertyTag + | SharedJSDocParameterTag + ; + +/** @internal */ +@Shared() +export class SharedJSDocTypeLiteral extends HasSymbol(SharedNodeBase) { + @Shared() jsDocPropertyTags!: ReadonlySharedArray | undefined; + @Shared() isArrayType!: boolean; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocTypeLiteral; + } +} + +/** @internal */ +@Shared() +export class SharedJSDocSatisfiesTag extends SharedNodeBase { + @Shared() typeExpression!: SharedJSDocTypeExpression; + @Shared() tagName!: SharedIdentifier; + @Shared() comment!: string | SharedNodeArray | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.JSDocSatisfiesTag; + } +} + +/** @internal */ +@Shared() +export class SharedFileReference extends Tagged(SharedStructBase, Tag.FileReference) { + @Shared() pos: number; + @Shared() end: number; + @Shared() fileName: string; + @Shared() resolutionMode: ResolutionMode | undefined; + constructor(pos: number, end: number, fileName: string, resolutionMode?: ResolutionMode) { + super(); + this.pos = pos; + this.end = end; + this.fileName = fileName; + this.resolutionMode = resolutionMode; + } +} + +/** @internal */ +@Shared() +export class SharedAmdDependency extends Tagged(SharedStructBase, Tag.AmdDependency) { + @Shared() path: string; + @Shared() name: string | undefined; + constructor(path: string, name?: string) { + super(); + this.path = path; + this.name = name; + } +} + +/** @internal */ +@Shared() +export class SharedCheckJsDirective extends Tagged(SharedStructBase, Tag.CheckJsDirective) { + @Shared() pos: number; + @Shared() end: number; + @Shared() enabled: boolean; + constructor(pos: number, end: number, enabled: boolean) { + super(); + this.pos = pos; + this.end = end; + this.enabled = enabled; + } +} + +/** @internal */ +@Shared() +export class SharedCommentRange extends SharedNodeBase { + @Shared() hasTrailingNewLine: boolean | undefined; +} + +/** @internal */ +@Shared() +export class SharedCommentDirective extends Tagged(SharedStructBase, Tag.CommentDirective) { + @Shared() range: SharedTextRange; + @Shared() type: CommentDirectiveType; + constructor(range: SharedTextRange, type: CommentDirectiveType) { + super(); + this.range = range; + this.type = type; + } +} + +/** @internal */ +@Shared() +export class SharedPragma extends Tagged(SharedStructBase, Tag.Pragma) { + @Shared() arguments: SharedPragmaArguments; + @Shared() range: SharedCommentRange; + constructor(args: SharedPragmaArguments, range: SharedCommentRange) { + super(); + this.arguments = args; + this.range = range; + } +} + +/** @internal */ +@Shared() +export class SharedPragmaArguments extends Tagged(SharedStructBase, Tag.PragmaArguments) { + @Shared() types: SharedPragmaSpan | undefined; + @Shared() lib: SharedPragmaSpan | undefined; + @Shared() path: SharedPragmaSpan | string | undefined; + @Shared() "no-default-lib": string | undefined; + @Shared() "resolution-mode": string | undefined; + @Shared() name: string | undefined; + @Shared() factory: string | undefined; +} + +/** @internal */ +@Shared() +export class SharedPragmaSpan extends Tagged(SharedStructBase, Tag.PragmaSpan) { + @Shared() pos: number; + @Shared() end: number; + @Shared() value: string; + constructor(pos: number, end: number, value: string) { + super(); + this.pos = pos; + this.end = end; + this.value = value; + } +} + +/** @internal */ +@Shared() +export class SharedSourceFile extends HasEndFlow(HasLocals(HasSymbol(SharedNodeBase))) { + @Shared() statements!: SharedNodeArray; + @Shared() endOfFileToken!: SharedEndOfFileToken; + @Shared() fileName!: string; + @Shared() path!: Path; + @Shared() text!: string; + @Shared() resolvedPath!: Path; + @Shared() originalFileName!: string; + @Shared() amdDependencies!: SharedArray; + @Shared() moduleName!: string | undefined; + @Shared() referencedFiles!: SharedArray; + @Shared() typeReferenceDirectives!: SharedArray; + @Shared() libReferenceDirectives!: SharedArray; + @Shared() languageVariant!: LanguageVariant; + @Shared() isDeclarationFile!: boolean; + @Shared() renamedDependencies!: SharedMap; + @Shared() hasNoDefaultLib!: boolean; + @Shared() languageVersion!: ScriptTarget; + @Shared() impliedNodeFormat!: ResolutionMode | undefined; + @Shared() scriptKind!: ScriptKind; + @Shared() pragmas!: SharedMap | SharedPragma>; + @Shared() externalModuleIndicator!: SharedNode | true | undefined; + @Shared() commonJsModuleIndicator!: SharedNode | undefined; + @Shared() identifiers!: SharedMap; + @Shared() nodeCount = 0; + @Shared() identifierCount = 0; + @Shared() symbolCount = 0; + @Shared() parseDiagnostics!: SharedArray; + @Shared() bindDiagnostics: undefined; + @Shared() bindSuggestionDiagnostics: undefined; + @Shared() lineMap: undefined; + @Shared() jsDocDiagnostics!: SharedArray | undefined; + @Shared() commentDirectives!: SharedArray | undefined; + @Shared() checkJsDirective!: SharedCheckJsDirective | undefined; + @Shared() version: string | undefined; + + static [Symbol.hasInstance](value: unknown) { + return value instanceof SharedNodeBase && value.kind === SyntaxKind.SourceFile; + } +} + +/** @internal */ +export type SharedNode = + | SharedToken + | SharedIdentifier + | SharedQualifiedName + | SharedComputedPropertyName + | SharedPrivateIdentifier + | SharedDecorator + | SharedTypeParameterDeclaration + | SharedCallSignatureDeclaration + | SharedConstructSignatureDeclaration + | SharedVariableDeclaration + | SharedVariableDeclarationList + | SharedParameterDeclaration + | SharedBindingElement + | SharedPropertySignature + | SharedPropertyDeclaration + | SharedPropertyAssignment + | SharedShorthandPropertyAssignment + | SharedSpreadAssignment + | SharedObjectBindingPattern + | SharedArrayBindingPattern + | SharedFunctionDeclaration + | SharedMethodSignature + | SharedMethodDeclaration + | SharedConstructorDeclaration + | SharedSemicolonClassElement + | SharedGetAccessorDeclaration + | SharedSetAccessorDeclaration + | SharedIndexSignatureDeclaration + | SharedClassStaticBlockDeclaration + | SharedImportTypeAssertionContainer + | SharedImportTypeNode + | SharedThisTypeNode + | SharedFunctionTypeNode + | SharedConstructorTypeNode + | SharedTypeReferenceNode + | SharedTypePredicateNode + | SharedTypeQueryNode + | SharedTypeLiteralNode + | SharedArrayTypeNode + | SharedTupleTypeNode + | SharedNamedTupleMember + | SharedOptionalTypeNode + | SharedRestTypeNode + | SharedUnionTypeNode + | SharedIntersectionTypeNode + | SharedConditionalTypeNode + | SharedInferTypeNode + | SharedParenthesizedTypeNode + | SharedTypeOperatorNode + | SharedIndexedAccessTypeNode + | SharedMappedTypeNode + | SharedLiteralTypeNode + | SharedStringLiteral + | SharedTemplateLiteralTypeNode + | SharedTemplateLiteralTypeSpan + | SharedOmittedExpression + | SharedPrefixUnaryExpression + | SharedPostfixUnaryExpression + | SharedDeleteExpression + | SharedTypeOfExpression + | SharedVoidExpression + | SharedAwaitExpression + | SharedYieldExpression + | SharedBinaryExpression + | SharedConditionalExpression + | SharedFunctionExpression + | SharedArrowFunction + | SharedRegularExpressionLiteral + | SharedNoSubstitutionTemplateLiteral + | SharedNumericLiteral + | SharedBigIntLiteral + | SharedTemplateHead + | SharedTemplateMiddle + | SharedTemplateTail + | SharedTemplateExpression + | SharedTemplateSpan + | SharedParenthesizedExpression + | SharedArrayLiteralExpression + | SharedSpreadElement + | SharedObjectLiteralExpression + | SharedPropertyAccessExpression + | SharedElementAccessExpression + | SharedCallExpression + | SharedExpressionWithTypeArguments + | SharedNewExpression + | SharedTaggedTemplateExpression + | SharedAsExpression + | SharedTypeAssertion + | SharedSatisfiesExpression + | SharedNonNullExpression + | SharedMetaProperty + | SharedJsxElement + | SharedJsxAttributes + | SharedJsxNamespacedName + | SharedJsxOpeningElement + | SharedJsxSelfClosingElement + | SharedJsxFragment + | SharedJsxOpeningFragment + | SharedJsxClosingFragment + | SharedJsxAttribute + | SharedJsxSpreadAttribute + | SharedJsxClosingElement + | SharedJsxExpression + | SharedJsxText + | SharedEmptyStatement + | SharedDebuggerStatement + | SharedMissingDeclaration + | SharedBlock + | SharedVariableStatement + | SharedExpressionStatement + | SharedIfStatement + | SharedDoStatement + | SharedWhileStatement + | SharedForStatement + | SharedForInStatement + | SharedForOfStatement + | SharedBreakStatement + | SharedContinueStatement + | SharedReturnStatement + | SharedWithStatement + | SharedSwitchStatement + | SharedCaseBlock + | SharedCaseClause + | SharedDefaultClause + | SharedLabeledStatement + | SharedThrowStatement + | SharedTryStatement + | SharedCatchClause + | SharedClassDeclaration + | SharedClassExpression + | SharedInterfaceDeclaration + | SharedHeritageClause + | SharedTypeAliasDeclaration + | SharedEnumMember + | SharedEnumDeclaration + | SharedModuleDeclaration + | SharedNamespaceDeclaration + | SharedJSDocNamespaceDeclaration + | SharedModuleBlock + | SharedImportEqualsDeclaration + | SharedExternalModuleReference + | SharedImportDeclaration + | SharedImportClause + | SharedAssertEntry + | SharedAssertClause + | SharedNamespaceImport + | SharedNamespaceExport + | SharedNamespaceExportDeclaration + | SharedExportDeclaration + | SharedNamedImports + | SharedNamedExports + | SharedImportSpecifier + | SharedExportSpecifier + | SharedExportAssignment + | SharedJSDocTypeExpression + | SharedJSDocNameReference + | SharedJSDocMemberName + | SharedJSDocAllType + | SharedJSDocUnknownType + | SharedJSDocNonNullableType + | SharedJSDocNullableType + | SharedJSDocOptionalType + | SharedJSDocFunctionType + | SharedJSDocVariadicType + | SharedJSDocNamepathType + | SharedJSDocNode + | SharedJSDocLink + | SharedJSDocLinkCode + | SharedJSDocLinkPlain + | SharedJSDocText + | SharedJSDocUnknownTag + | SharedJSDocAugmentsTag + | SharedJSDocImplementsTag + | SharedJSDocAuthorTag + | SharedJSDocDeprecatedTag + | SharedJSDocClassTag + | SharedJSDocPublicTag + | SharedJSDocPrivateTag + | SharedJSDocProtectedTag + | SharedJSDocReadonlyTag + | SharedJSDocOverrideTag + | SharedJSDocEnumTag + | SharedJSDocThisTag + | SharedJSDocTemplateTag + | SharedJSDocSeeTag + | SharedJSDocReturnTag + | SharedJSDocTypeTag + | SharedJSDocTypedefTag + | SharedJSDocCallbackTag + | SharedJSDocOverloadTag + | SharedJSDocThrowsTag + | SharedJSDocSignature + | SharedJSDocPropertyTag + | SharedJSDocParameterTag + | SharedJSDocTypeLiteral + | SharedJSDocSatisfiesTag + | SharedSourceFile + ; + +const sharedNodeTypes = { + // [TokenSyntaxKind]: SharedToken, + [SyntaxKind.EndOfFileToken]: SharedEndOfFileToken, + [SyntaxKind.ThisKeyword]: SharedThisExpression, + [SyntaxKind.SuperKeyword]: SharedSuperExpression, + [SyntaxKind.NumericLiteral]: SharedNumericLiteral, + [SyntaxKind.BigIntLiteral]: SharedBigIntLiteral, + [SyntaxKind.StringLiteral]: SharedStringLiteral, + [SyntaxKind.JsxText]: SharedJsxText, + [SyntaxKind.JsxTextAllWhiteSpaces]: SharedJsxText, + [SyntaxKind.RegularExpressionLiteral]: SharedRegularExpressionLiteral, + [SyntaxKind.NoSubstitutionTemplateLiteral]: SharedNoSubstitutionTemplateLiteral, + [SyntaxKind.TemplateHead]: SharedTemplateHead, + [SyntaxKind.TemplateMiddle]: SharedTemplateMiddle, + [SyntaxKind.TemplateTail]: SharedTemplateTail, + [SyntaxKind.Identifier]: SharedIdentifier, + + [SyntaxKind.QualifiedName]: SharedQualifiedName, + [SyntaxKind.ComputedPropertyName]: SharedComputedPropertyName, + [SyntaxKind.PrivateIdentifier]: SharedPrivateIdentifier, + [SyntaxKind.Decorator]: SharedDecorator, + [SyntaxKind.TypeParameter]: SharedTypeParameterDeclaration, + [SyntaxKind.CallSignature]: SharedCallSignatureDeclaration, + [SyntaxKind.ConstructSignature]: SharedConstructSignatureDeclaration, + [SyntaxKind.VariableDeclaration]: SharedVariableDeclaration, + [SyntaxKind.VariableDeclarationList]: SharedVariableDeclarationList, + [SyntaxKind.Parameter]: SharedParameterDeclaration, + [SyntaxKind.BindingElement]: SharedBindingElement, + [SyntaxKind.PropertySignature]: SharedPropertySignature, + [SyntaxKind.PropertyDeclaration]: SharedPropertyDeclaration, + [SyntaxKind.PropertyAssignment]: SharedPropertyAssignment, + [SyntaxKind.ShorthandPropertyAssignment]: SharedShorthandPropertyAssignment, + [SyntaxKind.SpreadAssignment]: SharedSpreadAssignment, + [SyntaxKind.ObjectBindingPattern]: SharedObjectBindingPattern, + [SyntaxKind.ArrayBindingPattern]: SharedArrayBindingPattern, + [SyntaxKind.FunctionDeclaration]: SharedFunctionDeclaration, + [SyntaxKind.MethodSignature]: SharedMethodSignature, + [SyntaxKind.MethodDeclaration]: SharedMethodDeclaration, + [SyntaxKind.Constructor]: SharedConstructorDeclaration, + [SyntaxKind.SemicolonClassElement]: SharedSemicolonClassElement, + [SyntaxKind.GetAccessor]: SharedGetAccessorDeclaration, + [SyntaxKind.SetAccessor]: SharedSetAccessorDeclaration, + [SyntaxKind.IndexSignature]: SharedIndexSignatureDeclaration, + [SyntaxKind.ClassStaticBlockDeclaration]: SharedClassStaticBlockDeclaration, + [SyntaxKind.ImportTypeAssertionContainer]: SharedImportTypeAssertionContainer, + [SyntaxKind.ImportType]: SharedImportTypeNode, + [SyntaxKind.ThisType]: SharedThisTypeNode, + [SyntaxKind.FunctionType]: SharedFunctionTypeNode, + [SyntaxKind.ConstructorType]: SharedConstructorTypeNode, + [SyntaxKind.TypeReference]: SharedTypeReferenceNode, + [SyntaxKind.TypePredicate]: SharedTypePredicateNode, + [SyntaxKind.TypeQuery]: SharedTypeQueryNode, + [SyntaxKind.TypeLiteral]: SharedTypeLiteralNode, + [SyntaxKind.ArrayType]: SharedArrayTypeNode, + [SyntaxKind.TupleType]: SharedTupleTypeNode, + [SyntaxKind.NamedTupleMember]: SharedNamedTupleMember, + [SyntaxKind.OptionalType]: SharedOptionalTypeNode, + [SyntaxKind.RestType]: SharedRestTypeNode, + [SyntaxKind.UnionType]: SharedUnionTypeNode, + [SyntaxKind.IntersectionType]: SharedIntersectionTypeNode, + [SyntaxKind.ConditionalType]: SharedConditionalTypeNode, + [SyntaxKind.InferType]: SharedInferTypeNode, + [SyntaxKind.ParenthesizedType]: SharedParenthesizedTypeNode, + [SyntaxKind.TypeOperator]: SharedTypeOperatorNode, + [SyntaxKind.IndexedAccessType]: SharedIndexedAccessTypeNode, + [SyntaxKind.MappedType]: SharedMappedTypeNode, + [SyntaxKind.LiteralType]: SharedLiteralTypeNode, + [SyntaxKind.TemplateLiteralType]: SharedTemplateLiteralTypeNode, + [SyntaxKind.TemplateLiteralTypeSpan]: SharedTemplateLiteralTypeSpan, + [SyntaxKind.OmittedExpression]: SharedOmittedExpression, + [SyntaxKind.PrefixUnaryExpression]: SharedPrefixUnaryExpression, + [SyntaxKind.PostfixUnaryExpression]: SharedPostfixUnaryExpression, + [SyntaxKind.DeleteExpression]: SharedDeleteExpression, + [SyntaxKind.TypeOfExpression]: SharedTypeOfExpression, + [SyntaxKind.VoidExpression]: SharedVoidExpression, + [SyntaxKind.AwaitExpression]: SharedAwaitExpression, + [SyntaxKind.YieldExpression]: SharedYieldExpression, + [SyntaxKind.BinaryExpression]: SharedBinaryExpression, + [SyntaxKind.ConditionalExpression]: SharedConditionalExpression, + [SyntaxKind.FunctionExpression]: SharedFunctionExpression, + [SyntaxKind.ArrowFunction]: SharedArrowFunction, + [SyntaxKind.TemplateExpression]: SharedTemplateExpression, + [SyntaxKind.TemplateSpan]: SharedTemplateSpan, + [SyntaxKind.ParenthesizedExpression]: SharedParenthesizedExpression, + [SyntaxKind.ArrayLiteralExpression]: SharedArrayLiteralExpression, + [SyntaxKind.SpreadElement]: SharedSpreadElement, + [SyntaxKind.ObjectLiteralExpression]: SharedObjectLiteralExpression, + [SyntaxKind.PropertyAccessExpression]: SharedPropertyAccessExpression, + [SyntaxKind.ElementAccessExpression]: SharedElementAccessExpression, + [SyntaxKind.CallExpression]: SharedCallExpression, + [SyntaxKind.ExpressionWithTypeArguments]: SharedExpressionWithTypeArguments, + [SyntaxKind.NewExpression]: SharedNewExpression, + [SyntaxKind.TaggedTemplateExpression]: SharedTaggedTemplateExpression, + [SyntaxKind.AsExpression]: SharedAsExpression, + [SyntaxKind.TypeAssertionExpression]: SharedTypeAssertion, + [SyntaxKind.SatisfiesExpression]: SharedSatisfiesExpression, + [SyntaxKind.NonNullExpression]: SharedNonNullExpression, + [SyntaxKind.MetaProperty]: SharedMetaProperty, + [SyntaxKind.JsxElement]: SharedJsxElement, + [SyntaxKind.JsxAttributes]: SharedJsxAttributes, + [SyntaxKind.JsxNamespacedName]: SharedJsxNamespacedName, + [SyntaxKind.JsxOpeningElement]: SharedJsxOpeningElement, + [SyntaxKind.JsxSelfClosingElement]: SharedJsxSelfClosingElement, + [SyntaxKind.JsxFragment]: SharedJsxFragment, + [SyntaxKind.JsxOpeningFragment]: SharedJsxOpeningFragment, + [SyntaxKind.JsxClosingFragment]: SharedJsxClosingFragment, + [SyntaxKind.JsxAttribute]: SharedJsxAttribute, + [SyntaxKind.JsxSpreadAttribute]: SharedJsxSpreadAttribute, + [SyntaxKind.JsxClosingElement]: SharedJsxClosingElement, + [SyntaxKind.JsxExpression]: SharedJsxExpression, + [SyntaxKind.EmptyStatement]: SharedEmptyStatement, + [SyntaxKind.DebuggerStatement]: SharedDebuggerStatement, + [SyntaxKind.MissingDeclaration]: SharedMissingDeclaration, + [SyntaxKind.Block]: SharedBlock, + [SyntaxKind.VariableStatement]: SharedVariableStatement, + [SyntaxKind.ExpressionStatement]: SharedExpressionStatement, + [SyntaxKind.IfStatement]: SharedIfStatement, + [SyntaxKind.DoStatement]: SharedDoStatement, + [SyntaxKind.WhileStatement]: SharedWhileStatement, + [SyntaxKind.ForStatement]: SharedForStatement, + [SyntaxKind.ForInStatement]: SharedForInStatement, + [SyntaxKind.ForOfStatement]: SharedForOfStatement, + [SyntaxKind.BreakStatement]: SharedBreakStatement, + [SyntaxKind.ContinueStatement]: SharedContinueStatement, + [SyntaxKind.ReturnStatement]: SharedReturnStatement, + [SyntaxKind.WithStatement]: SharedWithStatement, + [SyntaxKind.SwitchStatement]: SharedSwitchStatement, + [SyntaxKind.CaseBlock]: SharedCaseBlock, + [SyntaxKind.CaseClause]: SharedCaseClause, + [SyntaxKind.DefaultClause]: SharedDefaultClause, + [SyntaxKind.LabeledStatement]: SharedLabeledStatement, + [SyntaxKind.ThrowStatement]: SharedThrowStatement, + [SyntaxKind.TryStatement]: SharedTryStatement, + [SyntaxKind.CatchClause]: SharedCatchClause, + [SyntaxKind.ClassDeclaration]: SharedClassDeclaration, + [SyntaxKind.ClassExpression]: SharedClassExpression, + [SyntaxKind.InterfaceDeclaration]: SharedInterfaceDeclaration, + [SyntaxKind.HeritageClause]: SharedHeritageClause, + [SyntaxKind.TypeAliasDeclaration]: SharedTypeAliasDeclaration, + [SyntaxKind.EnumMember]: SharedEnumMember, + [SyntaxKind.EnumDeclaration]: SharedEnumDeclaration, + [SyntaxKind.ModuleDeclaration]: SharedModuleDeclaration, + [SyntaxKind.ModuleBlock]: SharedModuleBlock, + [SyntaxKind.ImportEqualsDeclaration]: SharedImportEqualsDeclaration, + [SyntaxKind.ExternalModuleReference]: SharedExternalModuleReference, + [SyntaxKind.ImportDeclaration]: SharedImportDeclaration, + [SyntaxKind.ImportClause]: SharedImportClause, + [SyntaxKind.AssertEntry]: SharedAssertEntry, + [SyntaxKind.AssertClause]: SharedAssertClause, + [SyntaxKind.NamespaceImport]: SharedNamespaceImport, + [SyntaxKind.NamespaceExport]: SharedNamespaceExport, + [SyntaxKind.NamespaceExportDeclaration]: SharedNamespaceExportDeclaration, + [SyntaxKind.ExportDeclaration]: SharedExportDeclaration, + [SyntaxKind.NamedImports]: SharedNamedImports, + [SyntaxKind.NamedExports]: SharedNamedExports, + [SyntaxKind.ImportSpecifier]: SharedImportSpecifier, + [SyntaxKind.ExportSpecifier]: SharedExportSpecifier, + [SyntaxKind.ExportAssignment]: SharedExportAssignment, + [SyntaxKind.JSDocTypeExpression]: SharedJSDocTypeExpression, + [SyntaxKind.JSDocNameReference]: SharedJSDocNameReference, + [SyntaxKind.JSDocMemberName]: SharedJSDocMemberName, + [SyntaxKind.JSDocAllType]: SharedJSDocAllType, + [SyntaxKind.JSDocUnknownType]: SharedJSDocUnknownType, + [SyntaxKind.JSDocNonNullableType]: SharedJSDocNonNullableType, + [SyntaxKind.JSDocNullableType]: SharedJSDocNullableType, + [SyntaxKind.JSDocOptionalType]: SharedJSDocOptionalType, + [SyntaxKind.JSDocFunctionType]: SharedJSDocFunctionType, + [SyntaxKind.JSDocVariadicType]: SharedJSDocVariadicType, + [SyntaxKind.JSDocNamepathType]: SharedJSDocNamepathType, + [SyntaxKind.JSDoc]: SharedJSDocNode, + [SyntaxKind.JSDocLink]: SharedJSDocLink, + [SyntaxKind.JSDocLinkCode]: SharedJSDocLinkCode, + [SyntaxKind.JSDocLinkPlain]: SharedJSDocLinkPlain, + [SyntaxKind.JSDocText]: SharedJSDocText, + [SyntaxKind.JSDocTag]: SharedJSDocUnknownTag, + [SyntaxKind.JSDocAugmentsTag]: SharedJSDocAugmentsTag, + [SyntaxKind.JSDocImplementsTag]: SharedJSDocImplementsTag, + [SyntaxKind.JSDocAuthorTag]: SharedJSDocAuthorTag, + [SyntaxKind.JSDocDeprecatedTag]: SharedJSDocDeprecatedTag, + [SyntaxKind.JSDocClassTag]: SharedJSDocClassTag, + [SyntaxKind.JSDocPublicTag]: SharedJSDocPublicTag, + [SyntaxKind.JSDocPrivateTag]: SharedJSDocPrivateTag, + [SyntaxKind.JSDocProtectedTag]: SharedJSDocProtectedTag, + [SyntaxKind.JSDocReadonlyTag]: SharedJSDocReadonlyTag, + [SyntaxKind.JSDocOverrideTag]: SharedJSDocOverrideTag, + [SyntaxKind.JSDocEnumTag]: SharedJSDocEnumTag, + [SyntaxKind.JSDocThisTag]: SharedJSDocThisTag, + [SyntaxKind.JSDocTemplateTag]: SharedJSDocTemplateTag, + [SyntaxKind.JSDocSeeTag]: SharedJSDocSeeTag, + [SyntaxKind.JSDocReturnTag]: SharedJSDocReturnTag, + [SyntaxKind.JSDocTypeTag]: SharedJSDocTypeTag, + [SyntaxKind.JSDocTypedefTag]: SharedJSDocTypedefTag, + [SyntaxKind.JSDocCallbackTag]: SharedJSDocCallbackTag, + [SyntaxKind.JSDocOverloadTag]: SharedJSDocOverloadTag, + [SyntaxKind.JSDocThrowsTag]: SharedJSDocThrowsTag, + [SyntaxKind.JSDocSignature]: SharedJSDocSignature, + [SyntaxKind.JSDocPropertyTag]: SharedJSDocPropertyTag, + [SyntaxKind.JSDocParameterTag]: SharedJSDocParameterTag, + [SyntaxKind.JSDocTypeLiteral]: SharedJSDocTypeLiteral, + [SyntaxKind.JSDocSatisfiesTag]: SharedJSDocSatisfiesTag, + [SyntaxKind.SourceFile]: SharedSourceFile, +}; + +/** @internal */ +export function getSharedConstructorForKind(kind: SyntaxKind): new () => SharedNode { + const type = (sharedNodeTypes as any)[kind] ?? (isTokenKind(kind) ? SharedToken : undefined); + Debug.assertIsDefined(type); + return type; +} + +// Mixins + +/** @internal */ +export interface SharedJSDocContainer extends SharedNodeBase { + jsDoc: SharedArray | undefined; +} + +function HasJSDoc SharedNodeBase>(base: F): F & (abstract new (...args: any) => SharedJSDocContainer) { + @Shared({ abstract: true }) + abstract class HasJSDoc extends base { + @Shared() jsDoc: SharedArray | undefined; + } + return HasJSDoc; +} + +/** @internal */ +export interface SharedLocalsContainer extends SharedNodeBase { + locals: undefined; + nextContainer: undefined; +} + +function HasLocals SharedNodeBase>(base: F): F & (abstract new (...args: any) => SharedLocalsContainer) { + @Shared({ abstract: true }) + abstract class HasLocals extends base { + @Shared() locals: undefined; + @Shared() nextContainer: undefined; + } + return HasLocals; +} + +/** @internal */ +export interface SharedFlowNodeContainer extends SharedNodeBase { + flowNode: undefined; +} + +function HasFlowNode SharedNodeBase>(base: F): F & (abstract new (...args: any) => SharedFlowNodeContainer) { + @Shared({ abstract: true }) + abstract class HasFlowNode extends base { + @Shared() flowNode: undefined; + } + return HasFlowNode; +} + +/** @internal */ +export interface SharedSymbolContainer extends SharedNodeBase { + symbol: undefined; + localSymbol: undefined; +} + +function HasSymbol SharedNodeBase>(base: F): F & (abstract new (...args: any) => SharedSymbolContainer) { + @Shared({ abstract: true }) + abstract class HasSymbol extends base { + @Shared() symbol: undefined; + @Shared() localSymbol: undefined; + } + return HasSymbol; +} + +/** @internal */ +export interface SharedEndFlowContainer extends SharedNodeBase { + endFlowNode: undefined; +} + +function HasEndFlow SharedNodeBase>(base: F): F & (abstract new (...args: any) => SharedEndFlowContainer) { + @Shared({ abstract: true }) + abstract class HasEndFlow extends base { + @Shared() endFlowNode: undefined; + } + return HasEndFlow; +} + +/** @internal */ +export interface SharedFunctionFlowContainer extends SharedEndFlowContainer { + returnFlowNode: undefined; +} + +function HasFunctionFlow SharedNodeBase>(base: F): F & (abstract new (...args: any) => SharedFunctionFlowContainer) { + @Shared({ abstract: true }) + abstract class HasFunctionFlow extends HasEndFlow(base) { + @Shared() returnFlowNode: undefined; + } + return HasFunctionFlow; +} diff --git a/src/compiler/sharing/sharedNodeAdapter.ts b/src/compiler/sharing/sharedNodeAdapter.ts new file mode 100644 index 00000000000..9ea4d81bc79 --- /dev/null +++ b/src/compiler/sharing/sharedNodeAdapter.ts @@ -0,0 +1,2545 @@ +import { AmdDependency, CommentDirective, Debug, Diagnostic, DiagnosticMessageChain, DiagnosticRelatedInformation, DiagnosticWithLocation, FileReference, getJSDocTypeAliasName, hasProperty, JSDocPropertyLikeTag, ReadonlyPragmaMap, setParent, SharedMap } from "../_namespaces/ts"; +import { identity } from "../core"; +import { ArrayBindingElement, ArrayBindingPattern, ArrayLiteralExpression, ArrayTypeNode, ArrowFunction, AsExpression, AssertClause, AssertEntry, AssertionKey, AwaitExpression, BigIntLiteral, BinaryExpression, BindingElement, BindingName, BindingPattern, Block, BreakStatement, CallExpression, CallSignatureDeclaration, CaseBlock, CaseClause, CaseOrDefaultClause, CatchClause, ClassDeclaration, ClassElement, ClassExpression, ClassStaticBlockDeclaration, ComputedPropertyName, ConciseBody, ConditionalExpression, ConditionalTypeNode, ConstructorDeclaration, ConstructorTypeNode, ConstructSignatureDeclaration, ContinueStatement, DebuggerStatement, Decorator, DefaultClause, DeleteExpression, DoStatement, ElementAccessExpression, EmptyStatement, EndOfFileToken, EntityName, EnumDeclaration, EnumMember, ExportAssignment, ExportDeclaration, ExportSpecifier, Expression, ExpressionStatement, ExpressionWithTypeArguments, ExternalModuleReference, FalseLiteral, ForInitializer, ForInStatement, ForOfStatement, ForStatement, FunctionDeclaration, FunctionExpression, FunctionTypeNode, GetAccessorDeclaration, HeritageClause, Identifier, IfStatement, ImportClause, ImportDeclaration, ImportEqualsDeclaration, ImportExpression, ImportSpecifier, ImportTypeAssertionContainer, ImportTypeNode, IndexedAccessTypeNode, IndexSignatureDeclaration, InferTypeNode, InterfaceDeclaration, IntersectionTypeNode, JSDoc, JSDocAllType, JSDocAugmentsTag, JSDocAuthorTag, JSDocCallbackTag, JSDocClassTag, JSDocComment, JSDocContainer, JSDocDeprecatedTag, JSDocEnumTag, JSDocFunctionType, JSDocImplementsTag, JSDocLink, JSDocLinkCode, JSDocLinkPlain, JSDocMemberName, JSDocNamepathType, JSDocNameReference, JSDocNamespaceDeclaration, JSDocNonNullableType, JSDocNullableType, JSDocOptionalType, JSDocOverloadTag, JSDocOverrideTag, JSDocParameterTag, JSDocPrivateTag, JSDocPropertyTag, JSDocProtectedTag, JSDocPublicTag, JSDocReadonlyTag, JSDocReturnTag, JSDocSatisfiesTag, JSDocSeeTag, JSDocSignature, JSDocTag, JSDocTemplateTag, JSDocText, JSDocThisTag, JSDocThrowsTag, JSDocTypedefTag, JSDocTypeExpression, JSDocTypeLiteral, JSDocTypeTag, JSDocUnknownTag, JSDocUnknownType, JSDocVariadicType, JsxAttribute, JsxAttributeLike, JsxAttributeName, JsxAttributes, JsxAttributeValue, JsxChild, JsxClosingElement, JsxClosingFragment, JsxElement, JsxExpression, JsxFragment, JsxNamespacedName, JsxOpeningElement, JsxOpeningFragment, JsxSelfClosingElement, JsxSpreadAttribute, JsxTagNameExpression, JsxTagNamePropertyAccess, JsxText, KeywordTypeNode, LabeledStatement, LeftHandSideExpression, LiteralTypeNode, MappedTypeNode, MemberExpression, MemberName, MetaProperty, MethodDeclaration, MethodSignature, MissingDeclaration, Modifier, ModifierLike, ModuleBlock, ModuleBody, ModuleDeclaration, ModuleName, ModuleReference, MutableNodeArray, NamedExportBindings, NamedExports, NamedImportBindings, NamedImports, NamedTupleMember, NamespaceDeclaration, NamespaceExport, NamespaceExportDeclaration, NamespaceImport, NewExpression, Node, NodeArray, NodeFactory, NonNullExpression, NoSubstitutionTemplateLiteral, NullLiteral, NumericLiteral, ObjectBindingPattern, ObjectLiteralElementLike, ObjectLiteralExpression, OmittedExpression, OptionalTypeNode, ParameterDeclaration, ParenthesizedExpression, ParenthesizedTypeNode, PostfixUnaryExpression, PrefixUnaryExpression, PrimaryExpression, PrivateIdentifier, PropertyAccessEntityNameExpression, PropertyAccessExpression, PropertyAssignment, PropertyDeclaration, PropertyName, PropertySignature, QualifiedName, RegularExpressionLiteral, RestTypeNode, ReturnStatement, SatisfiesExpression, SemicolonClassElement, SetAccessorDeclaration, ShorthandPropertyAssignment, SourceFile, SpreadAssignment, SpreadElement, Statement, StringLiteral, SuperExpression, SwitchStatement, SyntaxKind, TaggedTemplateExpression, TemplateExpression, TemplateHead, TemplateLiteral, TemplateLiteralTypeNode, TemplateLiteralTypeSpan, TemplateMiddle, TemplateSpan, TemplateTail, ThisExpression, ThisTypeNode, ThrowStatement, Token, TokenSyntaxKind, TrueLiteral, TryStatement, TupleTypeNode, TypeAliasDeclaration, TypeAssertion, TypeElement, TypeLiteralNode, TypeNode, TypeOfExpression, TypeOperatorNode, TypeParameterDeclaration, TypePredicateNode, TypeQueryNode, TypeReferenceNode, UnaryExpression, UnionTypeNode, VariableDeclaration, VariableDeclarationList, VariableStatement, VoidExpression, WhileStatement, WithStatement, YieldExpression } from "../types"; +import { Mutable } from "../utilities"; +import { SharedDiagnostic, SharedDiagnosticMessageChain, SharedDiagnosticRelatedInformation, SharedDiagnosticWithLocation } from "./sharedDiagnostics"; +import { SharedArrayBindingElement, SharedArrayBindingPattern, SharedArrayLiteralExpression, SharedArrayTypeNode, SharedArrowFunction, SharedAsExpression, SharedAssertClause, SharedAssertEntry, SharedAssertionKey, SharedAwaitExpression, SharedBigIntLiteral, SharedBinaryExpression, SharedBindingElement, SharedBindingName, SharedBindingPattern, SharedBlock, SharedBreakStatement, SharedCallExpression, SharedCallSignatureDeclaration, SharedCaseBlock, SharedCaseClause, SharedCaseOrDefaultClause, SharedCatchClause, SharedClassDeclaration, SharedClassElement, SharedClassExpression, SharedClassStaticBlockDeclaration, SharedComputedPropertyName, SharedConciseBody, SharedConditionalExpression, SharedConditionalTypeNode, SharedConstructorDeclaration, SharedConstructorTypeNode, SharedConstructSignatureDeclaration, SharedContinueStatement, SharedDebuggerStatement, SharedDecorator, SharedDefaultClause, SharedDeleteExpression, SharedDoStatement, SharedElementAccessExpression, SharedEmptyStatement, SharedEndOfFileToken, SharedEntityName, SharedEnumDeclaration, SharedEnumMember, SharedExportAssignment, SharedExportDeclaration, SharedExportSpecifier, SharedExpression, SharedExpressionStatement, SharedExpressionWithTypeArguments, SharedExternalModuleReference, SharedForInitializer, SharedForInStatement, SharedForOfStatement, SharedForStatement, SharedFunctionDeclaration, SharedFunctionExpression, SharedFunctionTypeNode, SharedGetAccessorDeclaration, SharedHeritageClause, SharedIdentifier, SharedIfStatement, SharedImportClause, SharedImportDeclaration, SharedImportEqualsDeclaration, SharedImportSpecifier, SharedImportTypeAssertionContainer, SharedImportTypeNode, SharedIndexedAccessTypeNode, SharedIndexSignatureDeclaration, SharedInferTypeNode, SharedInterfaceDeclaration, SharedIntersectionTypeNode, SharedJSDocAllType, SharedJSDocAugmentsTag, SharedJSDocAuthorTag, SharedJSDocCallbackTag, SharedJSDocClassTag, SharedJSDocComment, SharedJSDocContainer, SharedJSDocDeprecatedTag, SharedJSDocEnumTag, SharedJSDocFunctionType, SharedJSDocImplementsTag, SharedJSDocLink, SharedJSDocLinkCode, SharedJSDocLinkPlain, SharedJSDocMemberName, SharedJSDocNamepathType, SharedJSDocNameReference, SharedJSDocNamespaceDeclaration, SharedJSDocNode, SharedJSDocNonNullableType, SharedJSDocNullableType, SharedJSDocOptionalType, SharedJSDocOverloadTag, SharedJSDocOverrideTag, SharedJSDocParameterTag, SharedJSDocPrivateTag, SharedJSDocPropertyLikeTag, SharedJSDocPropertyTag, SharedJSDocProtectedTag, SharedJSDocPublicTag, SharedJSDocReadonlyTag, SharedJSDocReturnTag, SharedJSDocSatisfiesTag, SharedJSDocSeeTag, SharedJSDocSignature, SharedJSDocTag, SharedJSDocTemplateTag, SharedJSDocText, SharedJSDocThisTag, SharedJSDocThrowsTag, SharedJSDocTypedefTag, SharedJSDocTypeExpression, SharedJSDocTypeLiteral, SharedJSDocTypeTag, SharedJSDocUnknownTag, SharedJSDocUnknownType, SharedJSDocVariadicType, SharedJsxAttribute, SharedJsxAttributeLike, SharedJsxAttributeName, SharedJsxAttributes, SharedJsxAttributeValue, SharedJsxChild, SharedJsxClosingElement, SharedJsxClosingFragment, SharedJsxElement, SharedJsxExpression, SharedJsxFragment, SharedJsxNamespacedName, SharedJsxOpeningElement, SharedJsxOpeningFragment, SharedJsxSelfClosingElement, SharedJsxSpreadAttribute, SharedJsxTagNameExpression, SharedJsxText, SharedLabeledStatement, SharedLeftHandSideExpression, SharedLiteralTypeNode, SharedMappedTypeNode, SharedMemberExpression, SharedMemberName, SharedMetaProperty, SharedMethodDeclaration, SharedMethodSignature, SharedMissingDeclaration, SharedModifier, SharedModifierLike, SharedModuleBlock, SharedModuleBody, SharedModuleDeclaration, SharedModuleName, SharedModuleReference, SharedNamedExportBindings, SharedNamedExports, SharedNamedImportBindings, SharedNamedImports, SharedNamedTupleMember, SharedNamespaceExport, SharedNamespaceExportDeclaration, SharedNamespaceImport, SharedNewExpression, SharedNodeBase, SharedNonNullExpression, SharedNoSubstitutionTemplateLiteral, SharedNumericLiteral, SharedObjectBindingPattern, SharedObjectLiteralElement, SharedObjectLiteralExpression, SharedOmittedExpression, SharedOptionalTypeNode, SharedParameterDeclaration, SharedParenthesizedExpression, SharedParenthesizedTypeNode, SharedPostfixUnaryExpression, SharedPrefixUnaryExpression, SharedPrimaryExpression, SharedPrivateIdentifier, SharedPropertyAccessExpression, SharedPropertyAssignment, SharedPropertyDeclaration, SharedPropertyName, SharedPropertySignature, SharedQualifiedName, SharedRegularExpressionLiteral, SharedRestTypeNode, SharedReturnStatement, SharedSatisfiesExpression, SharedSemicolonClassElement, SharedSetAccessorDeclaration, SharedShorthandPropertyAssignment, SharedSourceFile, SharedSpreadAssignment, SharedSpreadElement, SharedStatement, SharedStringLiteral, SharedSwitchStatement, SharedTaggedTemplateExpression, SharedTemplateExpression, SharedTemplateHead, SharedTemplateLiteral, SharedTemplateLiteralTypeNode, SharedTemplateLiteralTypeSpan, SharedTemplateMiddle, SharedTemplateSpan, SharedTemplateTail, SharedThisTypeNode, SharedThrowStatement, SharedToken, SharedTryStatement, SharedTupleTypeNode, SharedTypeAliasDeclaration, SharedTypeAssertion, SharedTypeElement, SharedTypeLiteralNode, SharedTypeNode, SharedTypeOfExpression, SharedTypeOperatorNode, SharedTypeParameterDeclaration, SharedTypePredicateNode, SharedTypeQueryNode, SharedTypeReferenceNode, SharedUnaryExpression, SharedUnionTypeNode, SharedUpdateExpression, SharedVariableDeclaration, SharedVariableDeclarationList, SharedVariableStatement, SharedVoidExpression, SharedWhileStatement, SharedWithStatement, SharedYieldExpression } from "./sharedNode"; +import { SharedNodeArray } from "./sharedNodeArray"; +import { isSharedArray } from "./structs/shareable"; + +/** @internal */ +export function adoptSharedSourceFile(sharedFile: SharedSourceFile, factory: NodeFactory, sharedCache?: ReadonlyMap): SourceFile { + let file = sharedCache?.get(sharedFile) as SourceFile | undefined; + let cache: Map; + let needsParent: [SharedNodeBase, Node][]; + if (!file) { + cache = new Map(sharedCache); + needsParent = []; + file = visitSourceFile(sharedFile); + for (const [shared, node] of needsParent) { + const parent = cache.get(shared.parent!) as Node; + Debug.assert(parent); + setParent(node, parent); + } + file.__sharedCache__ = cache; + } + return file; + + function visitToken(shared: SharedToken): Token { + return finishNode(factory.createToken(shared.kind), shared); + } + + function visitEndOfFileToken(shared: SharedEndOfFileToken): EndOfFileToken { + const node = factory.createToken(SyntaxKind.EndOfFileToken) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitModifier(shared: SharedModifier): Modifier { + return visitToken(shared); + } + + function visitModifiers(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitModifierLike) as NodeArray; + } + + function visitModifierLike(shared: SharedModifierLike): ModifierLike { + if (shared.kind === SyntaxKind.Decorator) { + return visitDecorator(shared); + } + return visitModifier(shared); + } + + function visitEntityName(shared: SharedEntityName): EntityName { + switch (shared.kind) { + case SyntaxKind.Identifier: return visitIdentifier(shared); + case SyntaxKind.QualifiedName: return visitQualifiedName(shared); + default: Debug.assertNever(shared); + } + } + + function visitBindingName(shared: SharedBindingName): BindingName { + return shared.kind === SyntaxKind.Identifier ? visitIdentifier(shared) : + visitBindingPattern(shared); + } + + function visitPropertyName(shared: SharedPropertyName): PropertyName { + switch (shared.kind) { + case SyntaxKind.Identifier: return visitIdentifier(shared); + case SyntaxKind.StringLiteral: return visitStringLiteral(shared); + case SyntaxKind.NumericLiteral: return visitNumericLiteral(shared); + case SyntaxKind.ComputedPropertyName: return visitComputedPropertyName(shared); + case SyntaxKind.PrivateIdentifier: return visitPrivateIdentifier(shared); + default: Debug.assertNever(shared); + } + } + + function visitMemberName(shared: SharedMemberName): MemberName { + switch (shared.kind) { + case SyntaxKind.Identifier: return visitIdentifier(shared); + case SyntaxKind.PrivateIdentifier: return visitPrivateIdentifier(shared); + default: Debug.assertNever(shared); + } + } + + function visitIdentifier(shared: SharedIdentifier): Identifier { + const node = factory.createIdentifier("") as Mutable; + node.escapedText = shared.escapedText; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitQualifiedName(shared: SharedQualifiedName): QualifiedName { + const node = factory.createQualifiedName( + shared.left && visit(shared.left, visitEntityName), + shared.right && visit(shared.right, visitIdentifier) + ) as Mutable; + return finishNode(node, shared); + } + + function visitComputedPropertyName(shared: SharedComputedPropertyName): ComputedPropertyName { + const node = factory.createComputedPropertyName( + shared.expression && visit(shared.expression, visitExpression) + ) as Mutable; + return finishNode(node, shared); + } + + function visitPrivateIdentifier(shared: SharedPrivateIdentifier): PrivateIdentifier { + const node = factory.createPrivateIdentifier("#") as Mutable; + node.escapedText = shared.escapedText; + return finishNode(node, shared); + } + + function visitTypeParameters(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitTypeParameterDeclaration); + } + + function visitTypeParameterDeclaration(shared: SharedTypeParameterDeclaration): TypeParameterDeclaration { + const node = factory.createTypeParameterDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitIdentifier), + shared.constraint && visit(shared.constraint, visitTypeNode), + shared.default && visit(shared.default, visitTypeNode), + ) as Mutable; + node.expression = shared.expression && visit(shared.expression, visitExpression); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitParameters(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitParameterDeclaration); + } + + function visitParameterDeclaration(shared: SharedParameterDeclaration): ParameterDeclaration { + const node = factory.createParameterDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.dotDotDotToken && visit(shared.dotDotDotToken, visitToken), + shared.name && visit(shared.name, visitBindingName), + shared.questionToken && visit(shared.questionToken, visitToken), + shared.type && visit(shared.type, visitTypeNode), + shared.initializer && visit(shared.initializer, visitExpression) + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitDecorator(shared: SharedDecorator): Decorator { + const node = factory.createDecorator( + shared.expression && visit(shared.expression, visitExpression) + ) as Mutable; + return finishNode(node, shared); + } + + function visitPropertySignature(shared: SharedPropertySignature): PropertySignature { + const node = factory.createPropertySignature( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitPropertyName), + shared.questionToken && visit(shared.questionToken, visitToken), + shared.type && visit(shared.type, visitTypeNode) + ) as Mutable; + node.initializer = shared.initializer && visit(shared.initializer, visitExpression); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitCallSignatureDeclaration(shared: SharedCallSignatureDeclaration): CallSignatureDeclaration { + const node = factory.createCallSignature( + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode) + ) as Mutable; + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitConstructSignatureDeclaration(shared: SharedConstructSignatureDeclaration): ConstructSignatureDeclaration { + const node = factory.createConstructSignature( + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode) + ) as Mutable; + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitVariableDeclaration(shared: SharedVariableDeclaration): VariableDeclaration { + const node = factory.createVariableDeclaration( + shared.name && visit(shared.name, visitBindingName), + shared.exclamationToken && visit(shared.exclamationToken, visitToken), + shared.type && visit(shared.type, visitTypeNode), + shared.initializer && visit(shared.initializer, visitExpression), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitVariableDeclarations(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitVariableDeclaration); + } + + function visitVariableDeclarationList(shared: SharedVariableDeclarationList): VariableDeclarationList { + const node = factory.createVariableDeclarationList( + shared.declarations && visit(shared.declarations, visitVariableDeclarations), + ) as Mutable; + return finishNode(node, shared); + } + + function visitBindingElement(shared: SharedBindingElement): BindingElement { + const node = factory.createBindingElement( + shared.dotDotDotToken && visit(shared.dotDotDotToken, visitToken), + shared.propertyName && visit(shared.propertyName, visitPropertyName), + shared.name && visit(shared.name, visitBindingName), + shared.initializer && visit(shared.initializer, visitExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitPropertyDeclaration(shared: SharedPropertyDeclaration): PropertyDeclaration { + const node = factory.createPropertyDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitPropertyName), + shared.questionToken && visit(shared.questionToken, visitToken) || + shared.exclamationToken && visit(shared.exclamationToken, visitToken), + shared.type && visit(shared.type, visitTypeNode), + shared.initializer && visit(shared.initializer, visitExpression), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitPropertyAssignment(shared: SharedPropertyAssignment): PropertyAssignment { + const node = factory.createPropertyAssignment( + shared.name && visit(shared.name, visitPropertyName), + shared.initializer && visit(shared.initializer, visitExpression), + ) as Mutable; + node.modifiers = shared.modifiers && visit(shared.modifiers, visitModifiers); + node.questionToken = shared.questionToken && visit(shared.questionToken, visitToken); + node.exclamationToken = shared.exclamationToken && visit(shared.exclamationToken, visitToken); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitShorthandPropertyAssignment(shared: SharedShorthandPropertyAssignment): ShorthandPropertyAssignment { + const node = factory.createShorthandPropertyAssignment( + shared.name && visit(shared.name, visitIdentifier), + shared.objectAssignmentInitializer && visit(shared.objectAssignmentInitializer, visitExpression), + ) as Mutable; + node.equalsToken = shared.equalsToken && visit(shared.equalsToken, visitToken); + node.modifiers = shared.modifiers && visit(shared.modifiers, visitModifiers); + node.questionToken = shared.questionToken && visit(shared.questionToken, visitToken); + node.exclamationToken = shared.exclamationToken && visit(shared.exclamationToken, visitToken); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitSpreadAssignment(shared: SharedSpreadAssignment): SpreadAssignment { + const node = factory.createSpreadAssignment( + shared.expression && visit(shared.expression, visitExpression) + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitBindingPattern(shared: SharedBindingPattern): BindingPattern { + switch (shared.kind) { + case SyntaxKind.ObjectBindingPattern: return visitObjectBindingPattern(shared); + case SyntaxKind.ArrayBindingPattern: return visitArrayBindingPattern(shared); + default: Debug.assertNever(shared); + } + } + + function visitArrayBindingElement(shared: SharedArrayBindingElement): ArrayBindingElement { + switch (shared.kind) { + case SyntaxKind.BindingElement: return visitBindingElement(shared); + case SyntaxKind.OmittedExpression: return visitOmittedExpression(shared); + default: Debug.assertNever(shared); + } + } + + function visitBindingElements(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitBindingElement); + } + + function visitObjectBindingPattern(shared: SharedObjectBindingPattern): ObjectBindingPattern { + const node = factory.createObjectBindingPattern( + shared.elements && visit(shared.elements, visitBindingElements), + ) as Mutable; + return finishNode(node, shared); + } + + function visitArrayBindingElements(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitArrayBindingElement); + } + + function visitArrayBindingPattern(shared: SharedArrayBindingPattern): ArrayBindingPattern { + const node = factory.createArrayBindingPattern( + shared.elements && visit(shared.elements, visitArrayBindingElements), + ) as Mutable; + return finishNode(node, shared); + } + + function visitFunctionDeclaration(shared: SharedFunctionDeclaration): FunctionDeclaration { + const node = factory.createFunctionDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.asteriskToken && visit(shared.asteriskToken, visitToken), + shared.name && visit(shared.name, visitIdentifier), + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode), + shared.body && visit(shared.body, visitBlock), + ) as Mutable; + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitMethodSignature(shared: SharedMethodSignature): MethodSignature { + const node = factory.createMethodSignature( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitPropertyName), + shared.questionToken && visit(shared.questionToken, visitToken), + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitMethodDeclaration(shared: SharedMethodDeclaration): MethodDeclaration { + const node = factory.createMethodDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.asteriskToken && visit(shared.asteriskToken, visitToken), + shared.name && visit(shared.name, visitPropertyName), + shared.questionToken && visit(shared.questionToken, visitToken), + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode), + shared.body && visit(shared.body, visitBlock), + ) as Mutable; + node.exclamationToken = shared.exclamationToken && visit(shared.exclamationToken, visitToken); + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitConstructorDeclaration(shared: SharedConstructorDeclaration): ConstructorDeclaration { + const node = factory.createConstructorDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.parameters && visit(shared.parameters, visitParameters), + shared.body && visit(shared.body, visitBlock), + ) as Mutable; + node.typeParameters = shared.typeParameters && visit(shared.typeParameters, visitTypeParameters); + node.type = shared.type && visit(shared.type, visitTypeNode); + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitSemicolonClassElement(shared: SharedSemicolonClassElement): SemicolonClassElement { + const node = factory.createSemicolonClassElement() as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitGetAccessorDeclaration(shared: SharedGetAccessorDeclaration): GetAccessorDeclaration { + const node = factory.createGetAccessorDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitPropertyName), + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode), + shared.body && visit(shared.body, visitBlock), + ) as Mutable; + node.typeParameters = shared.typeParameters && visit(shared.typeParameters, visitTypeParameters); + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitSetAccessorDeclaration(shared: SharedSetAccessorDeclaration): SetAccessorDeclaration { + const node = factory.createSetAccessorDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitPropertyName), + shared.parameters && visit(shared.parameters, visitParameters), + shared.body && visit(shared.body, visitBlock), + ) as Mutable; + node.typeParameters = shared.typeParameters && visit(shared.typeParameters, visitTypeParameters); + node.type = shared.type && visit(shared.type, visitTypeNode); + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitIndexSignatureDeclaration(shared: SharedIndexSignatureDeclaration): IndexSignatureDeclaration { + const node = factory.createIndexSignature( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + node.typeParameters = shared.typeParameters && visit(shared.typeParameters, visitTypeParameters); + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitClassStaticBlockDeclaration(shared: SharedClassStaticBlockDeclaration): ClassStaticBlockDeclaration { + const node = factory.createClassStaticBlockDeclaration( + shared.body && visit(shared.body, visitBlock), + ) as Mutable; + node.modifiers = shared.modifiers && visit(shared.modifiers, visitModifiers); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitTypeArguments(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitTypeNode); + } + + function visitTypeNode(shared: SharedTypeNode): TypeNode { + switch (shared.kind) { + case SyntaxKind.ImportType: return visitImportTypeNode(shared); + case SyntaxKind.ThisType: return visitThisTypeNode(shared); + case SyntaxKind.FunctionType: return visitFunctionTypeNode(shared); + case SyntaxKind.ConstructorType: return visitConstructorTypeNode(shared); + case SyntaxKind.TypeReference: return visitTypeReferenceNode(shared); + case SyntaxKind.TypePredicate: return visitTypePredicateNode(shared); + case SyntaxKind.TypeQuery: return visitTypeQueryNode(shared); + case SyntaxKind.TypeLiteral: return visitTypeLiteralNode(shared); + case SyntaxKind.ArrayType: return visitArrayTypeNode(shared); + case SyntaxKind.TupleType: return visitTupleTypeNode(shared); + case SyntaxKind.NamedTupleMember: return visitNamedTupleMember(shared); + case SyntaxKind.OptionalType: return visitOptionalTypeNode(shared); + case SyntaxKind.RestType: return visitRestTypeNode(shared); + case SyntaxKind.UnionType: return visitUnionTypeNode(shared); + case SyntaxKind.IntersectionType: return visitIntersectionTypeNode(shared); + case SyntaxKind.ConditionalType: return visitConditionalTypeNode(shared); + case SyntaxKind.InferType: return visitInferTypeNode(shared); + case SyntaxKind.ParenthesizedType: return visitParenthesizedTypeNode(shared); + case SyntaxKind.TypeOperator: return visitTypeOperatorNode(shared); + case SyntaxKind.IndexedAccessType: return visitIndexedAccessTypeNode(shared); + case SyntaxKind.MappedType: return visitMappedTypeNode(shared); + case SyntaxKind.LiteralType: return visitLiteralTypeNode(shared); + case SyntaxKind.TemplateLiteralType: return visitTemplateLiteralTypeNode(shared); + case SyntaxKind.TemplateLiteralTypeSpan: return visitTemplateLiteralTypeSpan(shared); + case SyntaxKind.JSDocTypeExpression: return visitJSDocTypeExpression(shared); + case SyntaxKind.JSDocAllType: return visitJSDocAllType(shared); + case SyntaxKind.JSDocUnknownType: return visitJSDocUnknownType(shared); + case SyntaxKind.JSDocNonNullableType: return visitJSDocNonNullableType(shared); + case SyntaxKind.JSDocNullableType: return visitJSDocNullableType(shared); + case SyntaxKind.JSDocOptionalType: return visitJSDocOptionalType(shared); + case SyntaxKind.JSDocFunctionType: return visitJSDocFunctionType(shared); + case SyntaxKind.JSDocVariadicType: return visitJSDocVariadicType(shared); + case SyntaxKind.JSDocNamepathType: return visitJSDocNamepathType(shared); + default: return visitToken(shared) as KeywordTypeNode; + } + } + + function visitImportTypeAssertionContainer(shared: SharedImportTypeAssertionContainer): ImportTypeAssertionContainer { + const node = factory.createImportTypeAssertionContainer( + shared.assertClause && visit(shared.assertClause, visitAssertClause), + shared.multiLine, + ) as Mutable; + return finishNode(node, shared); + } + + function visitImportTypeNode(shared: SharedImportTypeNode): ImportTypeNode { + const node = factory.createImportTypeNode( + shared.argument && visit(shared.argument, visitTypeNode), + shared.assertions && visit(shared.assertions, visitImportTypeAssertionContainer), + shared.qualifier && visit(shared.qualifier, visitEntityName), + shared.typeArguments && visit(shared.typeArguments, visitTypeArguments), + shared.isTypeOf, + ) as Mutable; + return finishNode(node, shared); + } + + function visitThisTypeNode(shared: SharedThisTypeNode): ThisTypeNode { + const node = factory.createThisTypeNode() as Mutable; + return finishNode(node, shared); + } + + function visitFunctionTypeNode(shared: SharedFunctionTypeNode): FunctionTypeNode { + const node = factory.createFunctionTypeNode( + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + node.modifiers = shared.modifiers && visit(shared.modifiers, visitModifiers); + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitConstructorTypeNode(shared: SharedConstructorTypeNode): ConstructorTypeNode { + const node = factory.createConstructorTypeNode( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitTypeReferenceNode(shared: SharedTypeReferenceNode): TypeReferenceNode { + const node = factory.createTypeReferenceNode( + shared.typeName && visit(shared.typeName, visitEntityName), + shared.typeArguments && visit(shared.typeArguments, visitTypeArguments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitIdentifierOrThisTypeNode(shared: SharedIdentifier | SharedThisTypeNode): Identifier | ThisTypeNode { + return shared.kind === SyntaxKind.Identifier ? + visitIdentifier(shared) : + visitThisTypeNode(shared); + } + + function visitTypePredicateNode(shared: SharedTypePredicateNode): TypePredicateNode { + const node = factory.createTypePredicateNode( + shared.assertsModifier && visit(shared.assertsModifier, visitToken), + shared.parameterName && visit(shared.parameterName, visitIdentifierOrThisTypeNode), + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitTypeQueryNode(shared: SharedTypeQueryNode): TypeQueryNode { + const node = factory.createTypeQueryNode( + shared.exprName && visit(shared.exprName, visitEntityName), + shared.typeArguments && visit(shared.typeArguments, visitTypeArguments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitTypeLiteralNode(shared: SharedTypeLiteralNode): TypeLiteralNode { + const node = factory.createTypeLiteralNode( + shared.members && visit(shared.members, visitTypeElements), + ) as Mutable; + return finishNode(node, shared); + } + + function visitArrayTypeNode(shared: SharedArrayTypeNode): ArrayTypeNode { + const node = factory.createArrayTypeNode( + shared.elementType && visit(shared.elementType, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitTupleTypeElement(shared: SharedTypeNode | SharedNamedTupleMember): TypeNode | NamedTupleMember { + return shared.kind === SyntaxKind.NamedTupleMember ? visitNamedTupleMember(shared) : + visitTypeNode(shared); + } + + function visitTupleTypeElements(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitTupleTypeElement); + } + + function visitTupleTypeNode(shared: SharedTupleTypeNode): TupleTypeNode { + const node = factory.createTupleTypeNode( + shared.elements && visit(shared.elements, visitTupleTypeElements), + ) as Mutable; + return finishNode(node, shared); + } + + function visitNamedTupleMember(shared: SharedNamedTupleMember): NamedTupleMember { + const node = factory.createNamedTupleMember( + shared.dotDotDotToken && visit(shared.dotDotDotToken, visitToken), + shared.name && visit(shared.name, visitIdentifier), + shared.questionToken && visit(shared.questionToken, visitToken), + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitOptionalTypeNode(shared: SharedOptionalTypeNode): OptionalTypeNode { + const node = factory.createOptionalTypeNode( + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitRestTypeNode(shared: SharedRestTypeNode): RestTypeNode { + const node = factory.createRestTypeNode( + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitUnionTypeNode(shared: SharedUnionTypeNode): UnionTypeNode { + const node = factory.createUnionTypeNode( + shared.types && visit(shared.types, visitTypeArguments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitIntersectionTypeNode(shared: SharedIntersectionTypeNode): IntersectionTypeNode { + const node = factory.createIntersectionTypeNode( + shared.types && visit(shared.types, visitTypeArguments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitConditionalTypeNode(shared: SharedConditionalTypeNode): ConditionalTypeNode { + const node = factory.createConditionalTypeNode( + shared.checkType && visit(shared.checkType, visitTypeNode), + shared.extendsType && visit(shared.extendsType, visitTypeNode), + shared.trueType && visit(shared.trueType, visitTypeNode), + shared.falseType && visit(shared.falseType, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitInferTypeNode(shared: SharedInferTypeNode): InferTypeNode { + const node = factory.createInferTypeNode( + shared.typeParameter && visit(shared.typeParameter, visitTypeParameterDeclaration), + ) as Mutable; + return finishNode(node, shared); + } + + function visitParenthesizedTypeNode(shared: SharedParenthesizedTypeNode): ParenthesizedTypeNode { + const node = factory.createParenthesizedType( + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitTypeOperatorNode(shared: SharedTypeOperatorNode): TypeOperatorNode { + const node = factory.createTypeOperatorNode( + shared.operator, + shared.type && visit(shared.type, visitTypeNode) + ) as Mutable; + return finishNode(node, shared); + } + + function visitIndexedAccessTypeNode(shared: SharedIndexedAccessTypeNode): IndexedAccessTypeNode { + const node = factory.createIndexedAccessTypeNode( + shared.objectType && visit(shared.objectType, visitTypeNode), + shared.indexType && visit(shared.indexType, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitMappedTypeNode(shared: SharedMappedTypeNode): MappedTypeNode { + const node = factory.createMappedTypeNode( + shared.readonlyToken && visit(shared.readonlyToken, visitToken), + shared.typeParameter && visit(shared.typeParameter, visitTypeParameterDeclaration), + shared.nameType && visit(shared.nameType, visitTypeNode), + shared.questionToken && visit(shared.questionToken, visitToken), + shared.type && visit(shared.type, visitTypeNode), + shared.members && visit(shared.members, visitTypeElements), + ) as Mutable; + return finishNode(node, shared); + } + + function visitLiteralOfLiteralTypeNode(shared: SharedToken | SharedToken | SharedToken | SharedStringLiteral | SharedNumericLiteral | SharedBigIntLiteral | SharedPrefixUnaryExpression) { + switch (shared.kind) { + case SyntaxKind.NullKeyword: return visitToken(shared) as NullLiteral; + case SyntaxKind.TrueKeyword: return visitToken(shared) as TrueLiteral; + case SyntaxKind.FalseKeyword: return visitToken(shared) as FalseLiteral; + case SyntaxKind.StringLiteral: return visitStringLiteral(shared); + case SyntaxKind.NumericLiteral: return visitNumericLiteral(shared); + case SyntaxKind.BigIntLiteral: return visitBigIntLiteral(shared); + default: return visitPrefixUnaryExpression(shared); + } + } + + function visitLiteralTypeNode(shared: SharedLiteralTypeNode): LiteralTypeNode { + const node = factory.createLiteralTypeNode( + shared.literal && visit(shared.literal, visitLiteralOfLiteralTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitStringLiteral(shared: SharedStringLiteral): StringLiteral { + const node = factory.createStringLiteral(shared.text, shared.singleQuote) as Mutable; + node.isUnterminated = shared.isUnterminated; + node.hasExtendedUnicodeEscape = shared.hasExtendedUnicodeEscape; + return finishNode(node, shared); + } + + function visitTemplateLiteralTypeSpans(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitTemplateLiteralTypeSpan); + } + + function visitTemplateLiteralTypeNode(shared: SharedTemplateLiteralTypeNode): TemplateLiteralTypeNode { + const node = factory.createTemplateLiteralType( + shared.head && visit(shared.head, visitTemplateHead), + shared.templateSpans && visit(shared.templateSpans, visitTemplateLiteralTypeSpans), + ) as Mutable; + return finishNode(node, shared); + } + + function visitTemplateMiddleOrTail(shared: SharedTemplateMiddle | SharedTemplateTail): TemplateMiddle | TemplateTail { + return shared.kind === SyntaxKind.TemplateMiddle ? visitTemplateMiddle(shared) : visitTemplateTail(shared); + } + + function visitTemplateLiteralTypeSpan(shared: SharedTemplateLiteralTypeSpan): TemplateLiteralTypeSpan { + const node = factory.createTemplateLiteralTypeSpan( + shared.type && visit(shared.type, visitTypeNode), + shared.literal && visit(shared.literal, visitTemplateMiddleOrTail), + ) as Mutable; + return finishNode(node, shared); + } + + function visitExpression(shared: SharedExpression): Expression { + switch (shared.kind) { + case SyntaxKind.OmittedExpression: return visitOmittedExpression(shared); + case SyntaxKind.YieldExpression: return visitYieldExpression(shared); + case SyntaxKind.BinaryExpression: return visitBinaryExpression(shared); + case SyntaxKind.ConditionalExpression: return visitConditionalExpression(shared); + case SyntaxKind.ArrowFunction: return visitArrowFunction(shared); + case SyntaxKind.SpreadElement: return visitSpreadElement(shared); + case SyntaxKind.AsExpression: return visitAsExpression(shared); + case SyntaxKind.SatisfiesExpression: return visitSatisfiesExpression(shared); + case SyntaxKind.JsxOpeningElement: return visitJsxOpeningElement(shared); + case SyntaxKind.JsxOpeningFragment: return visitJsxOpeningFragment(shared); + case SyntaxKind.JsxClosingFragment: return visitJsxClosingFragment(shared); + case SyntaxKind.JsxExpression: return visitJsxExpression(shared); + default: return visitUnaryExpression(shared); + } + } + + function visitUnaryExpression(shared: SharedUnaryExpression | SharedUpdateExpression): UnaryExpression { + switch (shared.kind) { + case SyntaxKind.PrefixUnaryExpression: return visitPrefixUnaryExpression(shared); + case SyntaxKind.PostfixUnaryExpression: return visitPostfixUnaryExpression(shared); + case SyntaxKind.DeleteExpression: return visitDeleteExpression(shared); + case SyntaxKind.TypeOfExpression: return visitTypeOfExpression(shared); + case SyntaxKind.VoidExpression: return visitVoidExpression(shared); + case SyntaxKind.AwaitExpression: return visitAwaitExpression(shared); + case SyntaxKind.TypeAssertionExpression: return visitTypeAssertion(shared); + default: return visitLeftHandSideExpression(shared); + } + } + + function visitLeftHandSideExpression(shared: SharedLeftHandSideExpression): LeftHandSideExpression { + switch (shared.kind) { + case SyntaxKind.CallExpression: return visitCallExpression(shared); + case SyntaxKind.NewExpression: return visitNewExpression(shared); + case SyntaxKind.NonNullExpression: return visitNonNullExpression(shared); + default: return visitMemberExpression(shared); + } + } + + function visitMemberExpression(shared: SharedMemberExpression): MemberExpression { + switch (shared.kind) { + case SyntaxKind.PropertyAccessExpression: return visitPropertyAccessExpression(shared); + case SyntaxKind.ElementAccessExpression: return visitElementAccessExpression(shared); + case SyntaxKind.ExpressionWithTypeArguments: return visitExpressionWithTypeArguments(shared); + case SyntaxKind.TaggedTemplateExpression: return visitTaggedTemplateExpression(shared); + default: return visitPrimaryExpression(shared); + } + } + + function visitPrimaryExpression(shared: SharedPrimaryExpression): PrimaryExpression { + switch (shared.kind) { + case SyntaxKind.NullKeyword: return visitToken(shared) as NullLiteral; + case SyntaxKind.TrueKeyword: return visitToken(shared) as TrueLiteral; + case SyntaxKind.FalseKeyword: return visitToken(shared) as FalseLiteral; + case SyntaxKind.ThisKeyword: return visitToken(shared) as ThisExpression; + case SyntaxKind.SuperKeyword: return visitToken(shared) as SuperExpression; + case SyntaxKind.ImportKeyword: return visitToken(shared) as ImportExpression; + case SyntaxKind.Identifier: return visitIdentifier(shared); + case SyntaxKind.PrivateIdentifier: return visitPrivateIdentifier(shared); + case SyntaxKind.FunctionExpression: return visitFunctionExpression(shared); + case SyntaxKind.ClassExpression: return visitClassExpression(shared); + case SyntaxKind.RegularExpressionLiteral: return visitRegularExpressionLiteral(shared); + case SyntaxKind.NoSubstitutionTemplateLiteral: return visitNoSubstitutionTemplateLiteral(shared); + case SyntaxKind.StringLiteral: return visitStringLiteral(shared); + case SyntaxKind.NumericLiteral: return visitNumericLiteral(shared); + case SyntaxKind.BigIntLiteral: return visitBigIntLiteral(shared); + case SyntaxKind.TemplateExpression: return visitTemplateExpression(shared); + case SyntaxKind.ParenthesizedExpression: return visitParenthesizedExpression(shared); + case SyntaxKind.ArrayLiteralExpression: return visitArrayLiteralExpression(shared); + case SyntaxKind.ObjectLiteralExpression: return visitObjectLiteralExpression(shared); + case SyntaxKind.NewExpression: return visitNewExpression(shared); + case SyntaxKind.MetaProperty: return visitMetaProperty(shared); + case SyntaxKind.JsxElement: return visitJsxElement(shared); + case SyntaxKind.JsxAttributes: return visitJsxAttributes(shared); + case SyntaxKind.JsxSelfClosingElement: return visitJsxSelfClosingElement(shared); + case SyntaxKind.JsxFragment: return visitJsxFragment(shared); + case SyntaxKind.MissingDeclaration: return visitMissingDeclaration(shared); + default: Debug.assertNever(shared); + } + } + + function visitOmittedExpression(shared: SharedOmittedExpression): OmittedExpression { + const node = factory.createOmittedExpression() as Mutable; + return finishNode(node, shared); + } + + function visitPrefixUnaryExpression(shared: SharedPrefixUnaryExpression): PrefixUnaryExpression { + const node = factory.createPrefixUnaryExpression( + shared.operator, + shared.operand && visit(shared.operand, visitUnaryExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitPostfixUnaryExpression(shared: SharedPostfixUnaryExpression): PostfixUnaryExpression { + const node = factory.createPostfixUnaryExpression( + shared.operand && visit(shared.operand, visitUnaryExpression), + shared.operator, + ) as Mutable; + return finishNode(node, shared); + } + + function visitDeleteExpression(shared: SharedDeleteExpression): DeleteExpression { + const node = factory.createDeleteExpression( + shared.expression && visit(shared.expression, visitUnaryExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitTypeOfExpression(shared: SharedTypeOfExpression): TypeOfExpression { + const node = factory.createTypeOfExpression( + shared.expression && visit(shared.expression, visitUnaryExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitVoidExpression(shared: SharedVoidExpression): VoidExpression { + const node = factory.createVoidExpression( + shared.expression && visit(shared.expression, visitUnaryExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitAwaitExpression(shared: SharedAwaitExpression): AwaitExpression { + const node = factory.createAwaitExpression( + shared.expression && visit(shared.expression, visitUnaryExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitYieldExpression(shared: SharedYieldExpression): YieldExpression { + const node = factory.createYieldExpression( + shared.asteriskToken && visit(shared.asteriskToken, visitToken), + shared.expression && visit(shared.expression, visitExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitBinaryExpression(shared: SharedBinaryExpression): BinaryExpression { + const node = factory.createBinaryExpression( + shared.left && visit(shared.left, visitExpression), + shared.operatorToken && visit(shared.operatorToken, visitToken), + shared.right && visit(shared.right, visitExpression) + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitConditionalExpression(shared: SharedConditionalExpression): ConditionalExpression { + const node = factory.createConditionalExpression( + shared.condition && visit(shared.condition, visitExpression), + shared.questionToken && visit(shared.questionToken, visitToken), + shared.whenTrue && visit(shared.whenTrue, visitExpression), + shared.colonToken && visit(shared.colonToken, visitToken), + shared.whenFalse && visit(shared.whenFalse, visitExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitFunctionExpression(shared: SharedFunctionExpression): FunctionExpression { + const node = factory.createFunctionExpression( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.asteriskToken && visit(shared.asteriskToken, visitToken), + shared.name && visit(shared.name, visitIdentifier), + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode), + shared.body && visit(shared.body, visitBlock), + ) as Mutable; + node.questionToken = shared.questionToken && visit(shared.questionToken, visitToken); + node.exclamationToken = shared.exclamationToken && visit(shared.exclamationToken, visitToken); + node.typeArguments = shared.typeArguments && visit(shared.typeArguments, visitTypeArguments); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitConciseBody(shared: SharedConciseBody): ConciseBody { + return shared.kind === SyntaxKind.Block ? visitBlock(shared) : + visitExpression(shared); + } + + function visitArrowFunction(shared: SharedArrowFunction): ArrowFunction { + const node = factory.createArrowFunction( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode), + shared.equalsGreaterThanToken && visit(shared.equalsGreaterThanToken, visitToken), + shared.body && visit(shared.body, visitConciseBody), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitRegularExpressionLiteral(shared: SharedRegularExpressionLiteral): RegularExpressionLiteral { + const node = factory.createRegularExpressionLiteral( + shared.text, + ) as Mutable; + node.isUnterminated = shared.isUnterminated; + node.hasExtendedUnicodeEscape = shared.hasExtendedUnicodeEscape; + return finishNode(node, shared); + } + + function visitNoSubstitutionTemplateLiteral(shared: SharedNoSubstitutionTemplateLiteral): NoSubstitutionTemplateLiteral { + const node = factory.createNoSubstitutionTemplateLiteral( + shared.text, + shared.rawText, + ) as Mutable; + node.isUnterminated = shared.isUnterminated; + node.hasExtendedUnicodeEscape = shared.hasExtendedUnicodeEscape; + node.templateFlags = shared.templateFlags; + return finishNode(node, shared); + } + + function visitNumericLiteral(shared: SharedNumericLiteral): NumericLiteral { + const node = factory.createNumericLiteral( + shared.text, + shared.numericLiteralFlags, + ) as Mutable; + node.isUnterminated = shared.isUnterminated; + node.hasExtendedUnicodeEscape = shared.hasExtendedUnicodeEscape; + return finishNode(node, shared); + } + + function visitBigIntLiteral(shared: SharedBigIntLiteral): BigIntLiteral { + const node = factory.createBigIntLiteral( + shared.text, + ) as Mutable; + node.isUnterminated = shared.isUnterminated; + node.hasExtendedUnicodeEscape = shared.hasExtendedUnicodeEscape; + return finishNode(node, shared); + } + + function visitTemplateHead(shared: SharedTemplateHead): TemplateHead { + const node = factory.createTemplateHead( + shared.text, + shared.rawText, + shared.templateFlags, + ) as Mutable; + node.isUnterminated = shared.isUnterminated; + node.hasExtendedUnicodeEscape = shared.hasExtendedUnicodeEscape; + return finishNode(node, shared); + } + + function visitTemplateMiddle(shared: SharedTemplateMiddle): TemplateMiddle { + const node = factory.createTemplateMiddle( + shared.text, + shared.rawText, + shared.templateFlags, + ) as Mutable; + node.isUnterminated = shared.isUnterminated; + node.hasExtendedUnicodeEscape = shared.hasExtendedUnicodeEscape; + return finishNode(node, shared); + } + + function visitTemplateTail(shared: SharedTemplateTail): TemplateTail { + const node = factory.createTemplateTail( + shared.text, + shared.rawText, + shared.templateFlags, + ) as Mutable; + node.isUnterminated = shared.isUnterminated; + node.hasExtendedUnicodeEscape = shared.hasExtendedUnicodeEscape; + return finishNode(node, shared); + } + + function visitTemplateExpression(shared: SharedTemplateExpression): TemplateExpression { + const node = factory.createTemplateExpression( + shared.head && visit(shared.head, visitTemplateHead), + shared.templateSpans && visit(shared.templateSpans, visitTemplateSpans), + ) as Mutable; + return finishNode(node, shared); + } + + function visitTemplateSpans(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitTemplateSpan); + } + + function visitTemplateSpan(shared: SharedTemplateSpan): TemplateSpan { + const node = factory.createTemplateSpan( + shared.expression && visit(shared.expression, visitExpression), + shared.literal && visit(shared.literal, visitTemplateMiddleOrTail), + ) as Mutable; + return finishNode(node, shared); + } + + function visitParenthesizedExpression(shared: SharedParenthesizedExpression): ParenthesizedExpression { + const node = factory.createParenthesizedExpression( + shared.expression && visit(shared.expression, visitExpression), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitExpressions(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitExpression); + } + + function visitArrayLiteralExpression(shared: SharedArrayLiteralExpression): ArrayLiteralExpression { + const node = factory.createArrayLiteralExpression( + shared.elements && visit(shared.elements, visitExpressions), + shared.multiLine, + ) as Mutable; + return finishNode(node, shared); + } + + function visitSpreadElement(shared: SharedSpreadElement): SpreadElement { + const node = factory.createSpreadElement( + shared.expression && visit(shared.expression, visitExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitObjectLiteralElements(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitObjectLiteralElement); + } + + function visitObjectLiteralElement(shared: SharedObjectLiteralElement): ObjectLiteralElementLike { + switch (shared.kind) { + case SyntaxKind.PropertyAssignment: return visitPropertyAssignment(shared); + case SyntaxKind.ShorthandPropertyAssignment: return visitShorthandPropertyAssignment(shared); + case SyntaxKind.SpreadAssignment: return visitSpreadAssignment(shared); + case SyntaxKind.MethodDeclaration: return visitMethodDeclaration(shared); + case SyntaxKind.GetAccessor: return visitGetAccessorDeclaration(shared); + case SyntaxKind.SetAccessor: return visitSetAccessorDeclaration(shared); + default: Debug.assertNever(shared); + } + } + + function visitObjectLiteralExpression(shared: SharedObjectLiteralExpression): ObjectLiteralExpression { + const node = factory.createObjectLiteralExpression( + shared.properties && visit(shared.properties, visitObjectLiteralElements), + shared.multiLine, + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitPropertyAccessExpression(shared: SharedPropertyAccessExpression): PropertyAccessExpression { + const node = factory.createPropertyAccessExpression( + shared.expression && visit(shared.expression, visitLeftHandSideExpression), + shared.name && visit(shared.name, visitMemberName), + ) as Mutable; + node.questionDotToken = shared.questionDotToken && visit(shared.questionDotToken, visitToken); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitElementAccessExpression(shared: SharedElementAccessExpression): ElementAccessExpression { + const node = factory.createElementAccessExpression( + shared.expression && visit(shared.expression, visitLeftHandSideExpression), + shared.argumentExpression && visit(shared.argumentExpression, visitExpression) + ) as Mutable; + node.questionDotToken = shared.questionDotToken && visit(shared.questionDotToken, visitToken); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitCallExpression(shared: SharedCallExpression): CallExpression { + const node = factory.createCallExpression( + shared.expression && visit(shared.expression, visitExpression), + shared.typeArguments && visit(shared.typeArguments, visitTypeArguments), + shared.arguments && visit(shared.arguments, visitExpressions), + ) as Mutable; + node.questionDotToken = shared.questionDotToken && visit(shared.questionDotToken, visitToken); + return finishNode(node, shared); + } + + function visitExpressionWithTypeArguments(shared: SharedExpressionWithTypeArguments): ExpressionWithTypeArguments { + const node = factory.createExpressionWithTypeArguments( + shared.expression && visit(shared.expression, visitLeftHandSideExpression), + shared.typeArguments && visit(shared.typeArguments, visitTypeArguments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitNewExpression(shared: SharedNewExpression): NewExpression { + const node = factory.createNewExpression( + shared.expression && visit(shared.expression, visitLeftHandSideExpression), + shared.typeArguments && visit(shared.typeArguments, visitTypeArguments), + shared.arguments && visit(shared.arguments, visitExpressions), + ) as Mutable; + return finishNode(node, shared); + } + + function visitTemplateLiteral(shared: SharedTemplateLiteral): TemplateLiteral { + switch (shared.kind) { + case SyntaxKind.TemplateExpression: return visitTemplateExpression(shared); + case SyntaxKind.NoSubstitutionTemplateLiteral: return visitNoSubstitutionTemplateLiteral(shared); + default: Debug.assertNever(shared); + } + } + + function visitTaggedTemplateExpression(shared: SharedTaggedTemplateExpression): TaggedTemplateExpression { + const node = factory.createTaggedTemplateExpression( + shared.tag && visit(shared.tag, visitLeftHandSideExpression), + shared.typeArguments && visit(shared.typeArguments, visitTypeArguments), + shared.template && visit(shared.template, visitTemplateLiteral), + ) as Mutable; + node.questionDotToken = shared.questionDotToken && visit(shared.questionDotToken, visitToken); + return finishNode(node, shared); + } + + function visitAsExpression(shared: SharedAsExpression): AsExpression { + const node = factory.createAsExpression( + shared.expression && visit(shared.expression, visitExpression), + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitTypeAssertion(shared: SharedTypeAssertion): TypeAssertion { + const node = factory.createTypeAssertion( + shared.type && visit(shared.type, visitTypeNode), + shared.expression && visit(shared.expression, visitUnaryExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitSatisfiesExpression(shared: SharedSatisfiesExpression): SatisfiesExpression { + const node = factory.createSatisfiesExpression( + shared.expression && visit(shared.expression, visitExpression), + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitNonNullExpression(shared: SharedNonNullExpression): NonNullExpression { + const node = factory.createNonNullExpression( + shared.expression && visit(shared.expression, visitExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitMetaProperty(shared: SharedMetaProperty): MetaProperty { + const node = factory.createMetaProperty( + shared.keywordToken, + shared.name && visit(shared.name, visitIdentifier) + ) as Mutable; + return finishNode(node, shared); + } + + function visitJsxChildren(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitJsxChild); + } + + function visitJsxElement(shared: SharedJsxElement): JsxElement { + const node = factory.createJsxElement( + shared.openingElement && visit(shared.openingElement, visitJsxOpeningElement), + shared.children && visit(shared.children, visitJsxChildren), + shared.closingElement && visit(shared.closingElement, visitJsxClosingElement), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJsxAttributeLike(shared: SharedJsxAttributeLike): JsxAttributeLike { + switch (shared.kind) { + case SyntaxKind.JsxAttribute: return visitJsxAttribute(shared); + case SyntaxKind.JsxSpreadAttribute: return visitJsxSpreadAttribute(shared); + default: Debug.assertNever(shared); + } + } + + function visitJsxAttributeName(shared: SharedJsxAttributeName): JsxAttributeName { + switch (shared.kind) { + case SyntaxKind.Identifier: return visitIdentifier(shared); + case SyntaxKind.JsxNamespacedName: return visitJsxNamespacedName(shared); + default: Debug.assertNever(shared); + } + } + + function visitJsxTagNameExpression(shared: SharedJsxTagNameExpression): JsxTagNameExpression { + switch (shared.kind) { + case SyntaxKind.Identifier: return visitIdentifier(shared); + case SyntaxKind.ThisKeyword: return visitToken(shared) as ThisExpression; + case SyntaxKind.PropertyAccessExpression: return visitPropertyAccessExpression(shared) as JsxTagNamePropertyAccess; + case SyntaxKind.JsxNamespacedName: return visitJsxNamespacedName(shared); + default: Debug.assertNever(shared); + } + } + + function visitJsxChild(shared: SharedJsxChild): JsxChild { + switch (shared.kind) { + case SyntaxKind.JsxText: return visitJsxText(shared); + case SyntaxKind.JsxExpression: return visitJsxExpression(shared); + case SyntaxKind.JsxElement: return visitJsxElement(shared); + case SyntaxKind.JsxSelfClosingElement: return visitJsxSelfClosingElement(shared); + case SyntaxKind.JsxFragment: return visitJsxFragment(shared); + default: Debug.assertNever(shared); + } + } + + function visitJsxAttributeLikes(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitJsxAttributeLike); + } + + function visitJsxAttributes(shared: SharedJsxAttributes): JsxAttributes { + const node = factory.createJsxAttributes( + shared.properties && visit(shared.properties, visitJsxAttributeLikes), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJsxNamespacedName(shared: SharedJsxNamespacedName): JsxNamespacedName { + const node = factory.createJsxNamespacedName( + shared.name && visit(shared.name, visitIdentifier), + shared.namespace && visit(shared.namespace, visitIdentifier), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJsxOpeningElement(shared: SharedJsxOpeningElement): JsxOpeningElement { + const node = factory.createJsxOpeningElement( + shared.tagName && visit(shared.tagName, visitJsxTagNameExpression), + shared.typeArguments && visit(shared.typeArguments, visitTypeArguments), + shared.attributes && visit(shared.attributes, visitJsxAttributes), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJsxSelfClosingElement(shared: SharedJsxSelfClosingElement): JsxSelfClosingElement { + const node = factory.createJsxSelfClosingElement( + shared.tagName && visit(shared.tagName, visitJsxTagNameExpression), + shared.typeArguments && visit(shared.typeArguments, visitTypeArguments), + shared.attributes && visit(shared.attributes, visitJsxAttributes), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJsxFragment(shared: SharedJsxFragment): JsxFragment { + const node = factory.createJsxFragment( + shared.openingFragment && visit(shared.openingFragment, visitJsxOpeningFragment), + shared.children && visit(shared.children, visitJsxChildren), + shared.closingFragment && visit(shared.closingFragment, visitJsxClosingFragment), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJsxOpeningFragment(shared: SharedJsxOpeningFragment): JsxOpeningFragment { + const node = factory.createJsxOpeningFragment() as Mutable; + return finishNode(node, shared); + } + + function visitJsxClosingFragment(shared: SharedJsxClosingFragment): JsxClosingFragment { + const node = factory.createJsxClosingFragment() as Mutable; + return finishNode(node, shared); + } + + function visitJsxAttribute(shared: SharedJsxAttribute): JsxAttribute { + const node = factory.createJsxAttribute( + shared.name && visit(shared.name, visitJsxAttributeName), + shared.initializer && visit(shared.initializer, visitJsxAttributeValue), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJsxAttributeValue(shared: SharedJsxAttributeValue): JsxAttributeValue { + switch (shared.kind) { + case SyntaxKind.StringLiteral: return visitStringLiteral(shared); + case SyntaxKind.JsxExpression: return visitJsxExpression(shared); + case SyntaxKind.JsxElement: return visitJsxElement(shared); + case SyntaxKind.JsxSelfClosingElement: return visitJsxSelfClosingElement(shared); + case SyntaxKind.JsxFragment: return visitJsxFragment(shared); + default: Debug.assertNever(shared); + } + } + + function visitJsxSpreadAttribute(shared: SharedJsxSpreadAttribute): JsxSpreadAttribute { + const node = factory.createJsxSpreadAttribute( + shared.expression && visit(shared.expression, visitExpression) + ) as Mutable; + return finishNode(node, shared); + } + + function visitJsxClosingElement(shared: SharedJsxClosingElement): JsxClosingElement { + const node = factory.createJsxClosingElement( + shared.tagName && visit(shared.tagName, visitJsxTagNameExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJsxExpression(shared: SharedJsxExpression): JsxExpression { + const node = factory.createJsxExpression( + shared.dotDotDotToken && visit(shared.dotDotDotToken, visitToken), + shared.expression && visit(shared.expression, visitExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJsxText(shared: SharedJsxText): JsxText { + const node = factory.createJsxText( + shared.text, + shared.containsOnlyTriviaWhiteSpaces, + ) as Mutable; + node.isUnterminated = shared.isUnterminated; + node.hasExtendedUnicodeEscape = shared.hasExtendedUnicodeEscape; + return finishNode(node, shared); + } + + function visitStatements(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitStatement); + } + + function visitStatement(shared: SharedStatement): Statement { + switch (shared.kind) { + case SyntaxKind.EmptyStatement: return visitEmptyStatement(shared); + case SyntaxKind.DebuggerStatement: return visitDebuggerStatement(shared); + case SyntaxKind.MissingDeclaration: return visitMissingDeclaration(shared); + case SyntaxKind.Block: return visitBlock(shared); + case SyntaxKind.VariableStatement: return visitVariableStatement(shared); + case SyntaxKind.ExpressionStatement: return visitExpressionStatement(shared); + case SyntaxKind.IfStatement: return visitIfStatement(shared); + case SyntaxKind.DoStatement: return visitDoStatement(shared); + case SyntaxKind.WhileStatement: return visitWhileStatement(shared); + case SyntaxKind.ForStatement: return visitForStatement(shared); + case SyntaxKind.ForInStatement: return visitForInStatement(shared); + case SyntaxKind.ForOfStatement: return visitForOfStatement(shared); + case SyntaxKind.BreakStatement: return visitBreakStatement(shared); + case SyntaxKind.ContinueStatement: return visitContinueStatement(shared); + case SyntaxKind.ReturnStatement: return visitReturnStatement(shared); + case SyntaxKind.WithStatement: return visitWithStatement(shared); + case SyntaxKind.SwitchStatement: return visitSwitchStatement(shared); + case SyntaxKind.LabeledStatement: return visitLabeledStatement(shared); + case SyntaxKind.ThrowStatement: return visitThrowStatement(shared); + case SyntaxKind.TryStatement: return visitTryStatement(shared); + case SyntaxKind.FunctionDeclaration: return visitFunctionDeclaration(shared); + case SyntaxKind.ClassDeclaration: return visitClassDeclaration(shared); + case SyntaxKind.InterfaceDeclaration: return visitInterfaceDeclaration(shared); + case SyntaxKind.TypeAliasDeclaration: return visitTypeAliasDeclaration(shared); + case SyntaxKind.EnumDeclaration: return visitEnumDeclaration(shared); + case SyntaxKind.ModuleDeclaration: return visitModuleDeclaration(shared); + case SyntaxKind.ModuleBlock: return visitModuleBlock(shared); + case SyntaxKind.ImportEqualsDeclaration: return visitImportEqualsDeclaration(shared); + case SyntaxKind.ImportDeclaration: return visitImportDeclaration(shared); + case SyntaxKind.NamespaceExportDeclaration: return visitNamespaceExportDeclaration(shared); + case SyntaxKind.ExportDeclaration: return visitExportDeclaration(shared); + case SyntaxKind.ExportAssignment: return visitExportAssignment(shared); + default: Debug.assertNever(shared); + } + } + + function visitEmptyStatement(shared: SharedEmptyStatement): EmptyStatement { + const node = factory.createEmptyStatement() as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitDebuggerStatement(shared: SharedDebuggerStatement): DebuggerStatement { + const node = factory.createDebuggerStatement() as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitMissingDeclaration(shared: SharedMissingDeclaration): MissingDeclaration { + const node = factory.createMissingDeclaration() as Mutable; + node.name = shared.name && visit(shared.name, visitIdentifier); + node.modifiers = shared.modifiers && visit(shared.modifiers, visitModifiers); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitBlock(shared: SharedBlock): Block { + const node = factory.createBlock( + shared.statements && visit(shared.statements, visitStatements), + shared.multiLine, + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitVariableStatement(shared: SharedVariableStatement): VariableStatement { + const node = factory.createVariableStatement( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.declarationList && visit(shared.declarationList, visitVariableDeclarationList), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitExpressionStatement(shared: SharedExpressionStatement): ExpressionStatement { + const node = factory.createExpressionStatement( + shared.expression && visit(shared.expression, visitExpression), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitIfStatement(shared: SharedIfStatement): IfStatement { + const node = factory.createIfStatement( + shared.expression && visit(shared.expression, visitExpression), + shared.thenStatement && visit(shared.thenStatement, visitStatement), + shared.elseStatement && visit(shared.elseStatement, visitStatement), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitDoStatement(shared: SharedDoStatement): DoStatement { + const node = factory.createDoStatement( + shared.statement && visit(shared.statement, visitStatement), + shared.expression && visit(shared.expression, visitExpression), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitWhileStatement(shared: SharedWhileStatement): WhileStatement { + const node = factory.createWhileStatement( + shared.expression && visit(shared.expression, visitExpression), + shared.statement && visit(shared.statement, visitStatement), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitForInitializer(shared: SharedForInitializer): ForInitializer { + return shared.kind === SyntaxKind.VariableDeclarationList ? visitVariableDeclarationList(shared) : + visitExpression(shared); + } + + function visitForStatement(shared: SharedForStatement): ForStatement { + const node = factory.createForStatement( + shared.initializer && visit(shared.initializer, visitForInitializer), + shared.condition && visit(shared.condition, visitExpression), + shared.incrementor && visit(shared.incrementor, visitExpression), + shared.statement && visit(shared.statement, visitStatement), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitForInStatement(shared: SharedForInStatement): ForInStatement { + const node = factory.createForInStatement( + shared.initializer && visit(shared.initializer, visitForInitializer), + shared.expression && visit(shared.expression, visitExpression), + shared.statement && visit(shared.statement, visitStatement), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitForOfStatement(shared: SharedForOfStatement): ForOfStatement { + const node = factory.createForOfStatement( + shared.awaitModifier && visit(shared.awaitModifier, visitToken), + shared.initializer && visit(shared.initializer, visitForInitializer), + shared.expression && visit(shared.expression, visitExpression), + shared.statement && visit(shared.statement, visitStatement), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitBreakStatement(shared: SharedBreakStatement): BreakStatement { + const node = factory.createBreakStatement( + shared.label && visit(shared.label, visitIdentifier), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitContinueStatement(shared: SharedContinueStatement): ContinueStatement { + const node = factory.createContinueStatement( + shared.label && visit(shared.label, visitIdentifier), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitReturnStatement(shared: SharedReturnStatement): ReturnStatement { + const node = factory.createReturnStatement( + shared.expression && visit(shared.expression, visitExpression), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitWithStatement(shared: SharedWithStatement): WithStatement { + const node = factory.createWithStatement( + shared.expression && visit(shared.expression, visitExpression), + shared.statement && visit(shared.statement, visitStatement), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitSwitchStatement(shared: SharedSwitchStatement): SwitchStatement { + const node = factory.createSwitchStatement( + shared.expression && visit(shared.expression, visitExpression), + shared.caseBlock && visit(shared.caseBlock, visitCaseBlock), + ) as Mutable; + node.possiblyExhaustive = shared.possiblyExhaustive; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitCaseBlock(shared: SharedCaseBlock): CaseBlock { + const node = factory.createCaseBlock( + shared.clauses && visit(shared.clauses, visitCaseOrDefaultClauses), + ) as Mutable; + return finishNode(node, shared); + } + + function visitCaseOrDefaultClauses(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitCaseOrDefaultClause); + } + + function visitCaseOrDefaultClause(shared: SharedCaseOrDefaultClause): CaseOrDefaultClause { + switch (shared.kind) { + case SyntaxKind.CaseClause: return visitCaseClause(shared); + case SyntaxKind.DefaultClause: return visitDefaultClause(shared); + default: Debug.assertNever(shared); + } + } + + function visitCaseClause(shared: SharedCaseClause): CaseClause { + const node = factory.createCaseClause( + shared.expression && visit(shared.expression, visitExpression), + shared.statements && visit(shared.statements, visitStatements), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitDefaultClause(shared: SharedDefaultClause): DefaultClause { + const node = factory.createDefaultClause( + shared.statements && visit(shared.statements, visitStatements), + ) as Mutable; + return finishNode(node, shared); + } + + function visitLabeledStatement(shared: SharedLabeledStatement): LabeledStatement { + const node = factory.createLabeledStatement( + shared.label && visit(shared.label, visitIdentifier), + shared.statement && visit(shared.statement, visitStatement), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitThrowStatement(shared: SharedThrowStatement): ThrowStatement { + const node = factory.createThrowStatement( + shared.expression && visit(shared.expression, visitExpression), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitTryStatement(shared: SharedTryStatement): TryStatement { + const node = factory.createTryStatement( + shared.tryBlock && visit(shared.tryBlock, visitBlock), + shared.catchClause && visit(shared.catchClause, visitCatchClause), + shared.finallyBlock && visit(shared.finallyBlock, visitBlock), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitCatchClause(shared: SharedCatchClause): CatchClause { + const node = factory.createCatchClause( + shared.variableDeclaration && visit(shared.variableDeclaration, visitVariableDeclaration), + shared.block && visit(shared.block, visitBlock), + ) as Mutable; + return finishNode(node, shared); + } + + function visitClassElement(shared: SharedClassElement): ClassElement { + switch (shared.kind) { + case SyntaxKind.PropertyDeclaration: return visitPropertyDeclaration(shared); + case SyntaxKind.MethodDeclaration: return visitMethodDeclaration(shared); + case SyntaxKind.Constructor: return visitConstructorDeclaration(shared); + case SyntaxKind.SemicolonClassElement: return visitSemicolonClassElement(shared); + case SyntaxKind.GetAccessor: return visitGetAccessorDeclaration(shared); + case SyntaxKind.SetAccessor: return visitSetAccessorDeclaration(shared); + case SyntaxKind.IndexSignature: return visitIndexSignatureDeclaration(shared); + case SyntaxKind.ClassStaticBlockDeclaration: return visitClassStaticBlockDeclaration(shared); + default: Debug.assertNever(shared); + } + } + + function visitClassElements(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitClassElement) + } + + function visitClassDeclaration(shared: SharedClassDeclaration): ClassDeclaration { + const node = factory.createClassDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitIdentifier), + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.heritageClauses && visit(shared.heritageClauses, visitHeritageClauses), + shared.members && visit(shared.members, visitClassElements), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitClassExpression(shared: SharedClassExpression): ClassExpression { + const node = factory.createClassExpression( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitIdentifier), + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.heritageClauses && visit(shared.heritageClauses, visitHeritageClauses), + shared.members && visit(shared.members, visitClassElements), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitTypeElements(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitTypeElement); + } + + function visitTypeElement(shared: SharedTypeElement): TypeElement { + switch (shared.kind) { + case SyntaxKind.CallSignature: return visitCallSignatureDeclaration(shared); + case SyntaxKind.ConstructSignature: return visitConstructSignatureDeclaration(shared); + case SyntaxKind.MethodSignature: return visitMethodSignature(shared); + case SyntaxKind.IndexSignature: return visitIndexSignatureDeclaration(shared); + case SyntaxKind.PropertySignature: return visitPropertySignature(shared); + case SyntaxKind.GetAccessor: return visitGetAccessorDeclaration(shared); + case SyntaxKind.SetAccessor: return visitSetAccessorDeclaration(shared); + default: Debug.assertNever(shared); + } + } + + function visitInterfaceDeclaration(shared: SharedInterfaceDeclaration): InterfaceDeclaration { + const node = factory.createInterfaceDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitIdentifier), + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.heritageClauses && visit(shared.heritageClauses, visitHeritageClauses), + shared.members && visit(shared.members, visitTypeElements), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitHeritageClauses(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitHeritageClause) + } + + function visitExpressionsWithTypeArguments(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitExpressionWithTypeArguments); + } + + function visitHeritageClause(shared: SharedHeritageClause): HeritageClause { + const node = factory.createHeritageClause( + shared.token, + shared.types && visit(shared.types, visitExpressionsWithTypeArguments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitTypeAliasDeclaration(shared: SharedTypeAliasDeclaration): TypeAliasDeclaration { + const node = factory.createTypeAliasDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitIdentifier), + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitEnumMember(shared: SharedEnumMember): EnumMember { + const node = factory.createEnumMember( + shared.name && visit(shared.name, visitPropertyName), + shared.initializer && visit(shared.initializer, visitExpression), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitEnumMembers(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitEnumMember); + } + + function visitEnumDeclaration(shared: SharedEnumDeclaration): EnumDeclaration { + const node = factory.createEnumDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitIdentifier), + shared.members && visit(shared.members, visitEnumMembers), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitModuleName(shared: SharedModuleName): ModuleName { + switch (shared.kind) { + case SyntaxKind.Identifier: return visitIdentifier(shared); + case SyntaxKind.StringLiteral: return visitStringLiteral(shared); + default: Debug.assertNever(shared); + } + } + + function visitModuleBody(shared: SharedModuleBody): ModuleBody { + switch (shared.kind) { + case SyntaxKind.ModuleBlock: return visitModuleBlock(shared); + case SyntaxKind.ModuleDeclaration: return visitModuleDeclaration(shared) as NamespaceDeclaration; + case SyntaxKind.Identifier: return visitIdentifier(shared); + default: Debug.assertNever(shared); + } + } + + function visitModuleDeclaration(shared: SharedModuleDeclaration): ModuleDeclaration { + const node = factory.createModuleDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.name && visit(shared.name, visitModuleName), + shared.body && visit(shared.body, visitModuleBody), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitModuleBlock(shared: SharedModuleBlock): ModuleBlock { + const node = factory.createModuleBlock( + shared.statements && visit(shared.statements, visitStatements), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitModuleReference(shared: SharedModuleReference): ModuleReference { + switch (shared.kind) { + case SyntaxKind.Identifier: return visitIdentifier(shared); + case SyntaxKind.QualifiedName: return visitQualifiedName(shared); + case SyntaxKind.ExternalModuleReference: return visitExternalModuleReference(shared); + default: Debug.assertNever(shared); + } + } + + function visitImportEqualsDeclaration(shared: SharedImportEqualsDeclaration): ImportEqualsDeclaration { + const node = factory.createImportEqualsDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.isTypeOnly, + shared.name && visit(shared.name, visitIdentifier), + shared.moduleReference && visit(shared.moduleReference, visitModuleReference), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitExternalModuleReference(shared: SharedExternalModuleReference): ExternalModuleReference { + const node = factory.createExternalModuleReference( + shared.expression && visit(shared.expression, visitExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitImportDeclaration(shared: SharedImportDeclaration): ImportDeclaration { + const node = factory.createImportDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.importClause && visit(shared.importClause, visitImportClause), + shared.moduleSpecifier && visit(shared.moduleSpecifier, visitExpression), + shared.assertClause && visit(shared.assertClause, visitAssertClause), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitNamedImportBindings(shared: SharedNamedImportBindings): NamedImportBindings { + switch (shared.kind) { + case SyntaxKind.NamespaceImport: return visitNamespaceImport(shared); + case SyntaxKind.NamedImports: return visitNamedImports(shared); + default: Debug.assertNever(shared); + } + } + + function visitNamedExportBindings(shared: SharedNamedExportBindings): NamedExportBindings { + switch (shared.kind) { + case SyntaxKind.NamespaceExport: return visitNamespaceExport(shared); + case SyntaxKind.NamedExports: return visitNamedExports(shared); + default: Debug.assertNever(shared); + } + } + + function visitImportClause(shared: SharedImportClause): ImportClause { + const node = factory.createImportClause( + shared.isTypeOnly, + shared.name && visit(shared.name, visitIdentifier), + shared.namedBindings && visit(shared.namedBindings, visitNamedImportBindings), + ) as Mutable; + return finishNode(node, shared); + } + + function visitAssertionKey(shared: SharedAssertionKey): AssertionKey { + switch (shared.kind) { + case SyntaxKind.Identifier: return visitIdentifier(shared); + case SyntaxKind.StringLiteral: return visitStringLiteral(shared); + default: Debug.assertNever(shared); + } + } + + function visitAssertEntry(shared: SharedAssertEntry): AssertEntry { + const node = factory.createAssertEntry( + shared.name && visit(shared.name, visitAssertionKey), + shared.value && visit(shared.value, visitExpression), + ) as Mutable; + return finishNode(node, shared); + } + + function visitAssertEntries(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitAssertEntry); + } + + function visitAssertClause(shared: SharedAssertClause): AssertClause { + const node = factory.createAssertClause( + shared.elements && visit(shared.elements, visitAssertEntries), + shared.multiLine, + ) as Mutable; + return finishNode(node, shared); + } + + function visitNamespaceImport(shared: SharedNamespaceImport): NamespaceImport { + const node = factory.createNamespaceImport( + shared.name && visit(shared.name, visitIdentifier), + ) as Mutable; + return finishNode(node, shared); + } + + function visitNamespaceExport(shared: SharedNamespaceExport): NamespaceExport { + const node = factory.createNamespaceExport( + shared.name && visit(shared.name, visitIdentifier), + ) as Mutable; + return finishNode(node, shared); + } + + function visitNamespaceExportDeclaration(shared: SharedNamespaceExportDeclaration): NamespaceExportDeclaration { + const node = factory.createNamespaceExportDeclaration( + shared.name && visit(shared.name, visitIdentifier), + ) as Mutable; + node.modifiers = shared.modifiers && visit(shared.modifiers, visitModifiers); + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitExportDeclaration(shared: SharedExportDeclaration): ExportDeclaration { + const node = factory.createExportDeclaration( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.isTypeOnly, + shared.exportClause && visit(shared.exportClause, visitNamedExportBindings), + shared.moduleSpecifier && visit(shared.moduleSpecifier, visitExpression), + shared.assertClause && visit(shared.assertClause, visitAssertClause), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitImportSpecifiers(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitImportSpecifier); + } + + function visitExportSpecifiers(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitExportSpecifier); + } + + function visitNamedImports(shared: SharedNamedImports): NamedImports { + const node = factory.createNamedImports( + shared.elements && visit(shared.elements, visitImportSpecifiers), + ) as Mutable; + return finishNode(node, shared); + } + + function visitNamedExports(shared: SharedNamedExports): NamedExports { + const node = factory.createNamedExports( + shared.elements && visit(shared.elements, visitExportSpecifiers), + ) as Mutable; + return finishNode(node, shared); + } + + function visitImportSpecifier(shared: SharedImportSpecifier): ImportSpecifier { + const node = factory.createImportSpecifier( + shared.isTypeOnly, + shared.propertyName && visit(shared.propertyName, visitIdentifier), + shared.name && visit(shared.name, visitIdentifier), + ) as Mutable; + return finishNode(node, shared); + } + + function visitExportSpecifier(shared: SharedExportSpecifier): ExportSpecifier { + const node = factory.createExportSpecifier( + shared.isTypeOnly, + shared.propertyName && visit(shared.propertyName, visitIdentifier), + shared.name && visit(shared.name, visitIdentifier), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitExportAssignment(shared: SharedExportAssignment): ExportAssignment { + const node = factory.createExportAssignment( + shared.modifiers && visit(shared.modifiers, visitModifiers), + shared.isExportEquals, + shared.expression && visit(shared.expression, visitExpression), + ) as Mutable; + copyJSDoc(shared, node); + return finishNode(node, shared); + } + + function visitJSDocTypeExpression(shared: SharedJSDocTypeExpression): JSDocTypeExpression { + const node = factory.createJSDocTypeExpression( + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocNameReference(shared: SharedJSDocNameReference): JSDocNameReference { + const node = factory.createJSDocNameReference( + shared.name && visit(shared.name, visitJSDocEntityName), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocMemberName(shared: SharedJSDocMemberName): JSDocMemberName { + const node = factory.createJSDocMemberName( + shared.left && visit(shared.left, visitJSDocEntityName), + shared.right && visit(shared.right, visitIdentifier), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocAllType(shared: SharedJSDocAllType): JSDocAllType { + const node = factory.createJSDocAllType( + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocUnknownType(shared: SharedJSDocUnknownType): JSDocUnknownType { + const node = factory.createJSDocUnknownType( + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocNonNullableType(shared: SharedJSDocNonNullableType): JSDocNonNullableType { + const node = factory.createJSDocNonNullableType( + shared.type && visit(shared.type, visitTypeNode), + shared.postfix, + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocNullableType(shared: SharedJSDocNullableType): JSDocNullableType { + const node = factory.createJSDocNullableType( + shared.type && visit(shared.type, visitTypeNode), + shared.postfix, + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocOptionalType(shared: SharedJSDocOptionalType): JSDocOptionalType { + const node = factory.createJSDocOptionalType( + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocFunctionType(shared: SharedJSDocFunctionType): JSDocFunctionType { + const node = factory.createJSDocFunctionType( + shared.parameters && visit(shared.parameters, visitParameters), + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + node.typeParameters = shared.typeParameters && visit(shared.typeParameters, visitTypeParameters); + return finishNode(node, shared); + } + + function visitJSDocVariadicType(shared: SharedJSDocVariadicType): JSDocVariadicType { + const node = factory.createJSDocVariadicType( + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocNamepathType(shared: SharedJSDocNamepathType): JSDocNamepathType { + const node = factory.createJSDocNamepathType( + shared.type && visit(shared.type, visitTypeNode), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocTags(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitJSDocTag); + } + + function visitJSDocComments(sharedNodes: SharedNodeArray): NodeArray { + return visitNodes(sharedNodes, visitJSDocComment); + } + + function visitJSDocNode(shared: SharedJSDocNode): JSDoc { + const node = factory.createJSDocComment( + typeof shared.comment === "string" ? shared.comment : + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + shared.tags && visit(shared.tags, visitJSDocTags), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocTag(shared: SharedJSDocTag): JSDocTag { + switch (shared.kind) { + case SyntaxKind.JSDocTag: return visitJSDocUnknownTag(shared); + case SyntaxKind.JSDocAugmentsTag: return visitJSDocAugmentsTag(shared); + case SyntaxKind.JSDocImplementsTag: return visitJSDocImplementsTag(shared); + case SyntaxKind.JSDocAuthorTag: return visitJSDocAuthorTag(shared); + case SyntaxKind.JSDocDeprecatedTag: return visitJSDocDeprecatedTag(shared); + case SyntaxKind.JSDocClassTag: return visitJSDocClassTag(shared); + case SyntaxKind.JSDocPublicTag: return visitJSDocPublicTag(shared); + case SyntaxKind.JSDocPrivateTag: return visitJSDocPrivateTag(shared); + case SyntaxKind.JSDocProtectedTag: return visitJSDocProtectedTag(shared); + case SyntaxKind.JSDocReadonlyTag: return visitJSDocReadonlyTag(shared); + case SyntaxKind.JSDocOverrideTag: return visitJSDocOverrideTag(shared); + case SyntaxKind.JSDocEnumTag: return visitJSDocEnumTag(shared); + case SyntaxKind.JSDocThisTag: return visitJSDocThisTag(shared); + case SyntaxKind.JSDocTemplateTag: return visitJSDocTemplateTag(shared); + case SyntaxKind.JSDocSeeTag: return visitJSDocSeeTag(shared); + case SyntaxKind.JSDocReturnTag: return visitJSDocReturnTag(shared); + case SyntaxKind.JSDocTypeTag: return visitJSDocTypeTag(shared); + case SyntaxKind.JSDocTypedefTag: return visitJSDocTypedefTag(shared); + case SyntaxKind.JSDocCallbackTag: return visitJSDocCallbackTag(shared); + case SyntaxKind.JSDocOverloadTag: return visitJSDocOverloadTag(shared); + case SyntaxKind.JSDocThrowsTag: return visitJSDocThrowsTag(shared); + case SyntaxKind.JSDocPropertyTag: return visitJSDocPropertyTag(shared); + case SyntaxKind.JSDocParameterTag: return visitJSDocParameterTag(shared); + case SyntaxKind.JSDocSatisfiesTag: return visitJSDocSatisfiesTag(shared); + default: Debug.assertNever(shared); + } + } + + function visitJSDocEntityName(shared: SharedEntityName | SharedJSDocMemberName): EntityName | JSDocMemberName { + return shared.kind === SyntaxKind.JSDocMemberName ? visitJSDocMemberName(shared) : + visitEntityName(shared); + } + + function visitJSDocLink(shared: SharedJSDocLink): JSDocLink { + const node = factory.createJSDocLink( + shared.name && visit(shared.name, visitJSDocEntityName), + shared.text, + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocLinkCode(shared: SharedJSDocLinkCode): JSDocLinkCode { + const node = factory.createJSDocLinkCode( + shared.name && visit(shared.name, visitJSDocEntityName), + shared.text, + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocLinkPlain(shared: SharedJSDocLinkPlain): JSDocLinkPlain { + const node = factory.createJSDocLinkPlain( + shared.name && visit(shared.name, visitJSDocEntityName), + shared.text, + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocComment(shared: SharedJSDocComment): JSDocComment { + switch (shared.kind) { + case SyntaxKind.JSDocText: return visitJSDocText(shared); + case SyntaxKind.JSDocLink: return visitJSDocLink(shared); + case SyntaxKind.JSDocLinkCode: return visitJSDocLinkCode(shared); + case SyntaxKind.JSDocLinkPlain: return visitJSDocLinkPlain(shared); + default: Debug.assertNever(shared); + } + } + + function visitJSDocText(shared: SharedJSDocText): JSDocText { + const node = factory.createJSDocText( + shared.text, + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocUnknownTag(shared: SharedJSDocUnknownTag): JSDocUnknownTag { + const node = factory.createJSDocUnknownTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocAugmentsTag(shared: SharedJSDocAugmentsTag): JSDocAugmentsTag { + const node = factory.createJSDocAugmentsTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.class && visit(shared.class, visitExpressionWithTypeArguments) as ExpressionWithTypeArguments & { + readonly expression: Identifier | PropertyAccessEntityNameExpression; + }, + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocImplementsTag(shared: SharedJSDocImplementsTag): JSDocImplementsTag { + const node = factory.createJSDocImplementsTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.class && visit(shared.class, visitExpressionWithTypeArguments) as ExpressionWithTypeArguments & { + readonly expression: Identifier | PropertyAccessEntityNameExpression; + }, + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocAuthorTag(shared: SharedJSDocAuthorTag): JSDocAuthorTag { + const node = factory.createJSDocAuthorTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocDeprecatedTag(shared: SharedJSDocDeprecatedTag): JSDocDeprecatedTag { + const node = factory.createJSDocDeprecatedTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocClassTag(shared: SharedJSDocClassTag): JSDocClassTag { + const node = factory.createJSDocClassTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocPublicTag(shared: SharedJSDocPublicTag): JSDocPublicTag { + const node = factory.createJSDocPublicTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocPrivateTag(shared: SharedJSDocPrivateTag): JSDocPrivateTag { + const node = factory.createJSDocPrivateTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocProtectedTag(shared: SharedJSDocProtectedTag): JSDocProtectedTag { + const node = factory.createJSDocProtectedTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocReadonlyTag(shared: SharedJSDocReadonlyTag): JSDocReadonlyTag { + const node = factory.createJSDocReadonlyTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocOverrideTag(shared: SharedJSDocOverrideTag): JSDocOverrideTag { + const node = factory.createJSDocOverrideTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocEnumTag(shared: SharedJSDocEnumTag): JSDocEnumTag { + const node = factory.createJSDocEnumTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.typeExpression && visit(shared.typeExpression, visitJSDocTypeExpression), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocThisTag(shared: SharedJSDocThisTag): JSDocThisTag { + const node = factory.createJSDocThisTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.typeExpression && visit(shared.typeExpression, visitJSDocTypeExpression), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocTemplateTag(shared: SharedJSDocTemplateTag): JSDocTemplateTag { + const node = factory.createJSDocTemplateTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.constraint && visit(shared.constraint, visitJSDocTypeExpression), + shared.typeParameters && visit(shared.typeParameters, visitTypeParameters), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocSeeTag(shared: SharedJSDocSeeTag): JSDocSeeTag { + const node = factory.createJSDocSeeTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.name && visit(shared.name, visitJSDocNameReference), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocReturnTag(shared: SharedJSDocReturnTag): JSDocReturnTag { + const node = factory.createJSDocReturnTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.typeExpression && visit(shared.typeExpression, visitJSDocTypeExpression), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocTypeTag(shared: SharedJSDocTypeTag): JSDocTypeTag { + const node = factory.createJSDocTypeTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.typeExpression && visit(shared.typeExpression, visitJSDocTypeExpression), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocNamespaceDeclarationOrIdentifier(shared: SharedJSDocNamespaceDeclaration | SharedIdentifier): JSDocNamespaceDeclaration | Identifier { + return shared.kind === SyntaxKind.Identifier ? visitIdentifier(shared) : + visitModuleDeclaration(shared) as JSDocNamespaceDeclaration; + } + + function visitJSDocTypeExpressionOrTypeLiteral(shared: SharedJSDocTypeExpression | SharedJSDocTypeLiteral): JSDocTypeExpression | JSDocTypeLiteral { + return shared.kind === SyntaxKind.JSDocTypeExpression ? visitJSDocTypeExpression(shared) : + visitJSDocTypeLiteral(shared); + } + + function visitJSDocTypedefTag(shared: SharedJSDocTypedefTag): JSDocTypedefTag { + const node = factory.createJSDocTypedefTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.typeExpression && visit(shared.typeExpression, visitJSDocTypeExpressionOrTypeLiteral), + shared.fullName && visit(shared.fullName, visitJSDocNamespaceDeclarationOrIdentifier), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + node.name = getJSDocTypeAliasName(node.fullName); + return finishNode(node, shared); + } + + function visitJSDocCallbackTag(shared: SharedJSDocCallbackTag): JSDocCallbackTag { + const node = factory.createJSDocCallbackTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.typeExpression && visit(shared.typeExpression, visitJSDocSignature), + shared.fullName && visit(shared.fullName, visitJSDocNamespaceDeclarationOrIdentifier), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + node.name = getJSDocTypeAliasName(node.fullName); + return finishNode(node, shared); + } + + function visitJSDocOverloadTag(shared: SharedJSDocOverloadTag): JSDocOverloadTag { + const node = factory.createJSDocOverloadTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.typeExpression && visit(shared.typeExpression, visitJSDocSignature), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocThrowsTag(shared: SharedJSDocThrowsTag): JSDocThrowsTag { + const node = factory.createJSDocThrowsTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.typeExpression && visit(shared.typeExpression, visitJSDocTypeExpression), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocTemplateTags(shared: SharedArray): readonly JSDocTemplateTag[] { + return visitArray(shared, visitJSDocTemplateTag); + } + + function visitJSDocParameterTags(shared: SharedArray): readonly JSDocParameterTag[] { + return visitArray(shared, visitJSDocParameterTag); + } + + function visitJSDocSignature(shared: SharedJSDocSignature): JSDocSignature { + const node = factory.createJSDocSignature( + shared.typeParameters && visit(shared.typeParameters, visitJSDocTemplateTags), + shared.parameters && visit(shared.parameters, visitJSDocParameterTags), + shared.type && visit(shared.type, visitJSDocReturnTag), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocPropertyTag(shared: SharedJSDocPropertyTag): JSDocPropertyTag { + const node = factory.createJSDocPropertyTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.name && visit(shared.name, visitEntityName), + shared.isBracketed, + shared.typeExpression && visit(shared.typeExpression, visitJSDocTypeExpression), + shared.isNameFirst, + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocParameterTag(shared: SharedJSDocParameterTag): JSDocParameterTag { + const node = factory.createJSDocParameterTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.name && visit(shared.name, visitEntityName), + shared.isBracketed, + shared.typeExpression && visit(shared.typeExpression, visitJSDocTypeExpression), + shared.isNameFirst, + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocPropertyLikeTag(shared: SharedJSDocPropertyLikeTag): JSDocPropertyLikeTag { + return shared.kind === SyntaxKind.JSDocPropertyTag ? visitJSDocPropertyTag(shared) : + visitJSDocParameterTag(shared); + } + + function visitJSDocPropertyLikeTags(shared: SharedArray): readonly JSDocPropertyLikeTag[] { + return visitArray(shared, visitJSDocPropertyLikeTag); + } + + function visitJSDocTypeLiteral(shared: SharedJSDocTypeLiteral): JSDocTypeLiteral { + const node = factory.createJSDocTypeLiteral( + shared.jsDocPropertyTags && visit(shared.jsDocPropertyTags, visitJSDocPropertyLikeTags), + shared.isArrayType, + ) as Mutable; + return finishNode(node, shared); + } + + function visitJSDocSatisfiesTag(shared: SharedJSDocSatisfiesTag): JSDocSatisfiesTag { + const node = factory.createJSDocSatisfiesTag( + shared.tagName && visit(shared.tagName, visitIdentifier), + shared.typeExpression && visit(shared.typeExpression, visitJSDocTypeExpression), + typeof shared.comment === "string" ? shared.comment : + shared.comment && visit(shared.comment, visitJSDocComments), + ) as Mutable; + return finishNode(node, shared); + } + + function visitMap( + sharedMap: SharedMap, + visitKey: (key: KS) => K, + visitValue: (value: VS) => V, + ) { + const map = new Map(); + for (const [key, value] of SharedMap.entries(sharedMap)) { + map.set(visitKey(key), visitValue(value)); + } + return map; + } + + function visitDiagnosticMessageChains(shared: SharedArray): readonly DiagnosticMessageChain[] { + return visitArray(shared, visitDiagnosticMessageChain); + } + + function visitDiagnosticMessageChain(shared: SharedDiagnosticMessageChain): DiagnosticMessageChain { + return { + messageText: shared.messageText, + category: shared.category, + code: shared.code, + next: shared.next && visit(shared.next, visitDiagnosticMessageChains).slice(), + // TODO: repopulateInfo + }; + } + + function visitDiagnosticRelatedInformations(shared: SharedArray): readonly DiagnosticRelatedInformation[] { + return visitArray(shared, visitDiagnosticRelatedInformation); + } + + function visitDiagnosticRelatedInformation(shared: SharedDiagnosticRelatedInformation | SharedDiagnostic): DiagnosticRelatedInformation { + if (hasProperty(shared, "reportsUnnecessary") || + hasProperty(shared, "reportsDeprecated") || + hasProperty(shared, "source") || + hasProperty(shared, "relatedInformation") || + hasProperty(shared, "skippedOn")) { + return visitDiagnostic(shared as SharedDiagnostic); + } + return { + category: shared.category, + code: shared.code, + file, + start: shared.start, + length: shared.length, + messageText: typeof shared.messageText === "string" ? shared.messageText : + visitDiagnosticMessageChain(shared.messageText), + }; + } + + function visitDiagnostics(shared: SharedArray): readonly Diagnostic[]; + function visitDiagnostics(shared: SharedArray): readonly DiagnosticWithLocation[]; + function visitDiagnostics(shared: SharedArray) { + return visitArray(shared, visitDiagnostic); + } + + function visitDiagnostic(shared: SharedDiagnostic): Diagnostic; + function visitDiagnostic(shared: SharedDiagnosticWithLocation): DiagnosticWithLocation; + function visitDiagnostic(shared: SharedDiagnostic): Diagnostic { + let file: SourceFile | undefined; + if (shared.file) { + file = cache.get(shared.file) as SourceFile | undefined; + Debug.assert(file); + } + + return { + category: shared.category, + code: shared.code, + file, + start: shared.start, + length: shared.length, + messageText: typeof shared.messageText === "string" ? shared.messageText : + visitDiagnosticMessageChain(shared.messageText), + reportsUnnecessary: shared.reportsUnnecessary, + reportsDeprecated: shared.reportsDeprecated, + source: shared.source, + relatedInformation: shared.relatedInformation && visit(shared.relatedInformation, visitDiagnosticRelatedInformations).slice(), + }; + } + + function visitSourceFile(shared: SharedSourceFile): SourceFile { + const node = factory.createSourceFile( + shared.statements && visit(shared.statements, visitStatements), + shared.endOfFileToken && visit(shared.endOfFileToken, visitEndOfFileToken), + shared.flags + ) as Mutable; + finishNode(node, shared); + node.fileName = shared.fileName; + node.path = shared.path; + node.text = shared.text; + node.resolvedPath = shared.resolvedPath; + node.originalFileName = shared.originalFileName; + node.amdDependencies = shared.amdDependencies && (cache.get(shared.amdDependencies) as readonly AmdDependency[] ?? visitArray(shared.amdDependencies, identity)); + node.moduleName = shared.moduleName; + node.referencedFiles = shared.referencedFiles && (cache.get(shared.referencedFiles) as readonly FileReference[] ?? visitArray(shared.referencedFiles, identity)); + node.typeReferenceDirectives = shared.typeReferenceDirectives && (cache.get(shared.typeReferenceDirectives) as readonly FileReference[] ?? visitArray(shared.typeReferenceDirectives, identity)); + node.libReferenceDirectives = shared.libReferenceDirectives && (cache.get(shared.libReferenceDirectives) as readonly FileReference[] ?? visitArray(shared.libReferenceDirectives, identity)); + node.languageVariant = shared.languageVariant; + node.isDeclarationFile = shared.isDeclarationFile; + node.renamedDependencies = shared.renamedDependencies && visitMap(shared.renamedDependencies, identity, identity); + node.hasNoDefaultLib = shared.hasNoDefaultLib; + node.languageVersion = shared.languageVersion; + node.impliedNodeFormat = shared.impliedNodeFormat; + node.scriptKind = shared.scriptKind; + node.pragmas = cache.get(shared.pragmas) as ReadonlyPragmaMap ?? visitMap(shared.pragmas, identity, value => isSharedArray(value) ? visitArray(value, identity) : value); + if (shared.externalModuleIndicator === true) { + node.externalModuleIndicator = true; + } + else if (shared.externalModuleIndicator) { + node.externalModuleIndicator = cache.get(shared.externalModuleIndicator) as Node | undefined; + Debug.assert(node.externalModuleIndicator); + } + if (shared.commonJsModuleIndicator) { + node.commonJsModuleIndicator = cache.get(shared.commonJsModuleIndicator) as Node | undefined; + Debug.assert(node.commonJsModuleIndicator); + } + node.identifiers = shared.identifiers && visitMap(shared.identifiers, identity, identity); + node.nodeCount = shared.nodeCount; + node.identifierCount = shared.identifierCount; + node.symbolCount = shared.symbolCount; + node.parseDiagnostics = shared.parseDiagnostics && visit(shared.parseDiagnostics, visitDiagnostics).slice(); + Debug.assert(!shared.bindDiagnostics); + Debug.assert(!shared.bindSuggestionDiagnostics); + Debug.assert(!shared.lineMap); + node.jsDocDiagnostics = shared.jsDocDiagnostics && visit(shared.jsDocDiagnostics, visitDiagnostics).slice(); + node.commentDirectives = shared.commentDirectives && (cache.get(shared.commentDirectives) as readonly CommentDirective[] ?? visitArray(shared.commentDirectives, identity)); + node.checkJsDirective = shared.checkJsDirective; + if (shared.version) { + node.version = shared.version; + } + return node; + } + + function visit(shared: T, visit: (value: T) => U): U { + return cache.get(shared) as U ?? visit(shared); + } + + function visitNodes(sharedNodes: SharedNodeArray, visit: (value: T) => U): NodeArray { + const nodes = factory.createNodeArray(visitArray(sharedNodes.items, visit), sharedNodes.hasTrailingComma) as MutableNodeArray; + nodes.pos = sharedNodes.pos; + nodes.end = sharedNodes.end; + nodes.hasTrailingComma = sharedNodes.hasTrailingComma; + nodes.transformFlags = sharedNodes.transformFlags; + if (sharedNodes.isMissingList) { + (nodes as any).isMissingList = true; + } + return nodes; + } + + function visitArray(sharedArray: SharedArray, visit: (value: T) => U): readonly U[] { + const nodes: U[] = []; + for (let i = 0; i < sharedArray.length; i++) { + nodes[i] = visit(sharedArray[i]); + } + return nodes; + } + + function copyJSDoc(shared: SharedJSDocContainer, node: Mutable): void { + node.jsDoc = shared.jsDoc && visitArray(shared.jsDoc, visitJSDocNode); + } + + function finishNode(node: Mutable, shared: SharedNodeBase): T { + node.pos = shared.pos; + node.end = shared.end; + node.flags = shared.flags; + node.transformFlags = shared.transformFlags; + node.__shared__ = shared; + cache.set(shared, node); + if (shared.parent) { + needsParent.push([shared, node]); + } + return node; + } +} diff --git a/src/compiler/sharing/sharedNodeArray.ts b/src/compiler/sharing/sharedNodeArray.ts new file mode 100644 index 00000000000..fdba28abcfb --- /dev/null +++ b/src/compiler/sharing/sharedNodeArray.ts @@ -0,0 +1,29 @@ +import { TransformFlags } from "../types"; +import type { SharedNodeBase } from "./sharedNode"; +import { Identifiable } from "./structs/identifiableStruct"; +import { isShareableNonPrimitive } from "./structs/shareable"; +import { Shared, SharedStructBase } from "./structs/sharedStruct"; +import { isTaggedStruct, Tag, Tagged } from "./structs/taggedStruct"; + +/** @internal */ +@Shared() +export class SharedNodeArray extends Identifiable(Tagged(SharedStructBase, Tag.NodeArray)) { + @Shared() items!: SharedArray; + @Shared() pos = -1; + @Shared() end = -1; + @Shared() hasTrailingComma = false; + @Shared() transformFlags = TransformFlags.None; + @Shared() isMissingList = false; + + static * values(self: SharedNodeArray): IterableIterator { + for (let i = 0; i < self.items.length; i++) { + yield self.items[i]; + } + } + + static [Symbol.hasInstance](value: unknown): value is SharedNodeArray { + return isShareableNonPrimitive(value) && + isTaggedStruct(value) && + value.__tag__ === Tag.NodeArray; + } +} diff --git a/src/compiler/sharing/sharedObjectAllocator.ts b/src/compiler/sharing/sharedObjectAllocator.ts new file mode 100644 index 00000000000..d9ec2e254b4 --- /dev/null +++ b/src/compiler/sharing/sharedObjectAllocator.ts @@ -0,0 +1,37 @@ +import { Identifier, Node, NodeArray, PrivateIdentifier, SourceFile, SyntaxKind, Token } from "../types"; +import { ObjectAllocator, objectAllocator } from "../utilities"; +import { getSharedConstructorForKind, SharedNodeBase } from "./sharedNode"; +import { SharedNodeArray } from "./sharedNodeArray"; + +function NodeArray(items: readonly any[], hasTrailingComma?: boolean) { + const array = new SharedNodeArray(); + array.items = new SharedArray(items.length); + for (let i = 0; i < items.length; i++) { + array.items[i] = items[i]; + } + array.hasTrailingComma = !!hasTrailingComma; + return array; +} + +function Node(kind: SyntaxKind, pos: number, end: number) { + const SharedNode = getSharedConstructorForKind(kind); + const node = new SharedNode() as SharedNodeBase; + node.kind = kind; + node.pos = pos; + node.end = end; + return node; +} + +/** @internal */ +export function getSharedObjectAllocator(): ObjectAllocator { + return { + ...objectAllocator, + getNodeArrayConstructor: () => NodeArray as unknown as new (items: readonly T[], hasTrailingComma?: boolean | undefined) => NodeArray, + getNodeConstructor: () => Node as unknown as new (kind: SyntaxKind, pos: number, end: number) => Node, + getTokenConstructor: () => Node as unknown as new (kind: Kind, pos: number, end: number) => Token, + getIdentifierConstructor: () => Node as unknown as new (kind: SyntaxKind.Identifier, pos: number, end: number) => Identifier, + getPrivateIdentifierConstructor: () => Node as unknown as new (kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) => PrivateIdentifier, + getSourceFileConstructor: () => Node as unknown as new (kind: SyntaxKind.SourceFile, pos: number, end: number) => SourceFile, + // getSymbolConstructor: () => SymbolConstructor, + }; +} diff --git a/src/compiler/sharing/sharedParserState.ts b/src/compiler/sharing/sharedParserState.ts new file mode 100644 index 00000000000..6ca12aa8bfb --- /dev/null +++ b/src/compiler/sharing/sharedParserState.ts @@ -0,0 +1,51 @@ +import { Condition } from "../threading/condition"; +import { Mutex } from "../threading/mutex"; +import { SharedMutex } from "../threading/sharedMutex"; +import { ResolutionMode, ScriptTarget } from "../types"; +import { SharedMap } from "./collections/sharedMap"; +import { SharedSourceFile } from "./sharedNode"; +import { Shared, SharedStructBase } from "./structs/sharedStruct"; + +/** @internal */ +@Shared() +export class SharedParserState extends SharedStructBase { + @Shared() sharedMutex = new SharedMutex(); + @Shared() files = new SharedMap(); +} + +/** @internal */ +@Shared() +export class SharedSourceFileEntry extends SharedStructBase { + @Shared() parserState: SharedParserState; + @Shared() setParentNodes: boolean; + @Shared() setFileVersion: boolean; + @Shared() fileName: string; + @Shared() languageVersion: ScriptTarget; + @Shared() impliedNodeFormat: ResolutionMode | undefined; + @Shared() shouldCreateNewSourceFile: boolean | undefined; + @Shared() done = false; + @Shared() error = false; + @Shared() file: SharedSourceFile | undefined; + @Shared() fileMutex = new Mutex(); + @Shared() fileCondition = new Condition(); + + constructor( + parserState: SharedParserState, + setFileVersion: boolean, + setParentNodes: boolean, + fileName: string, + languageVersion: ScriptTarget, + impliedNodeFormat: ResolutionMode | undefined, + shouldCreateNewSourceFile: boolean | undefined, + ) { + super(); + this.parserState = parserState; + this.setFileVersion = setFileVersion; + this.setParentNodes = setParentNodes; + this.fileName = fileName; + this.languageVersion = languageVersion; + this.impliedNodeFormat = impliedNodeFormat; + this.shouldCreateNewSourceFile = shouldCreateNewSourceFile; + } +} + diff --git a/src/compiler/sharing/sharedSymbol.ts b/src/compiler/sharing/sharedSymbol.ts new file mode 100644 index 00000000000..533db898ab4 --- /dev/null +++ b/src/compiler/sharing/sharedSymbol.ts @@ -0,0 +1,36 @@ +import { __String, SymbolFlags } from "../types"; +import { SharedResizableArray } from "./collections/sharedResizableArray"; +import { SharedMap } from "./collections/sharedMap"; +import { Identifiable } from "./structs/identifiableStruct"; +import { SharedDeclaration } from "./sharedNode"; +import { Shared, SharedStructBase } from "./structs/sharedStruct"; +import { Tag, Tagged } from "./structs/taggedStruct"; + +declare global { + interface OtherShareablePrimitive { + __String: __String; + } +} + +/** @internal */ +export interface SharedSymbolTable extends SharedMap<__String, SharedSymbol> { +} + +/** @internal */ +@Shared() +export class SharedSymbol extends Identifiable(Tagged(SharedStructBase, Tag.Symbol)) { + @Shared() flags!: SymbolFlags; + @Shared() escapedName!: __String; + @Shared() declarations: SharedResizableArray | undefined; + @Shared() valueDeclaration: SharedDeclaration | undefined; + @Shared() members: SharedSymbolTable | undefined; + @Shared() exports: SharedSymbolTable | undefined; + @Shared() globalExports: SharedSymbolTable | undefined; + @Shared() parent: SharedSymbol | undefined; + @Shared() exportSymbol: SharedSymbol | undefined; + @Shared() constEnumOnlyModule: boolean | undefined; + @Shared() isReferenced: SymbolFlags | undefined; + @Shared() isReplaceableByMethod: boolean | undefined; + @Shared() isAssigned: boolean | undefined; + @Shared() assignmentDeclarationMembers: SharedMap | undefined; +} diff --git a/src/compiler/sharing/structs/fakeSharedStruct.ts b/src/compiler/sharing/structs/fakeSharedStruct.ts new file mode 100644 index 00000000000..2a7d02d679c --- /dev/null +++ b/src/compiler/sharing/structs/fakeSharedStruct.ts @@ -0,0 +1,145 @@ +// Brand used to identify a fake shared struct, so that we can emulate the restriction that shared struct fields + +import { isNull } from "../../core"; + +// can only contain shareable primitives and other shared structs. +const allFakeSharedStructInstances = new WeakSet(); +const allFakeSharedArrayInstances = new WeakSet(); + +// Emulates the behavior of the Shared Structs origin trial, for forwards compatibility. +const proxyHandler: ProxyHandler = { + set(target, p, value, receiver) { + switch (typeof value) { + case "undefined": + case "string": + case "number": + case "boolean": + break; + case "object": + if (isNull(value) || allFakeSharedStructInstances.has(value) || allFakeSharedArrayInstances.has(value)) { // eslint-disable-line no-null/no-null + break; + } + // falls through + default: + throw new TypeError("value not shareable"); + } + return Reflect.set(target, p, value, receiver); + }, + defineProperty(target, property, attributes) { + const { value } = attributes; + switch (typeof value) { + case "undefined": + case "string": + case "number": + case "boolean": + break; + case "object": + if (isNull(value) || allFakeSharedStructInstances.has(value) || allFakeSharedArrayInstances.has(value)) { // eslint-disable-line no-null/no-null + break; + } + // falls through + default: + throw new TypeError("value not shareable"); + } + return Reflect.defineProperty(target, property, attributes); + }, +}; + +/** + * Creates a new fake SharedStructType constructor that emulates fixed-shape object behavior, though the result can't + * actually be shared across threads. Fake shared structs are useful in single-threaded scenarios when SharedStructType + * isn't available and allows us to write single threaded code that is forwards-compatible with eventual multi-threaded + * code. + * @internal + */ +export const FakeSharedStructType = class FakeSharedStructType { + constructor(fields: readonly string[]) { + const thisFakeSharedStructInstances = new WeakSet(); + class FakeSharedStruct { // eslint-disable-line @typescript-eslint/no-unsafe-declaration-merging + constructor() { + // Fields must be explicitly defined and are not configurable. + for (const field of fields) { + Object.defineProperty(this, field, { + enumerable: false, + configurable: false, + writable: true, + value: undefined + }); + } + Object.setPrototypeOf(this, null); // eslint-disable-line no-null/no-null + Object.preventExtensions(this); + + const fakeSharedStruct = new Proxy(this, proxyHandler); + allFakeSharedStructInstances.add(fakeSharedStruct); + thisFakeSharedStructInstances.add(fakeSharedStruct); + return fakeSharedStruct as this; + } + + static [Symbol.hasInstance](value: unknown) { + try { + return typeof value === "object" && !isNull(value) && thisFakeSharedStructInstances.has(value); + } + catch { + return false; + } + } + } + + // augment the interface with the brand from `SharedStruct`. + interface FakeSharedStruct extends SharedStruct { // eslint-disable-line @typescript-eslint/no-unsafe-declaration-merging + } + + Object.setPrototypeOf(FakeSharedStruct.prototype, null); // eslint-disable-line no-null/no-null + return FakeSharedStruct; + } + + static isSharedStruct(value: unknown): value is SharedStruct { + try { + return typeof value === "object" && !isNull(value) && allFakeSharedStructInstances.has(value); + } + catch { + return false; + } + } +} as SharedStructTypeConstructor; + +/** + * Creates a new fake SharedArray constructor that emulates fixed-shape object behavior, though the result can't + * actually be shared across threads. Fake shared structs are useful in single-threaded scenarios when SharedStructType + * isn't available and allows us to write single threaded code that is forwards-compatible with eventual multi-threaded + * code. + * @internal + */ +export const FakeSharedArray = class FakeSharedArray { + constructor(length: number) { + for (let i = 0; i < length; i++) { + Object.defineProperty(this, i, { + enumerable: false, + configurable: false, + writable: true, + value: undefined + }); + } + Object.defineProperty(this, "length", { + enumerable: false, + configurable: false, + writable: false, + value: length + }); + Object.setPrototypeOf(this, null); // eslint-disable-line no-null/no-null + Object.preventExtensions(this); + + const fakeSharedArray = new Proxy(this, proxyHandler); + allFakeSharedArrayInstances.add(fakeSharedArray); + return fakeSharedArray as this; + } + + static [Symbol.hasInstance](value: unknown) { + try { + return typeof value === "object" && !isNull(value) && allFakeSharedArrayInstances.has(value); + } + catch { + return false; + } + } +} as SharedArrayConstructor; diff --git a/src/compiler/sharing/structs/identifiableStruct.ts b/src/compiler/sharing/structs/identifiableStruct.ts new file mode 100644 index 00000000000..f8f5161d0cc --- /dev/null +++ b/src/compiler/sharing/structs/identifiableStruct.ts @@ -0,0 +1,52 @@ +import { workerThreads } from "../../workerThreads"; +import { Shared } from "./sharedStruct"; + +const identifiableConstructors = new WeakSet(); + +let typeCount = 0; + +/** @internal */ +export interface IdentifiableStruct extends SharedStruct { + readonly __hash__: number; +} + +/** + * Defines an "identity hash" on a shared struct, allowing it to be used as a key in a `SharedMap` or `SharedSet`. + * @internal + */ +export function Identifiable any>(base: C): C & (abstract new (...args: any) => IdentifiableStruct) { + if (isIdentifiableConstructor(base)) { + // constructor is already marked as identifiable. Nothing to do here. + return base; + } + + const threadId = workerThreads?.threadId ?? 0; + const typeId = typeCount++; + let instanceCount = 0; + + @Shared({ abstract: true }) + abstract class IdentifiableStruct extends base { + @Shared() readonly __hash__: number; + constructor(...args: any) { + super(...args); + const instanceId = instanceCount++; + let hc = threadId; + hc = ((hc << 7) | (hc >>> 25)) ^ typeId; + hc = ((hc << 7) | (hc >>> 25)) ^ instanceId; + this.__hash__ = hc; + } + } + + identifiableConstructors.add(IdentifiableStruct); + return IdentifiableStruct; +} + +function isIdentifiableConstructor(value: object | null) { + while (value) { + if (identifiableConstructors.has(value)) { + return true; + } + value = Object.getPrototypeOf(value); + } + return false; +} \ No newline at end of file diff --git a/src/compiler/sharing/structs/shareable.ts b/src/compiler/sharing/structs/shareable.ts new file mode 100644 index 00000000000..f10b28fa52d --- /dev/null +++ b/src/compiler/sharing/structs/shareable.ts @@ -0,0 +1,54 @@ +import { isNull } from "../../core"; +import { FakeSharedArray, FakeSharedStructType } from "./fakeSharedStruct"; + +/** + * Returns whether the provided value is a shareable primitive value. + * @internal + */ +export function isShareablePrimitive(value: unknown): value is ShareablePrimitive { + switch (typeof value) { + case "undefined": + case "string": + case "number": + case "boolean": + case "symbol": + return true; + case "object": + return value === null; // eslint-disable-line no-null/no-null -- necessary for comparison + default: + return false; + } +} + +// You cannot use `instanceof` with shared structs and there is no `isArray`-like test currently. The only way we have +// to determine if a value is a shareable non-primitive (i.e., a shared struct, `SharedArray`, `Atomics.Mutex`, or +// `Atomics.Condition`) is to try and assign it to a property of a shared struct and catch the exception. This will +// hopefully be addressed in a future version of the origin trial. + +const supportsSharedStructs = typeof SharedStructType === "function"; +const sharedStructTypeConstructor = supportsSharedStructs ? SharedStructType : FakeSharedStructType; +const sharedArrayConstructor = supportsSharedStructs ? SharedArray : FakeSharedArray; + +/** + * Returns whether the provided value is a shareable non-primitive value. + * @internal + */ +export function isShareableNonPrimitive(value: unknown): value is ShareableNonPrimitive { + if (typeof value !== "object" || isNull(value)) return false; + return sharedStructTypeConstructor.isSharedStruct(value) || + value instanceof sharedArrayConstructor || + supportsSharedStructs && (value instanceof Atomics.Mutex || value instanceof Atomics.Condition); +} + +/** + * Returns whether the provided value is a shareable value. + * @internal + */ +export function isShareable(value: unknown): value is Shareable { + return isShareablePrimitive(value) || isShareableNonPrimitive(value); +} + +/** @internal */ +export function isSharedArray(value: unknown): value is SharedArray { + return value instanceof SharedArray; +} diff --git a/src/compiler/sharing/structs/sharedStruct.ts b/src/compiler/sharing/structs/sharedStruct.ts new file mode 100644 index 00000000000..d12638b722c --- /dev/null +++ b/src/compiler/sharing/structs/sharedStruct.ts @@ -0,0 +1,333 @@ +import "../../symbolMetadataShim"; +import "./sharedStructsGlobals"; + +import { Debug } from "../../debug"; +import { FakeSharedStructType } from "./fakeSharedStruct"; +import { AbstractConstructor } from "../../types"; + +// Holds the fields tracked with `@Shared()` +const weakSharedFields = new WeakMap(); + +// Holds the `SharedStructType` instances produced for classes tracked with `@Shared()` +const weakSharedStructType = new WeakMap(); + +// WeakRef and FinalizationRegistry may not exist, so we'll fall back to a non-weak shim when unavailable +interface WeakRef { + deref(): T | undefined; +} + +interface FinalizationRegistry { + register(target: WeakKey, heldValue: T, unregisterToken?: WeakKey): void; + unregister(unregisterToken: WeakKey): void; +} + +declare var WeakRef: new (value: T) => WeakRef; +declare var FinalizationRegistry: new (cleanupCallback: (heldValue: T) => void) => FinalizationRegistry; + +class FakeWeakRef implements WeakRef { + private _target: T; // NOTE: Not actually weak + constructor(target: T) { + this._target = target; + } + deref(): T | undefined { + return this._target; + } +} + +class FakeFinalizationRegistry implements FinalizationRegistry { + constructor(cleanupCallback: (heldValue: T) => void) { + void cleanupCallback; + } + register(target: WeakKey, heldValue: T, unregisterToken?: WeakKey): void { + void target; + void heldValue; + void unregisterToken; + } + unregister(unregisterToken: WeakKey): void { + void unregisterToken; + } +} + +function createWeakRef(value: T) { + return typeof WeakRef === "function" ? new WeakRef(value) : new FakeWeakRef(value); +} + +function createFinalizationRegistry(cleanupCallback: (heldValue: T) => void): FinalizationRegistry { + return typeof FinalizationRegistry === "function" ? new FinalizationRegistry(cleanupCallback) : new FakeFinalizationRegistry(cleanupCallback); +} + +function createSharedStructType(fields: readonly string[]) { + // If shared structs are available, create a shared struct type to use when creating a new instance of the + // target. Otherwise, create a fake shared struct that emulates the required semantics to ensure we are forwards + // compatible. + return typeof SharedStructType === "function" ? new SharedStructType(fields) : new FakeSharedStructType(fields); +} + +class SharedStructTypeFactory { + private _fields: readonly string[]; + private _structType: WeakRef SharedStruct> | undefined; + private _arrayTypes: Map SharedStruct>> | undefined; + private _registry: FinalizationRegistry; + constructor(fields: readonly string[]) { + this._fields = fields; + this._registry = createFinalizationRegistry(length => { this._arrayTypes?.delete(length); }); + } + + structType(): new () => SharedStruct { + let structType = this._structType?.deref(); + if (!structType) { + structType = createSharedStructType(this._fields); + this._structType = createWeakRef(structType); + } + return structType; + } + + arrayType(length: number): new () => SharedStruct { + let arrayType = this._arrayTypes?.get(length)?.deref(); + if (!arrayType) { + const fields = new Set(this._fields); + fields.add("length"); + for (let i = 0; i < length; i++) { + fields.add(`${i}`); + } + arrayType = createSharedStructType([...fields]); + this._arrayTypes ??= new Map(); + this._arrayTypes.set(length, createWeakRef(arrayType)); + this._registry.register(arrayType, length); + } + return arrayType; + } +} + +interface SharedClassDecorator { + (target: T, context: ClassDecoratorContext): void; + (target: T): void; +} + +interface SharedClassOptions { + abstract?: boolean; +} + +interface SharedFieldDecorator { + (target: undefined, context: ClassFieldDecoratorContext & { private: false, static: false, name: string }): void; + (target: This, propertyKey: string, descriptor?: TypedPropertyDescriptor): void; +} + +function getOrCreateMetadata(target: AbstractConstructor) { + if (Object.prototype.hasOwnProperty.call(target, Symbol.metadata)) { + return target[Symbol.metadata]; + } + const metadata = Object.create(target[Symbol.metadata] ?? null); + Object.defineProperty(target, Symbol.metadata, { + enumerable: false, + configurable: true, + writable: true, + value: metadata + }); + return metadata; +} + +/** + * A decorator used to mark a `class` or a non-static public class field as "shared". This is intended to be used to + * emulate syntax for shared structs and provide a mechanism to associate types with shared struct fields. + * @internal + */ +export function Shared(): SharedClassDecorator & SharedFieldDecorator; +/** + * A decorator used to mark a `class` as "shared". This is intended to be used to emulate syntax for shared structs. + * @param options.abstract Indicates the `class` is `abstract` and cannot be constructed directly. + * @internal + */ +export function Shared(options: SharedClassOptions): SharedClassDecorator; +export function Shared(options?: SharedClassOptions) { + function SharedClassDecorator(target: T, context: ClassDecoratorContext) { + if (weakSharedStructType.has(target)) { + Debug.fail("@Shared() cannot be applied to the same class more than once."); + } + + // Delete the default `constructor` property from the prototype, as shared structs cannot currently have + // properties on the prototype. + Reflect.deleteProperty(target.prototype, "constructor"); + + // Validate the prototype chain. Even though `@Shared` class supertypes will have a null prototype, there may + // have been non-`@Shared` abstract classes in between. + let prototype: object | null = target.prototype; + while (prototype) { + if (Reflect.ownKeys(target.prototype).length > 0) { + Debug.fail(`Shared struct type '${context.name}' cannot have methods on its prototype.`); + } + + prototype = Object.getPrototypeOf(prototype); + + // Break early when we find a `@Shared` supertype since it should have already been validated. + if (prototype && weakSharedStructType.has(prototype)) { + break; + } + } + + // Give the constructor a null-ish prototype. We can't set it directly because the `prototype` property of a + // class constructor is non-configurable, non-writable. Instead, we set it's `[[Prototype]]` to `null`. + Object.setPrototypeOf(target.prototype, null); // eslint-disable-line no-null/no-null + + // If the class is `abstract`, do not associate a shared struct type with it. + if (options?.abstract) { + return; + } + + // If decorator metadata is not available we cannot emulate shared struct. In this case we are in an environment + // where `../symbolMetadataShim` could not execute and likely doesn't support `SharedStructType` anyways. In + // that case, we fall back to a regular object with no sharing support. + if (!context.metadata) { + weakSharedStructType.set(target.prototype, Object as any); + return; + } + + // Collect all of the `@Shared` fields in declaration order, with supertype fields coming first in the list. + let fields: string[] = []; + let metadata: DecoratorMetadata | null = context.metadata; + while (metadata) { + const sharedFields = weakSharedFields.get(metadata); + if (sharedFields) { + fields = [...sharedFields, ...fields]; + } + metadata = Object.getPrototypeOf(metadata); + } + + // Remove duplicate field names + fields = [...new Set(fields)]; + + weakSharedStructType.set(target.prototype, new SharedStructTypeFactory(fields)); + return; + } + + function SharedFieldDecorator(_target: undefined, context: ClassFieldDecoratorContext) { + Debug.assert(!context.private, "@Shared is not supported on private fields."); + Debug.assert(!context.static, "@Shared is not supported on static fields."); + Debug.assert(typeof context.name === "string", "@Shared cannot be used on symbol-named fields."); + + // If `context.metadata` does not exists then we cannot record shared field information about the class and + // should do nothing. The `@Shared` decorator on the class will perform appropriate fallback behavior. + if (!context.metadata) { + return; + } + + // Get or create an array of `@Shared` field names to be used to construct a SharedStructType for the class. + let sharedFields = weakSharedFields.get(context.metadata); + if (!sharedFields) weakSharedFields.set(context.metadata, sharedFields = []); + + Debug.assert(!sharedFields.includes(context.name), "@Shared cannot be specified more than once on the same declaration."); + sharedFields.push(context.name); + } + + function decorator(...args: + | [target: AbstractConstructor] + | [target: AbstractConstructor, context: ClassDecoratorContext] + | [target: undefined, context: ClassFieldDecoratorContext] + | [target: object, propertyKey: string, descriptor?: PropertyDescriptor] + ) { + if (args.length === 1) { + const [target] = args; + return SharedClassDecorator(target, { + name: target.name, + get metadata() { return getOrCreateMetadata(target); } + } as ClassDecoratorContext); + } + else if (typeof args[1] === "string") { + const [target, propertyKey, _descriptor] = args; + Debug.assert(target); + return SharedFieldDecorator( + /*target*/ undefined, + { + kind: "field", + name: propertyKey, + public: true, + static: false, + private: false, + get metadata() { return getOrCreateMetadata(target.constructor as AbstractConstructor); } + } as unknown as ClassFieldDecoratorContext + ); + } + else { + const [target, context] = args; + switch (context.kind) { + case "class": return SharedClassDecorator(target as AbstractConstructor, context); + case "field": return SharedFieldDecorator(target as undefined, context); + default: Debug.fail(`@Shared is not supported on ${(context as DecoratorContext).kind} declarations.`); + } + } + } + + return decorator; +} + +class NullObject extends null { // eslint-disable-line no-null/no-null + constructor() { + const self = Object.create(new.target.prototype); + Object.setPrototypeOf(self, null); // eslint-disable-line no-null/no-null + return self; + } +} + +Object.setPrototypeOf(NullObject.prototype, null); // eslint-disable-line no-null/no-null + +/** @internal */ +@Shared({ abstract: true }) +// The compiler doesn't like us using `extends null` in `SharedStructBase` due to the interface extension to +// opt into the `SharedStruct` brand. If we use `extends null` below we end up with this error: +// +// Class static side 'typeof SharedStructBase' incorrectly extends base class static side 'null'.ts(2417) +// +export abstract class SharedStructBase extends NullObject { // eslint-disable-line @typescript-eslint/no-unsafe-declaration-merging + constructor() { + // Find the first `SharedStructTypeFactory` instance associated with this prototype chain + let prototype: object | null = new.target.prototype; + while (prototype) { + const StructType = weakSharedStructType.get(prototype)?.structType(); + if (StructType) { + // This will either be an instance of `SharedStructType` (if shared structs and `context.metadata` are + // available), an instance of `FakeSharedStructType` (if shared structs are not available, but + // `context.metadata` is), or `Object` (if neither shared structs nor `context.metadata` are available). + return new StructType(); + } + prototype = Object.getPrototypeOf(prototype); + } + Debug.fail("Class not marked @Shareable()"); + super(); + } +} + +// Reopen `SharedStructBase` to give it the `SharedStruct` brand. +/** @internal */ +export interface SharedStructBase extends SharedStruct { // eslint-disable-line @typescript-eslint/no-unsafe-declaration-merging +} + +// /** @internal */ +// @Shared({ abstract: true }) +// export abstract class SharedArrayBase extends SharedStructBase { +// @Shared() readonly length!: number; +// constructor(length: number) { +// // Find the first `SharedStructTypeFactory` instance associated with this prototype chain +// let prototype: object | null = new.target.prototype; +// while (prototype) { +// const StructType = weakSharedStructType.get(prototype)?.arrayType(length); +// if (StructType) { +// // This will either be an instance of `SharedStructType` (if shared structs and `context.metadata` are +// // available), an instance of `FakeSharedStructType` (if shared structs are not available, but +// // `context.metadata` is), or `Object` (if neither shared structs nor `context.metadata` are available). +// const inst = new StructType() as Mutable>; +// inst.length = length; +// return inst; +// } +// prototype = Object.getPrototypeOf(prototype); +// } +// Debug.fail("Class not marked @Shareable()"); +// super(); +// } + +// [index: number]: T; +// } + +// // Reopen `SharedArrayBase` to give it the `SharedArray` brand. +// /** @internal */ +// export interface SharedArrayBase extends SharedArray { // eslint-disable-line @typescript-eslint/no-unsafe-declaration-merging +// } \ No newline at end of file diff --git a/src/compiler/sharing/structs/sharedStructsGlobals.ts b/src/compiler/sharing/structs/sharedStructsGlobals.ts new file mode 100644 index 00000000000..57c4d96640c --- /dev/null +++ b/src/compiler/sharing/structs/sharedStructsGlobals.ts @@ -0,0 +1,124 @@ +export { }; + +// NOTE: These types relate to the origin trial implementation of the shared structs proposal and may not be indicative +// of the final proposal. To use these types you must pass `--shared-string-table --harmony-struct` to NodeJS. + +// the following brands are used to distinguish Shared Structs-related objects from other objects since they are not +// interchangeable with `object`: +declare const kGlobalSharedStructBrand: unique symbol; +declare const kGlobalSharedArrayBrand: unique symbol; +declare const kGlobalMutexBrand: unique symbol; +declare const kGlobalConditionBrand: unique symbol; + +declare global { + type ShareablePrimitive = + | string + | number + | boolean + | null + | undefined + | OtherShareablePrimitive[keyof OtherShareablePrimitive] + ; + + type ShareableNonPrimitive = + | SharedStruct + | SharedArray + | Atomics.Mutex + | Atomics.Condition + | OtherShareableNonPrimitive[keyof OtherShareableNonPrimitive] + ; + + type Shareable = + | ShareablePrimitive + | ShareableNonPrimitive + ; + + interface OtherShareablePrimitive {} + interface OtherShareableNonPrimitive {} + + interface SharedStruct { + [kGlobalSharedStructBrand]: any; + } + + type SharedStructType = new () => T; + + interface SharedStructTypeConstructor { + new (fields: readonly (keyof T & string)[]): SharedStructType; + new (fields: readonly string[]): SharedStructType; + isSharedStruct(value: unknown): value is SharedStruct; + } + + const SharedStructType: SharedStructTypeConstructor; + + interface SharedArray { + [kGlobalSharedArrayBrand]: any; + [index: number]: T; + readonly length: number; + } + + interface ReadonlySharedArray extends SharedArray { + readonly [index: number]: T; + } + + type SharedArrayConstructor = new (length: number) => SharedArray; + + const SharedArray: SharedArrayConstructor; + + namespace Atomics { + /** + * An opaque object that represents a shareable mutex. + */ + interface Mutex { + [kGlobalMutexBrand]: any; + } + + interface MutexConstructor { + new (): Mutex; + /** + * Locks the mutex during the synchronous execution of the provided callback. + * @param mutex The mutex object. + * @param cb The callback to execute. + * @returns The result of executing the callback. + */ + lock(mutex: Mutex, cb: () => T): T; + /** + * Tries to lock the mutex during the synchronous execution of the provided callback + * @param mutex The mutex object. + * @param cb The callback to execute. + * @returns `true` if the lock was taken and the callback was executed; otherwise, `false`. + */ + tryLock(mutex: Mutex, cb: () => void): boolean; + } + + /** + * An opaque object that represents a shareable condition variable. + */ + interface Condition { + [kGlobalConditionBrand]: any; + } + + interface ConditionConstructor { + new (): Condition; + /** + * Waits for a period of time for notification signal on a Condition. + * @param condition The condition to wait for. + * @param mutex The mutex to use to wait on. + * @param timeout The period of time to wait. + * @returns `true` if the condition was notified prior to the timeout elapsing; otherwise, `false`. + */ + wait(condition: Condition, mutex: Mutex, timeout?: number): boolean; + /** + * Notify one or more waiters on a condition. + * @param condition The condition to notify. + * @param count The number of waiters to notify (default: all current waiters). + * @returns The number of waiters that were notified. + */ + notify(condition: Condition, count?: number): number; + } + } + + interface Atomics { + readonly Mutex: Atomics.MutexConstructor; + readonly Condition: Atomics.ConditionConstructor; + } +} diff --git a/src/compiler/sharing/structs/taggedStruct.ts b/src/compiler/sharing/structs/taggedStruct.ts new file mode 100644 index 00000000000..1e53482a53c --- /dev/null +++ b/src/compiler/sharing/structs/taggedStruct.ts @@ -0,0 +1,68 @@ +import { hasProperty } from "../../core"; +import { isShareableNonPrimitive } from "./shareable"; +import { Shared } from "./sharedStruct"; + +/** @internal */ +export const enum Tag { + Mutex, + SharedMutex, + Condition, + ManualResetEvent, + CountdownEvent, + Map, + Set, + ResizableArray, + NodeArray, + Node, + Symbol, + FileReference, + AmdDependency, + CheckJsDirective, + CommentDirective, + TextRange, + Diagnostic, + DiagnosticMessageChain, + DiagnosticRelatedInformation, + Pragma, + PragmaArguments, + PragmaSpan, +} + +/** @internal */ +export interface TaggedStruct extends SharedStruct { + readonly __tag__: TTag; +} + +/** @internal */ +export type TaggedStructConstructor = (abstract new (...args: any) => TaggedStruct) & { + readonly __tag__: TTag +}; + +/** @internal */ +export type Tagged SharedStruct, TTag extends Tag> = + (F extends abstract new (...args: infer A) => (infer R extends TaggedStruct) ? (abstract new (...args: A) => Omit) : F) & TaggedStructConstructor + +/** + * Since shared structs do not support instanceof, we often need a way to distingush one shared struct from another to + * create the correct proxy class in our membrane. To accomplish this, we can use the Tagged mixin to inject a + * `__tag__` field that we can use instead of `instanceof`. + * @internal + */ +export function Tagged SharedStruct, TTag extends Tag>(base: F, tag: TTag): Tagged { + @Shared({ abstract: true }) + abstract class TaggedStruct extends base { + static readonly __tag__ = tag; + @Shared() readonly __tag__ = tag; + } + return TaggedStruct as Tagged; +} + +/** @internal */ +export function isTaggedStructObject(value: ShareableNonPrimitive, tag?: TTag): value is TaggedStruct { + return hasProperty(value, "__tag__") && (tag === undefined || (value as TaggedStruct).__tag__ === tag); +} + +/** @internal */ +export function isTaggedStruct(value: unknown, tag?: TTag): value is TaggedStruct { + return isShareableNonPrimitive(value) && isTaggedStructObject(value, tag); +} diff --git a/src/compiler/symbolDisposeShim.ts b/src/compiler/symbolDisposeShim.ts new file mode 100644 index 00000000000..29e9f95b0dd --- /dev/null +++ b/src/compiler/symbolDisposeShim.ts @@ -0,0 +1,12 @@ +/// +export {}; + +// Shim `Symbol.dispose` so that we can can use `using`. +if (!Symbol.dispose) { + try { + Object.defineProperty(Symbol, "dispose", { configurable: true, writable: true, value: Symbol.for("Symbol.dispose") }); + } + catch { + // do nothing + } +} diff --git a/src/compiler/symbolMetadataShim.ts b/src/compiler/symbolMetadataShim.ts new file mode 100644 index 00000000000..040338b14da --- /dev/null +++ b/src/compiler/symbolMetadataShim.ts @@ -0,0 +1,12 @@ +/// +export {}; + +// Inject `Symbol.metadata`, if it is missing, so that we can leverage it from the `@Shared()` decorator. +if (!Symbol.metadata) { + try { + Object.defineProperty(Symbol, "metadata", { configurable: true, writable: true, value: Symbol.for("Symbol.metadata") }); + } + catch { + // do nothing + } +} diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index db12d3a6dd9..1b28b242e89 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -1420,7 +1420,9 @@ export interface System { realpath?(path: string): string; /** @internal */ getEnvironmentVariable(name: string): string; /** @internal */ tryEnableSourceMapsForHost?(): void; + /** @internal */ tryEnableSharedStructs?(): void; /** @internal */ debugMode?: boolean; + /** @internal */ cpuCount?(): number; setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; clearTimeout?(timeoutId: any): void; clearScreen?(): void; @@ -1433,6 +1435,8 @@ export interface System { // For testing /** @internal */ now?(): Date; /** @internal */ storeFilesChangingSignatureDuringEmit?: boolean; + + /** @internal */ stringSeed?: number; } export interface FileWatcher { @@ -1454,7 +1458,8 @@ export let sys: System = (() => { const nativePattern = /^native |^\([^)]+\)$|^(internal[\\/]|[a-zA-Z0-9_\s]+(\.js)?$)/; const _fs: typeof import("fs") = require("fs"); const _path: typeof import("path") = require("path"); - const _os = require("os"); + const _os: typeof import("os") = require("os"); + const _cp: typeof import("child_process") = require("child_process"); // crypto can be absent on reduced node installations let _crypto: typeof import("crypto") | undefined; try { @@ -1506,9 +1511,16 @@ export let sys: System = (() => { inodeWatching: isLinuxOrMacOs, sysLog, }); + + const stringSeedFromEnv = +(process.env.TS_STRING_SEED || NaN); + const stringSeed = isFinite(stringSeedFromEnv) && Math.floor(stringSeedFromEnv) === stringSeedFromEnv ? + stringSeedFromEnv >>> 0 : + Math.floor(Math.random() * 0xffffffff) >>> 0; + const nodeSystem: System = { args: process.argv.slice(2), newLine: _os.EOL, + stringSeed, useCaseSensitiveFileNames, write(s: string): void { process.stdout.write(s); @@ -1587,6 +1599,29 @@ export let sys: System = (() => { // Could not enable source maps. } }, + tryEnableSharedStructs() { + // If `SharedStructType` is available, or if we've already restarted with options, do nothing. + if (typeof SharedStructType === "function" || process.env.TS_RESTARTED_WITH_OPTIONS === "1") { + return; + } + + const execArgv = ["--shared-string-table", "--harmony-struct"]; + // If a call to `node --version` with the provided args results in a non-zero exit status, then + // we cannot restart with the provided options. + if (_cp.spawnSync(process.execPath, [...process.execArgv, ...execArgv, "--version"]).status) { + return; + } + + // Start the process with the provided arguments, exiting with that process's exit code. + process.exit(_cp.spawnSync( + process.execPath, + [...process.execArgv, ...execArgv, ...process.argv.slice(1)], + { stdio: "inherit", env: { ...process.env, TS_RESTARTED_WITH_OPTIONS: "1" } }, + ).status ?? 0); + }, + cpuCount() { + return _os.cpus().length; + }, setTimeout, clearTimeout, clearScreen: () => { @@ -1611,7 +1646,6 @@ export let sys: System = (() => { } } }; - return nodeSystem; /** * `throwIfNoEntry` was added so recently that it's not in the node types. @@ -1983,6 +2017,8 @@ export let sys: System = (() => { hash.update(data); return hash.digest("hex"); } + + return nodeSystem; } let sys: System | undefined; diff --git a/src/compiler/threading/condition.ts b/src/compiler/threading/condition.ts new file mode 100644 index 00000000000..c53e08dfeb3 --- /dev/null +++ b/src/compiler/threading/condition.ts @@ -0,0 +1,110 @@ +import { Debug } from "../debug"; +import { Mutex } from "./mutex"; +import { Shared, SharedStructBase } from "../sharing/structs/sharedStruct"; +import { isTaggedStruct, Tag, Tagged } from "../sharing/structs/taggedStruct"; +import { UniqueLock } from "./uniqueLock"; + +/** @internal */ +@Shared() +export class Condition extends Tagged(SharedStructBase, Tag.Condition) { + @Shared() private _condition = new Atomics.Condition(); + + /** + * Waits until a Condition is signaled via a call to {@link notify|`Condition.notify`}. + * @param self The condition to wait for. + * @param lock A {@link UniqueLock} taken for a {@link Mutex}. + * @param stopWaiting An optional callback that can be used to continue waiting if a necessary condition is not met. + */ + static wait(self: Condition, lock: UniqueLock, stopWaiting?: () => boolean): void { + Condition.waitUntil(self, lock, Infinity, stopWaiting!); + } + + /** + * Waits until a Condition is signaled via a call to {@link notify|`Condition.notify`}, or until a timeout period + * has elapsed. + * @param self The condition to wait for. + * @param lock A {@link UniqueLock} taken for a {@link Mutex}. + * @param timeout The number of milliseconds to wait before returning. + * @returns `"no-timeout"` if the condition was notified before the timeout elapsed, or `"timeout"` to indicate the + * timeout elapsed before the condition was notified. + */ + static waitFor(self: Condition, lock: UniqueLock, timeout: number): "no-timeout" | "timeout"; + /** + * Waits until a Condition is signaled via a call to {@link notify|`Condition.notify`}, or until a timeout period + * has elapsed. + * @param self The condition to wait for. + * @param lock A {@link UniqueLock} taken for a {@link Mutex}. + * @param timeout The number of milliseconds to wait before returning. + * @param stopWaiting An optional callback that can be used to continue waiting if a necessary condition is not met. + * @returns `true` if the condition was notified and the `stopWaiting` callback returned `true`. Otherwise, + * `false` to indicate that the timeout elapsed before the condition was ready. + */ + static waitFor(self: Condition, lock: UniqueLock, timeout: number, stopWaiting: () => boolean): boolean; + static waitFor(self: Condition, lock: UniqueLock, timeout: number, stopWaiting?: () => boolean): "no-timeout" | "timeout" | boolean { + return Condition.waitUntil(self, lock, Date.now() + timeout, stopWaiting!); + } + + /** + * Waits until a Condition is signaled via a call to {@link notify|`Condition.notify`}, or until a specific time has been reached. + * @param self The condition to wait for. + * @param lock A {@link UniqueLock} taken for a {@link Mutex}. + * @param absoluteTimeout The number of milliseconds since the UNIX epoch. + * @returns `"no-timeout"` if the condition was notified before the timeout elapsed, or `"timeout"` to indicate the + * timeout elapsed before the condition was notified. + */ + static waitUntil(self: Condition, lock: UniqueLock, absoluteTimeout: number): "no-timeout" | "timeout"; + /** + * Waits until a Condition is signaled via a call to {@link notify|`Condition.notify`}, or until a specific time has been reached. + * @param self The condition to wait for. + * @param lock A {@link UniqueLock} taken for a {@link Mutex}. + * @param absoluteTimeout The number of milliseconds since the UNIX epoch. + * @param stopWaiting An optional callback that can be used to continue waiting if a necessary condition is not met. + * @returns `true` if the condition was notified and the `stopWaiting` callback returned `true`. Otherwise, + * `false` to indicate that the timeout elapsed before the condition was ready. + */ + static waitUntil(self: Condition, lock: UniqueLock, absoluteTimeout: number, stopWaiting: () => boolean): boolean; + static waitUntil(self: Condition, lock: UniqueLock, absoluteTimeout: number, stopWaiting?: () => boolean): "no-timeout" | "timeout" | boolean { + const mutex = lock.mutex; + Debug.assert(mutex); + Debug.assert(mutex["_locked"]); // eslint-disable-line dot-notation -- declared `private` + const nativeMutex = mutex["_mutex"]; // eslint-disable-line dot-notation -- declared `private` + const nativeCondition = mutex["_condition"]; // eslint-disable-line dot-notation -- declared `private` + return Atomics.Mutex.lock(nativeMutex, () => { + while (!stopWaiting?.()) { + mutex["_locked"] = false; // eslint-disable-line dot-notation -- declared `private` + Atomics.Condition.notify(nativeCondition); + try { + const remainingTimeout = isFinite(absoluteTimeout) ? Date.now() - absoluteTimeout : undefined; + const result = Atomics.Condition.wait(self._condition, nativeMutex, remainingTimeout) ? "no-timeout" : "timeout"; + if (result === "timeout") { + return stopWaiting ? stopWaiting() : result; + } + if (!stopWaiting) { + return "no-timeout"; + } + } + finally { + while (mutex["_locked"]) { // eslint-disable-line dot-notation -- declared `private` + Atomics.Condition.wait(nativeCondition, nativeMutex); + } + mutex["_locked"] = true; // eslint-disable-line dot-notation -- declared `private` + } + } + return true; + }); + } + + /** + * Notify one or more waiters on a condition. + * @param condition The condition to notify. + * @param count The number of waiters to notify (default: all current waiters). + * @returns The number of waiters that were notified. + */ + static notify(self: Condition, count?: number) { + return Atomics.Condition.notify(self._condition, count); + } + + static [Symbol.hasInstance](value: unknown): value is Condition { + return isTaggedStruct(value, Tag.Condition); + } +} diff --git a/src/compiler/threading/countdownEvent.ts b/src/compiler/threading/countdownEvent.ts new file mode 100644 index 00000000000..1026caf2cad --- /dev/null +++ b/src/compiler/threading/countdownEvent.ts @@ -0,0 +1,118 @@ +import { Condition, Debug, isTaggedStruct, Mutex, Shared, SharedStructBase, Tag, Tagged, UniqueLock } from "../_namespaces/ts"; + +const MAX_INT32 = 2 ** 31 - 1; + +/** @internal */ +@Shared() +export class CountdownEvent extends Tagged(SharedStructBase, Tag.CountdownEvent) { + @Shared() private _mutex: Mutex | undefined = new Mutex(); + @Shared() private _condition: Condition | undefined = new Condition(); + @Shared() private _signaled = false; + @Shared() private _initialCount: number; + @Shared() private _remainingCount: number = 0; + + constructor(initialCount = 0) { + super(); + initialCount |= 0; + Debug.assert(initialCount >= 0, "initialCount out of range"); + this._initialCount = initialCount; + this._remainingCount = initialCount; + this._signaled = initialCount === 0; + } + + static initialCount(self: CountdownEvent) { + return self._initialCount; + } + + static remainingCount(self: CountdownEvent) { + return self._remainingCount; + } + + static isSet(self: CountdownEvent) { + return self._remainingCount === 0; + } + + static add(self: CountdownEvent, count?: number) { + Debug.assert(CountdownEvent.tryAdd(self, count), "event is already signaled"); + } + + static tryAdd(self: CountdownEvent, count = 1) { + count |= 0; + Debug.assert(count >= 1, "count out of range"); + Debug.assert(self._mutex, "object disposed"); + using _ = new UniqueLock(self._mutex); + if (self._remainingCount === 0) return false; + Debug.assert(self._remainingCount <= MAX_INT32 - count, "count out of range"); + self._remainingCount += count; + return true; + } + + static reset(self: CountdownEvent, count?: number) { + if (count !== undefined) { + count |= 0; + Debug.assert(count >= 0, "count out of range"); + } + + Debug.assert(self._mutex && self._condition, "object disposed"); + using _ = new UniqueLock(self._mutex); + count ??= self._initialCount; + self._remainingCount = count; + self._initialCount = count; + if (count === 0) { + if (!self._signaled) { + self._signaled = true; + Condition.notify(self._condition); + } + } + else { + self._signaled = false; + } + } + + static signal(self: CountdownEvent, count = 1) { + count |= 0; + Debug.assert(count >= 1, "count out of range"); + + Debug.assert(self._mutex && self._condition, "object disposed"); + using _ = new UniqueLock(self._mutex); + Debug.assert(self._remainingCount >= count, "count out of range"); + self._remainingCount = self._remainingCount - count; + if (self._remainingCount === 0) { + if (!self._signaled) { + self._signaled = true; + Condition.notify(self._condition); + } + return true; + } + return false; + } + + static wait(self: CountdownEvent, timeout?: number) { + if (timeout !== undefined) { + timeout = Math.max(0, timeout | 0); + } + + Debug.assert(self._mutex && self._condition, "object disposed"); + if (CountdownEvent.isSet(self)) { + return true; + } + + using lck = new UniqueLock(self._mutex); + if (timeout !== undefined) { + return Condition.waitFor(self._condition, lck, timeout) !== "timeout"; + } + else { + Condition.wait(self._condition, lck); + return true; + } + } + + static close(self: CountdownEvent): void { + self._mutex = undefined; + self._condition = undefined; + } + + static [Symbol.hasInstance](value: unknown): value is CountdownEvent { + return isTaggedStruct(value, Tag.CountdownEvent); + } +} diff --git a/src/compiler/threading/lockable.ts b/src/compiler/threading/lockable.ts new file mode 100644 index 00000000000..a8151997211 --- /dev/null +++ b/src/compiler/threading/lockable.ts @@ -0,0 +1,10 @@ +/** @internal */ +export interface BasicLockable { + lock(): void; + unlock(): void; +} + +/** @internal */ +export interface Lockable extends BasicLockable { + tryLock(): boolean; +} diff --git a/src/compiler/threading/manualResetEvent.ts b/src/compiler/threading/manualResetEvent.ts new file mode 100644 index 00000000000..4158bb2df1a --- /dev/null +++ b/src/compiler/threading/manualResetEvent.ts @@ -0,0 +1,41 @@ +import { Shared, SharedStructBase } from "../sharing/structs/sharedStruct"; +import { Tag, Tagged } from "../sharing/structs/taggedStruct"; + +/** @internal */ +@Shared() +export class ManualResetEvent extends Tagged(SharedStructBase, Tag.ManualResetEvent) { + @Shared() private mutex = new Atomics.Mutex(); + @Shared() private condition = new Atomics.Condition(); + @Shared() private signaled = false; + + static isSet(self: ManualResetEvent) { + return self.signaled; + } + + static set(self: ManualResetEvent) { + let result = false; + Atomics.Mutex.tryLock(self.mutex, () => { + if (!self.signaled) { + self.signaled = true; + Atomics.Condition.notify(self.condition); + result = true; + } + }); + return result; + } + + static reset(self: ManualResetEvent) { + let result = false; + Atomics.Mutex.tryLock(self.mutex, () => { + if (self.signaled) { + self.signaled = false; + result = true; + } + }); + return result; + } + + static wait(self: ManualResetEvent, timeout?: number) { + return Atomics.Mutex.lock(self.mutex, () => Atomics.Condition.wait(self.condition, self.mutex, timeout)); + } +} \ No newline at end of file diff --git a/src/compiler/threading/mutex.ts b/src/compiler/threading/mutex.ts new file mode 100644 index 00000000000..c39a9b03fbf --- /dev/null +++ b/src/compiler/threading/mutex.ts @@ -0,0 +1,154 @@ +import { hasProperty } from "../core"; +import { Debug } from "../debug"; +import { Identifiable } from "../sharing/structs/identifiableStruct"; +import { Shared, SharedStructBase } from "../sharing/structs/sharedStruct"; +import { Tag, Tagged, TaggedStruct } from "../sharing/structs/taggedStruct"; +import { Lockable } from "./lockable"; + +let tryLock: (self: Mutex, cacheKey?: object) => boolean; +let lock: (self: Mutex, cacheKey?: object) => void; +let unlock: (self: Mutex, cacheKey?: object) => void; + +interface CallbackCache { + tryLock?: () => void; + lock?: () => void; + unlock?: () => void; +} + +/** + * This wraps the experimental `Atomics.Mutex` such that locks can be taken via the RAII-compatible `UniqueLock` and + * `ScopedLock` primitives. + * @internal + */ +@Shared() +export class Mutex extends Identifiable(Tagged(SharedStructBase, Tag.Mutex)) { + @Shared() private _mutex = new Atomics.Mutex(); + @Shared() private _condition = new Atomics.Condition(); + @Shared() private _locked = false; + + static { + const callbackCache = new WeakMap(); + + // we reuse the same lockTaken variable for each call to tryLock in a thread because its not + // reentrant and is reset each time the callback is invoked. + let lockTaken = false; + + function getCache(key: object | undefined) { + if (key) { + let cache = callbackCache.get(key); + if (!cache) callbackCache.set(key, cache = {}); + return cache; + } + } + + function makeTryLockCallback(self: Mutex, cache: CallbackCache | undefined) { + const fn = () => { + if (!self._locked) { + self._locked = true; + lockTaken = true; + } + }; + return cache ? cache.tryLock = fn : fn; + } + + function makeLockCallback(self: Mutex, cache: CallbackCache | undefined) { + const fn = () => { + while (self._locked) { + Atomics.Condition.wait(self._condition, self._mutex); + } + self._locked = true; + }; + return cache ? cache.lock = fn : fn; + } + + function makeUnlockCallback(self: Mutex, cache: CallbackCache | undefined) { + const fn = () => { + self._locked = false; + }; + return cache ? cache.unlock = fn : fn; + } + + tryLock = (self, cacheKey) => { + lockTaken = false; + const cache = getCache(cacheKey); + Atomics.Mutex.tryLock(self._mutex, cache?.tryLock ?? makeTryLockCallback(self, cache)); + return lockTaken; + }; + + lock = (self, cacheKey) => { + const cache = getCache(cacheKey); + Atomics.Mutex.lock(self._mutex, cache?.lock ?? makeLockCallback(self, cache)); + }; + + unlock = (self, cacheKey) => { + const cache = getCache(cacheKey); + Atomics.Mutex.lock(self._mutex, cache?.unlock ?? makeUnlockCallback(self, cache)); + Atomics.Condition.notify(self._condition); + }; + } + + static tryLock(self: Mutex, cb: () => void): boolean { + if (!tryLock(self, /*cacheKey*/ undefined)) { + return false; + } + + try { + cb(); + return true; + } + finally { + unlock(self, /*cacheKey*/ undefined); + } + } + + /** + * Locks the mutex and invokes `cb` in the context of the lock. The mutex will always be unlocked when the method + * returns, regardless as to whether `cb` throws an exception. + */ + static lock(self: Mutex, cb: () => T): T { + lock(self, /*cacheKey*/ undefined); + try { + return cb(); + } + finally { + unlock(self, /*cacheKey*/ undefined); + } + } + + static asLockable(self: Mutex): Lockable { + return new LockableMutex(self); + } + + static [Symbol.hasInstance](value: unknown): value is Mutex { + return typeof value === "object" && value !== null && // eslint-disable-line no-null/no-null -- necessary for comparison + hasProperty(value, "__tag__") && + (value as TaggedStruct).__tag__ === Tag.Mutex; + } +} + +class LockableMutex implements Lockable { + private _mutex: Mutex; + private _ownsLock = false; + + constructor(mutex: Mutex) { + this._mutex = mutex; + } + + tryLock(): boolean { + Debug.assert(!this._ownsLock); + this._ownsLock = tryLock(this._mutex, this); + return this._ownsLock; + } + + lock(): void { + Debug.assert(!this._ownsLock); + lock(this._mutex, this); + this._ownsLock = true; + } + + unlock(): void { + Debug.assert(this._ownsLock); + unlock(this._mutex, this); + this._ownsLock = false; + } +} diff --git a/src/compiler/threading/scopedLock.ts b/src/compiler/threading/scopedLock.ts new file mode 100644 index 00000000000..a0f3efe29ef --- /dev/null +++ b/src/compiler/threading/scopedLock.ts @@ -0,0 +1,108 @@ +import { Debug } from "../debug"; +import "../symbolDisposeShim"; + +import { Lockable } from "./lockable"; +import { Mutex } from "./mutex"; +import { SharedMutex } from "./sharedMutex"; + +/** @internal */ +export class ScopedLock { + private _mutexes: readonly Lockable[] | undefined; + + constructor(mutexes: Iterable) { + const array: Lockable[] = []; + for (const mutex of mutexes) { + array.push( + mutex instanceof Mutex ? Mutex.asLockable(mutex) : + mutex instanceof SharedMutex ? SharedMutex.asLockable(mutex) : + mutex); + } + + let remaining = array.length; + let index = 0; + let error; + let hasError = false; + while (!hasError) { + try { + if (remaining === 0) { + // return all locks taken, in the order they were taken. index should be back at the first lock taken in + // this attempt. + this._mutexes = [...array.slice(index), ...array.slice(0, index)]; + return; + } + + const lockable = array[index]; + if (remaining === array.length) { + // always wait for the first lock + lockable.lock(); + } + else { + if (lockable.tryLock()) { + // this lock was taken. move to the next lock + index = (index + 1) % array.length; + remaining--; + } + else { + // if we fail to take a lock, unlock each lock taken so far so that we start over at the current + // index. + let i = (index + array.length - 1) % array.length; + while (remaining < array.length) { + // always unlock all locks taken, even if one unlock fails for some reason. + try { + array[i].unlock(); + } + catch (e) { + error = error ? createSuppressedError(e, error) : e; + hasError = true; + } + i = (index + array.length - 1) % array.length; + remaining++; + } + } + } + } + catch (e) { + error = error ? createSuppressedError(e, error) : e; + hasError = true; + } + } + + // if we reach this point, an error occurred. unlock all locks taken in reverse order and throw the error. + let i = (index + array.length - 1) % array.length; + while (remaining < array.length) { + // always unlock all locks taken, even if one unlock fails for some reason. + try { + array[i].unlock(); + } + catch (e) { + error = createSuppressedError(e, error); + } + i = (index + array.length - 1) % array.length; + remaining++; + } + + throw error; + } + + [Symbol.dispose]() { + const mutexes = this._mutexes; + if (mutexes) { + this._mutexes = undefined; + for (let i = mutexes.length - 1; i >= 0; i--) { + mutexes[i].unlock(); + } + } + } +} + +function createSuppressedError(error: unknown, suppressed: unknown) { + if (typeof SuppressedError === "function") { + return Debug.captureStackTrace(new SuppressedError(error, suppressed), createSuppressedError); + } + + const e = new Error("An error suppression occurred.") as SuppressedError; + e.error = error; + e.suppressed = suppressed; + e.name = "SuppressedError"; + return Debug.captureStackTrace(e, createSuppressedError); +} diff --git a/src/compiler/threading/sharedLock.ts b/src/compiler/threading/sharedLock.ts new file mode 100644 index 00000000000..df067f3f595 --- /dev/null +++ b/src/compiler/threading/sharedLock.ts @@ -0,0 +1,108 @@ +import "../symbolDisposeShim"; + +import { Debug } from "../debug"; +import { SharedLockable } from "./sharedLockable"; +import { SharedMutex } from "./sharedMutex"; + +/** @internal */ +export class SharedLock { + private _mutex: T | undefined; + private _lockable: SharedLockable | undefined; + private _ownsLock = false; + + constructor(); + constructor(mutex: T, t?: "defer-lock" | "try-to-lock" | "adopt-lock"); + constructor(mutex?: T, t?: "defer-lock" | "try-to-lock" | "adopt-lock") { + this._mutex = mutex; + this._lockable = + mutex instanceof SharedMutex ? SharedMutex.asSharedLockable(mutex) : + mutex; + if (this._lockable) { + switch (t) { + case "defer-lock": + break; + case "try-to-lock": + this._ownsLock = this._lockable.tryLockShared(); + break; + case "adopt-lock": + this._ownsLock = true; + break; + case undefined: + this._lockable.lockShared(); + this._ownsLock = true; + break; + } + } + } + + get mutex() { + return this._mutex; + } + + get ownsLock() { + return this._ownsLock; + } + + tryLock(): boolean { + Debug.assert(this._lockable); + Debug.assert(!this._ownsLock); + this._ownsLock = this._lockable.tryLockShared(); + return this._ownsLock; + } + + lock(): void { + Debug.assert(this._lockable); + Debug.assert(!this._ownsLock); + this._lockable.lockShared(); + this._ownsLock = true; + } + + unlock() { + Debug.assert(this._lockable); + Debug.assert(this._ownsLock); + this._ownsLock = false; + this._lockable.unlockShared(); + } + + release() { + this._mutex = undefined; + this._lockable = undefined; + this._ownsLock = false; + } + + swap(other: SharedLock) { + const mutex = other._mutex; + other._mutex = this._mutex; + const lockable = other._lockable; + other._lockable = this._lockable; + const ownsLock = other._ownsLock; + other._ownsLock = this._ownsLock; + this._mutex = mutex; + this._lockable = lockable; + this._ownsLock = ownsLock; + } + + move() { + const other = new SharedLock(); + other._mutex = this._mutex; + other._lockable = this._lockable; + other._ownsLock = this._ownsLock; + this._mutex = undefined; + this._lockable = undefined; + this._ownsLock = false; + return other; + } + + [Symbol.dispose]() { + const lockable = this._lockable; + if (lockable) { + const ownsLock = this._ownsLock; + this._mutex = undefined; + this._lockable = undefined; + this._ownsLock = false; + if (ownsLock) { + lockable.unlockShared(); + } + } + } +} diff --git a/src/compiler/threading/sharedLockable.ts b/src/compiler/threading/sharedLockable.ts new file mode 100644 index 00000000000..437053772f2 --- /dev/null +++ b/src/compiler/threading/sharedLockable.ts @@ -0,0 +1,6 @@ +/** @internal */ +export interface SharedLockable { + tryLockShared(): boolean; + lockShared(): void; + unlockShared(): void; +} diff --git a/src/compiler/threading/sharedMutex.ts b/src/compiler/threading/sharedMutex.ts new file mode 100644 index 00000000000..03c21a8a43f --- /dev/null +++ b/src/compiler/threading/sharedMutex.ts @@ -0,0 +1,277 @@ +import "../symbolDisposeShim"; + +import { hasProperty } from "../core"; +import { Debug } from "../debug"; +import { Identifiable } from "../sharing/structs/identifiableStruct"; +import { Shared, SharedStructBase } from "../sharing/structs/sharedStruct"; +import { Tag, Tagged, TaggedStruct } from "../sharing/structs/taggedStruct"; +import { Lockable } from "./lockable"; +import { SharedLockable } from "./sharedLockable"; + +const EXCLUSIVE = 1 << 31 >>> 0; +const SHARED = ~EXCLUSIVE >>> 0; + +let tryLock: (self: SharedMutex, cacheKey: object | undefined) => boolean; +let lock: (self: SharedMutex, cacheKey: object | undefined) => void; +let unlock: (self: SharedMutex, cacheKey: object | undefined) => void; +let tryLockShared: (self: SharedMutex, cacheKey: object | undefined) => boolean; +let lockShared: (self: SharedMutex, cacheKey: object | undefined) => void; +let unlockShared: (self: SharedMutex, cacheKey: object | undefined) => void; + +interface CallbackCache { + tryLock?: () => void; + lock?: () => void; + unlock?: () => void; + tryLockShared?: () => void; + lockShared?: () => void; + unlockShared?: () => void; +} + +/** @internal */ +@Shared() +export class SharedMutex extends Identifiable(Tagged(SharedStructBase, Tag.SharedMutex)) { + @Shared() private _mutex = new Atomics.Mutex(); + @Shared() private _gate1 = new Atomics.Condition(); + @Shared() private _gate2 = new Atomics.Condition(); + @Shared() private _state = 0; + + static { + const callbackCache = new WeakMap(); + + // we reuse the same lockTaken variable for each call to tryLock/tryShardLock in a thread because its not + // reentrant and is reset each time the callback is invoked. + let lockTaken = false; + + function getCache(key: object | undefined) { + if (key) { + let cache = callbackCache.get(key); + if (!cache) callbackCache.set(key, cache = {}); + return cache; + } + } + + function makeTryLockCallback(self: SharedMutex, cache: CallbackCache | undefined) { + const fn = () => { + if (self._state === 0) { + self._state |= EXCLUSIVE; + lockTaken = true; + } + }; + return cache ? cache.tryLock = fn : fn; + } + + function makeLockCallback(self: SharedMutex, cache: CallbackCache | undefined) { + const fn = () => { + // take exclusive lock + while (self._state & EXCLUSIVE) { + Atomics.Condition.wait(self._gate1, self._mutex); + } + + self._state |= EXCLUSIVE; + + // wait for readers to drain + while (self._state & SHARED) { + Atomics.Condition.wait(self._gate2, self._mutex); + } + }; + return cache ? cache.lock = fn : fn; + } + + function makeUnlockCallback(self: SharedMutex, cache: CallbackCache | undefined) { + const fn = () => { + // release exclusive lock + self._state = 0; + }; + return cache ? cache.unlock = fn : fn; + } + + function makeTryLockSharedCallback(self: SharedMutex, cache: CallbackCache | undefined) { + const fn = () => { + if (self._state === 0) { + self._state |= EXCLUSIVE; + lockTaken = true; + } + }; + return cache ? cache.tryLockShared = fn : fn; + } + + function makeLockSharedCallback(self: SharedMutex, cache: CallbackCache | undefined) { + const fn = () => { + while ((self._state & EXCLUSIVE) || ((self._state & SHARED) >>> 0) === SHARED) { + Atomics.Condition.wait(self._gate1, self._mutex); + } + const readerCount = ((self._state & SHARED) >>> 0) + 1; + self._state = (readerCount | (self._state & EXCLUSIVE)) >>> 0; + }; + return cache ? cache.lockShared = fn : fn; + } + + function makeUnlockSharedCallback(self: SharedMutex, cache: CallbackCache | undefined) { + const fn = () => { + const readerCount = ((self._state & SHARED) >>> 0) - 1; + self._state = (readerCount | (self._state & EXCLUSIVE)) >>> 0; + if (self._state & EXCLUSIVE) { + if (readerCount === 0) { + Atomics.Condition.notify(self._gate2, 1); + } + } + else { + if (readerCount === SHARED - 1) { + Atomics.Condition.notify(self._gate1, 1); + } + } + }; + return cache ? cache.unlockShared = fn : fn; + } + + tryLock = (self, cacheKey) => { + lockTaken = false; + const cache = getCache(cacheKey); + Atomics.Mutex.tryLock(self._mutex, cache?.tryLock ?? makeTryLockCallback(self, cache)); + return lockTaken; + }; + + lock = (self, cacheKey) => { + const cache = getCache(cacheKey); + Atomics.Mutex.lock(self._mutex, cache?.lock ?? makeLockCallback(self, cache)); + }; + + unlock = (self, cacheKey) => { + const cache = getCache(cacheKey); + Atomics.Mutex.lock(self._mutex, cache?.unlock ?? makeUnlockCallback(self, cache)); + Atomics.Condition.notify(self._gate1); + }; + + tryLockShared = (self, cacheKey) => { + lockTaken = false; + const cache = getCache(cacheKey); + Atomics.Mutex.tryLock(self._mutex, cache?.tryLockShared ?? makeTryLockSharedCallback(self, cache)); + return lockTaken; + }; + + lockShared = (self, cacheKey) => { + const cache = getCache(cacheKey); + Atomics.Mutex.lock(self._mutex, cache?.lockShared ?? makeLockSharedCallback(self, cache)); + }; + + unlockShared = (self, cacheKey) => { + const cache = getCache(cacheKey); + Atomics.Mutex.lock(self._mutex, cache?.unlockShared ?? makeUnlockCallback(self, cache)); + }; + } + + static tryLock(self: SharedMutex, cb: () => void): boolean { + if (!tryLock(self, /*cacheKey*/ undefined)) { + return false; + } + + try { + cb(); + return true; + } + finally { + unlock(self, /*cacheKey*/ undefined); + } + } + + static lock(self: SharedMutex, cb: () => T): T { + lock(self, /*cacheKey*/ undefined); + try { + return cb(); + } + finally { + unlock(self, /*cacheKey*/ undefined); + } + } + + static tryLockShared(self: SharedMutex, cb: () => void): boolean { + if (!tryLockShared(self, /*cacheKey*/ undefined)) { + return false; + } + try { + cb(); + return true; + } + finally { + unlockShared(self, /*cacheKey*/ undefined); + } + } + + static lockShared(self: SharedMutex, cb: () => T): T { + lockShared(self, /*cacheKey*/ undefined); + try { + return cb(); + } + finally { + unlockShared(self, /*cacheKey*/ undefined); + } + } + + static asLockable(self: SharedMutex): Lockable { + return new LockableSharedMutex(self); + } + + static asSharedLockable(self: SharedMutex): SharedLockable { + return new SharedLockableSharedMutex(self); + } + + static [Symbol.hasInstance](value: unknown): value is SharedMutex { + return typeof value === "object" && value !== null && // eslint-disable-line no-null/no-null -- necessary for comparision + hasProperty(value, "__tag__") && + (value as TaggedStruct).__tag__ === Tag.SharedMutex; + } +} + +class LockableSharedMutex implements Lockable { + private _mutex: SharedMutex; + private _ownsLock = false; + + constructor(mutex: SharedMutex) { + this._mutex = mutex; + } + + tryLock(): boolean { + Debug.assert(!this._ownsLock); + this._ownsLock = tryLock(this._mutex, this); + return this._ownsLock; + } + + lock(): void { + Debug.assert(!this._ownsLock); + lock(this._mutex, this); + this._ownsLock = true; + } + + unlock(): void { + Debug.assert(this._ownsLock); + unlock(this._mutex, this); + this._ownsLock = false; + } +} + +class SharedLockableSharedMutex implements SharedLockable { + private _mutex: SharedMutex; + private _ownsLock = false; + + constructor(mutex: SharedMutex) { + this._mutex = mutex; + } + + tryLockShared(): boolean { + Debug.assert(!this._ownsLock); + this._ownsLock = tryLockShared(this._mutex, this); + return this._ownsLock; + } + + lockShared(): void { + Debug.assert(!this._ownsLock); + lockShared(this._mutex, this); + this._ownsLock = true; + } + + unlockShared(): void { + Debug.assert(this._ownsLock); + unlockShared(this._mutex, this); + this._ownsLock = false; + } +} diff --git a/src/compiler/threading/threadPool.ts b/src/compiler/threading/threadPool.ts new file mode 100644 index 00000000000..8a8eafe7a84 --- /dev/null +++ b/src/compiler/threading/threadPool.ts @@ -0,0 +1,231 @@ +import { WorkerThreadsHost, Worker, workerThreads } from "../workerThreads"; +import { SharedLinkedList } from "../sharing/collections/sharedLinkedList"; +import { Shared, SharedStructBase } from "../sharing/structs/sharedStruct"; +import { Mutex } from "./mutex"; +import { Condition } from "./condition"; +import { UniqueLock } from "./uniqueLock"; +import { Debug } from "../debug"; +import { isNodeLikeSystem } from "../core"; +import { CountdownEvent } from "./countdownEvent"; + +@Shared() +class ThreadPoolWorkItem extends SharedStructBase { + @Shared() readonly name: string; + @Shared() readonly arg: Shareable; + + constructor(name: string, arg?: Shareable) { + super(); + this.name = name; + this.arg = arg; + } +} + +/** @internal */ +@Shared() +export class ThreadPoolState extends SharedStructBase { + @Shared() mutex = new Mutex(); + @Shared() condition = new Condition(); + @Shared() countdown = new CountdownEvent(1); + @Shared() queue = new SharedLinkedList(); + @Shared() active = 0; + @Shared() done = false; + @Shared() error: string | undefined; +} + +/** + * Creates a thread pool of one or more worker threads. A {@link ThreadPool} can only be created on the main thread. + * @internal + */ +export class ThreadPool { + readonly poolSize: number; + + private readonly _host: WorkerThreadsHost; + private _workers: Worker[] = []; + private _state = new ThreadPoolState(); + + private _listening = false; + private _onUncaughtException = () => { + if (!this._state.done) { + this.abort(); + } + }; + + constructor(poolSize: number, host = workerThreads ?? Debug.fail("Worker threads not available.")) { + Debug.assert(poolSize >= 1); + Debug.assert(host.isMainThread(), "A new thread pool can only be created on the main thread."); + this.poolSize = poolSize; + this._host = host; + } + + /** + * Starts all threads in the thread pool. + */ + start(): void { + if (this._workers.length < this.poolSize) { + this._startListening(); + } + for (let i = this._workers.length; i < this.poolSize; i++) { + this._workers.push(this._host.createWorker({ + name: "ThreadPool Thread", + workerData: { type: "ThreadPoolThread", state: this._state } + })); + } + } + + /** + * Disable the addition of new work items in the thread pool and wait for threads to terminate gracefully. + */ + stop(timeout?: number) { + this._shutdown(); + return CountdownEvent.wait(this._state.countdown, timeout); + } + + /** + * Immediately stop all threads in the thread pool and wait for them to terminate. + */ + async abort() { + this._shutdown(); + const workers = this._workers.splice(0, this._workers.length); + await Promise.all(workers.map(worker => worker.terminate())); + } + + /** + * Queues a work item to execute in a worker thread. + * @param name The name of the work item to execute. + * @param arg An argument passed to the work item that can be used to communicate and coordinate with the background + * thread. + */ + queueWorkItem(name: string, arg?: Shareable) { + { + using _ = new UniqueLock(this._state.mutex); + SharedLinkedList.push(this._state.queue, new ThreadPoolWorkItem(name, arg)); + } + + Condition.notify(this._state.condition, 1); + } + + private _shutdown() { + this._stopListening(); + + Debug.log.trace("Shutting down thread pool"); + if (!this._state.done) { + using _ = new UniqueLock(this._state.mutex); + this._state.done = true; + Condition.notify(this._state.condition); + CountdownEvent.signal(this._state.countdown, 1); + } + } + + private _startListening() { + if (isNodeLikeSystem()) { + if (this._listening) { + return; + } + this._listening = true; + process.on("uncaughtExceptionMonitor", this._onUncaughtException); + } + } + + private _stopListening() { + if (isNodeLikeSystem()) { + if (!this._listening) { + return; + } + process.off("uncaughtExceptionMonitor", this._onUncaughtException); + } + } +} + +/** + * Represents a thread in a {@link ThreadPool} and can be used to process work items. A {@link ThreadPoolThread} should + * only be used in a worker thread. + * @internal + */ +export class ThreadPoolThread { + private _state: ThreadPoolState; + private _processWorkItem: (name: string, arg: Shareable) => void; + + constructor(state: ThreadPoolState, processWorkItem: (name: string, arg: Shareable) => void) { + this._state = state; + this._processWorkItem = processWorkItem; + } + + /** + * Runs the thread pool thread until the {@link ThreadPool} signals the thread should shut down. + */ + run() { + let running = true; + let started = false; + try { + const processWorkItem = this._processWorkItem; + if (!CountdownEvent.tryAdd(this._state.countdown, 1)) { + Debug.log.trace("thread pool is already shut down"); + return; + } + + while (running) { + let workItem: ThreadPoolWorkItem | undefined; + { + using lck = new UniqueLock(this._state.mutex); + + // decrement the active thread counter before we start waiting for work + if (started) { + this._state.active--; + } + + // wait until we have work to do + Condition.wait(this._state.condition, lck, () => this._state.queue.size > 0 || this._state.done); + + // stop the thread if the thread pool is closed + if (this._state.done) { + if (started) { + this._state.active--; + } + running = false; + break; + } + + // increment the active thread counter before we start processing a work item + this._state.active++; + started = true; + workItem = SharedLinkedList.shift(this._state.queue); + } + + // process the workitem + if (workItem) { + try { + processWorkItem(workItem.name, workItem.arg); + } + catch (e) { + Debug.log.trace(e); + running = false; + using _ = new UniqueLock(this._state.mutex); + this._state.active--; + break; + } + } + } + } + catch (e) { + Debug.log.trace(e); + } + + // Debug.log.trace(`shutting down.`); + CountdownEvent.signal(this._state.countdown); + } + + /** + * Queue additional work to be performed in another thread. + */ + queueWorkItem(name: string, arg?: Shareable) { + Debug.assert(!this._state.done); + + { + using _ = new UniqueLock(this._state.mutex); + Debug.assert(!this._state.done); + SharedLinkedList.push(this._state.queue, new ThreadPoolWorkItem(name, arg)); + } + + Condition.notify(this._state.condition, 1); + } +} diff --git a/src/compiler/threading/uniqueLock.ts b/src/compiler/threading/uniqueLock.ts new file mode 100644 index 00000000000..3f95db5f4ab --- /dev/null +++ b/src/compiler/threading/uniqueLock.ts @@ -0,0 +1,118 @@ +import "../symbolDisposeShim"; + +import { hasProperty } from "../core"; +import { Debug } from "../debug"; +import { BasicLockable, Lockable } from "./lockable"; +import { Mutex } from "./mutex"; +import { SharedMutex } from "./sharedMutex"; + +function isLockable(x: BasicLockable): x is Lockable { + return x instanceof UniqueLock ? !!x.mutex && isLockable(x.mutex) : hasProperty(x, "tryLock") && typeof (x as Lockable).tryLock === "function"; +} + +/** @internal */ +export class UniqueLock { + private _mutex: T | undefined; + private _lockable: BasicLockable | undefined; + private _ownsLock = false; + + constructor(mutex?: T); + constructor(mutex: Extract, t?: "defer-lock" | "try-to-lock" | "adopt-lock"); + constructor(mutex: T, t?: "defer-lock" | "adopt-lock"); + constructor(mutex?: T, t?: "defer-lock" | "try-to-lock" | "adopt-lock") { + this._mutex = mutex; + this._lockable = + mutex instanceof Mutex ? Mutex.asLockable(mutex) : + mutex instanceof SharedMutex ? SharedMutex.asLockable(mutex) : + mutex; + if (this._lockable) { + switch (t) { + case "defer-lock": + break; + case "try-to-lock": + Debug.assert(isLockable(this._lockable)); + this._ownsLock = this._lockable.tryLock(); + break; + case "adopt-lock": + this._ownsLock = true; + break; + case undefined: + this._lockable.lock(); + this._ownsLock = true; + break; + } + } + } + + get mutex() { + return this._mutex; + } + + get ownsLock() { + return this._ownsLock; + } + + tryLock(this: UniqueLock>): boolean { + Debug.assert(this._lockable); + Debug.assert(!this._ownsLock); + Debug.assert(isLockable(this._lockable)); + this._ownsLock = this._lockable.tryLock(); + return this._ownsLock; + } + + lock(): void { + Debug.assert(this._lockable); + Debug.assert(!this._ownsLock); + this._lockable.lock(); + this._ownsLock = true; + } + + unlock() { + Debug.assert(this._lockable); + Debug.assert(this._ownsLock); + this._ownsLock = false; + this._lockable.unlock(); + } + + release() { + this._mutex = undefined; + this._lockable = undefined; + this._ownsLock = false; + } + + swap(other: UniqueLock) { + const mutex = other._mutex; + const lockable = other._lockable; + const ownsLock = other._ownsLock; + other._mutex = this._mutex; + other._lockable = this._lockable; + other._ownsLock = this._ownsLock; + this._mutex = mutex; + this._lockable = lockable; + this._ownsLock = ownsLock; + } + + move() { + const other = new UniqueLock(); + other._mutex = this._mutex; + other._lockable = this._lockable; + other._ownsLock = this._ownsLock; + this._mutex = undefined; + this._lockable = undefined; + this._ownsLock = false; + return other; + } + + [Symbol.dispose]() { + const lockable = this._lockable; + if (lockable) { + const ownsLock = this._ownsLock; + this._mutex = undefined; + this._lockable = undefined; + this._ownsLock = false; + if (ownsLock) { + lockable.unlock(); + } + } + } +} diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 3bc998eb7dc..07a1f205bd9 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -52,7 +52,6 @@ import { ForegroundColorEscapeSequences, formatColorAndReset, getAllProjectOutputs, - getBuildInfo as ts_getBuildInfo, getBuildInfoFileVersionMap, getConfigFileParsingDiagnostics, getDirectoryPath, @@ -61,7 +60,6 @@ import { getFilesInErrorForSummary, getFirstProjectOutput, getLocaleTimeString, - getModifiedTime as ts_getModifiedTime, getNormalizedAbsolutePath, getParsedCommandLineOfConfigFile, getPendingEmitKind, @@ -111,6 +109,9 @@ import { Status, sys, System, + ThreadPool, + getBuildInfo as ts_getBuildInfo, + getModifiedTime as ts_getModifiedTime, toPath as ts_toPath, TypeReferenceDirectiveResolutionCache, unorderedRemoveItem, @@ -126,6 +127,7 @@ import { WatchStatusReporter, WatchType, WildcardDirectoryWatcher, + WorkerThreadsHost, writeFile, WriteFileCallback, } from "./_namespaces/ts"; @@ -155,6 +157,7 @@ export interface BuildOptions { emitDeclarationOnly?: boolean; sourceMap?: boolean; inlineSourceMap?: boolean; + maxCpuCount?: number; traceResolution?: boolean; /** @internal */ diagnostics?: boolean; @@ -302,8 +305,8 @@ export function createBuilderStatusReporter(system: System, pretty?: boolean): D }; } -function createSolutionBuilderHostBase(system: System, createProgram: CreateProgram | undefined, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter) { - const host = createProgramHost(system, createProgram) as SolutionBuilderHostBase; +function createSolutionBuilderHostBase(system: System, createProgram: CreateProgram | undefined, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, workerThreads?: WorkerThreadsHost, threadPool?: ThreadPool) { + const host = createProgramHost(system, createProgram, workerThreads, threadPool) as SolutionBuilderHostBase; host.getModifiedTime = system.getModifiedTime ? path => system.getModifiedTime!(path) : returnUndefined; host.setModifiedTime = system.setModifiedTime ? (path, date) => system.setModifiedTime!(path, date) : noop; host.deleteFile = system.deleteFile ? path => system.deleteFile!(path) : noop; @@ -313,14 +316,20 @@ function createSolutionBuilderHostBase(system: System, return host; } -export function createSolutionBuilderHost(system = sys, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportErrorSummary?: ReportEmitErrorSummary) { - const host = createSolutionBuilderHostBase(system, createProgram, reportDiagnostic, reportSolutionBuilderStatus) as SolutionBuilderHost; +export function createSolutionBuilderHost(system?: System, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportErrorSummary?: ReportEmitErrorSummary): SolutionBuilderHost; +/** @internal */ +export function createSolutionBuilderHost(system?: System, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportErrorSummary?: ReportEmitErrorSummary, workerThreads?: WorkerThreadsHost, threadPool?: ThreadPool): SolutionBuilderHost; +export function createSolutionBuilderHost(system = sys, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportErrorSummary?: ReportEmitErrorSummary, workerThreads?: WorkerThreadsHost, threadPool?: ThreadPool) { + const host = createSolutionBuilderHostBase(system, createProgram, reportDiagnostic, reportSolutionBuilderStatus, workerThreads, threadPool) as SolutionBuilderHost; host.reportErrorSummary = reportErrorSummary; return host; } -export function createSolutionBuilderWithWatchHost(system = sys, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter) { - const host = createSolutionBuilderHostBase(system, createProgram, reportDiagnostic, reportSolutionBuilderStatus) as SolutionBuilderWithWatchHost; +export function createSolutionBuilderWithWatchHost(system?: System, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): SolutionBuilderWithWatchHost; +/** @internal */ +export function createSolutionBuilderWithWatchHost(system?: System, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, workerThreads?: WorkerThreadsHost, threadPool?: ThreadPool): SolutionBuilderWithWatchHost; +export function createSolutionBuilderWithWatchHost(system = sys, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, workerThreads?: WorkerThreadsHost, threadPool?: ThreadPool) { + const host = createSolutionBuilderHostBase(system, createProgram, reportDiagnostic, reportSolutionBuilderStatus, workerThreads, threadPool) as SolutionBuilderWithWatchHost; const watchHost = createWatchHost(system, reportWatchStatus); copyProperties(host, watchHost); return host; diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index 6b8d0464146..31eb10b026d 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "../tsconfig-base", "compilerOptions": { - "types": ["node"] + "types": ["node"], + "experimentalDecorators": true }, "references": [ diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5432adf33eb..df3e00d76e1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -17,7 +17,11 @@ import { ProgramBuildInfo, SymlinkCache, ThisContainer, + ThreadPool, + WorkerThreadsHost, } from "./_namespaces/ts"; +import { SharedNodeBase } from "./sharing/sharedNode"; +import { SharedParserState } from "./sharing/sharedParserState"; // branded string type used to store absolute, normalized and canonicalized paths // arbitrary file name can be converted to Path via toPath function @@ -927,6 +931,7 @@ export interface Node extends ReadonlyTextRange { // `locals` and `nextContainer` have been moved to `LocalsContainer` // `flowNode` has been moved to `FlowContainer` // see: https://github.com/microsoft/TypeScript/pull/51682 + /** @internal */ __shared__?: SharedNodeBase; } export interface JSDocContainer extends Node { @@ -935,7 +940,7 @@ export interface JSDocContainer extends Node { } /** @internal */ -export interface JSDocArray extends Array { +export interface JSDocArray extends ReadonlyArray { jsDocCache?: readonly JSDocTag[]; // Cache for getJSDocTags } @@ -4364,7 +4369,7 @@ export interface SourceFile extends Declaration, LocalsContainer { /** @internal */ lineMap: readonly number[]; /** @internal */ classifiableNames?: ReadonlySet<__String>; // Comments containing @ts-* directives, in order. - /** @internal */ commentDirectives?: CommentDirective[]; + /** @internal */ commentDirectives?: readonly CommentDirective[]; // Stores a mapping 'external module reference text' -> 'resolved file name' | undefined // It is used to resolve module names in the checker. // Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead @@ -4385,6 +4390,8 @@ export interface SourceFile extends Declaration, LocalsContainer { /** @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit; /** @internal */ endFlowNode?: FlowNode; + + /** @internal */ __sharedCache__?: ReadonlyMap; } /** @internal */ @@ -4959,6 +4966,9 @@ export interface TypeCheckerHost extends ModuleSpecifierResolutionHost { typesPackageExists(packageName: string): boolean; packageBundlesTypes(packageName: string): boolean; + + workerThreads: WorkerThreadsHost | undefined; + threadPool: ThreadPool | undefined; } export interface TypeChecker { @@ -7147,6 +7157,7 @@ export interface CompilerOptions { /** @internal */listFilesOnly?: boolean; locale?: string; mapRoot?: string; + maxCpuCount?: number; maxNodeModuleJsDepth?: number; module?: ModuleKind; moduleResolution?: ModuleResolutionKind; @@ -7426,6 +7437,7 @@ export interface CommandLineOptionOfStringType extends CommandLineOptionBase { export interface CommandLineOptionOfNumberType extends CommandLineOptionBase { type: "number"; defaultValueDescription: number | undefined | DiagnosticMessage; + defaultIfValueMissing?: () => number | undefined; } /** @internal */ @@ -7760,6 +7772,7 @@ export type HasInvalidatedLibResolutions = (libFileName: string) => boolean; export type HasChangedAutomaticTypeDirectiveNames = () => boolean; export interface CompilerHost extends ModuleResolutionHost { + /** @internal */ requestSourceFile?(parserState: SharedParserState, fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, shouldCreateNewSourceFile?: boolean, setFileVersion?: boolean): void; getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; getSourceFileByPath?(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; getCancellationToken?(): CancellationToken; @@ -7836,6 +7849,8 @@ export interface CompilerHost extends ModuleResolutionHost { // For testing: /** @internal */ storeFilesChangingSignatureDuringEmit?: boolean; /** @internal */ getBuildInfo?(fileName: string, configFilePath: string | undefined): BuildInfo | undefined; + /** @internal */ workerThreads?: WorkerThreadsHost | undefined; + /** @internal */ threadPool?: ThreadPool | undefined; } /** true if --out otherwise source file name * @@ -8764,7 +8779,8 @@ export interface NodeFactory { createJsxText(text: string, containsOnlyTriviaWhiteSpaces?: boolean): JsxText; updateJsxText(node: JsxText, text: string, containsOnlyTriviaWhiteSpaces?: boolean): JsxText; createJsxOpeningFragment(): JsxOpeningFragment; - createJsxJsxClosingFragment(): JsxClosingFragment; + /** @deprecated Use `createJsxClosingFragment` */ createJsxJsxClosingFragment(): JsxClosingFragment; + createJsxClosingFragment(): JsxClosingFragment; updateJsxFragment(node: JsxFragment, openingFragment: JsxOpeningFragment, children: readonly JsxChild[], closingFragment: JsxClosingFragment): JsxFragment; createJsxAttribute(name: JsxAttributeName, initializer: JsxAttributeValue | undefined): JsxAttribute; updateJsxAttribute(node: JsxAttribute, name: JsxAttributeName, initializer: JsxAttributeValue | undefined): JsxAttribute; @@ -10007,3 +10023,9 @@ export interface Queue { dequeue(): T; isEmpty(): boolean; } + +/** @internal */ +export type Constructor = new (...args: A) => T; + +/** @internal */ +export type AbstractConstructor = abstract new (...args: A) => T; \ No newline at end of file diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 638d76f79b5..b840370ed37 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -34,6 +34,7 @@ import { BindingElement, BindingElementOfBareOrAccessedRequire, Block, + BuildOptions, BundleFileSection, BundleFileSectionKind, BundleFileTextLike, @@ -400,6 +401,7 @@ import { ModuleResolutionKind, moduleResolutionOptionDeclarations, MultiMap, + MutableNodeArray, NamedDeclaration, NamedExports, NamedImports, @@ -501,7 +503,7 @@ import { SuperExpression, SuperProperty, SwitchStatement, - Symbol, + type Symbol, SymbolFlags, SymbolTable, SyntaxKind, @@ -562,6 +564,8 @@ import { WriteFileCallbackData, YieldExpression, } from "./_namespaces/ts"; +import { SharedModifierLike } from "./sharing/sharedNode"; +import { SharedNodeArray } from "./sharing/sharedNodeArray"; /** @internal */ export const resolvingEmptyArray: never[] = []; @@ -1131,7 +1135,7 @@ export function isPinnedComment(text: string, start: number) { } /** @internal */ -export function createCommentDirectivesMap(sourceFile: SourceFile, commentDirectives: CommentDirective[]): CommentDirectivesMap { +export function createCommentDirectivesMap(sourceFile: SourceFile, commentDirectives: readonly CommentDirective[]): CommentDirectivesMap { const directivesByLine = new Map( commentDirectives.map(commentDirective => ([ `${getLineAndCharacterOfPosition(sourceFile, commentDirective.range.end).line}`, @@ -6945,7 +6949,11 @@ export function getSyntacticModifierFlagsNoCache(node: Node): ModifierFlags { } /** @internal */ -export function modifiersToFlags(modifiers: readonly ModifierLike[] | undefined) { +export function modifiersToFlags(modifiers: Iterable | SharedNodeArray | undefined) { + if (modifiers instanceof SharedNodeArray) { + modifiers = SharedNodeArray.values(modifiers); + } + let flags = ModifierFlags.None; if (modifiers) { for (const modifier of modifiers) { @@ -7197,41 +7205,91 @@ function isExportDefaultSymbol(symbol: Symbol): boolean { export function tryExtractTSExtension(fileName: string): string | undefined { return find(supportedTSExtensionsForExtractExtension, extension => fileExtensionIs(fileName, extension)); } -/** - * Replace each instance of non-ascii characters by one, two, three, or four escape sequences - * representing the UTF-8 encoding of the character, and return the expanded char code list. - */ -function getExpandedCharCodes(input: string): number[] { - const output: number[] = []; - const length = input.length; +function writeByte(output: number[] | Uint8Array | undefined, index: number, value: number) { + if (output) { + output[index] = value; + } +} + +/** + * NOTE: Use `utf8encodeInto` instead. This function is a fallback implementation and is only exposed for testing + * purposes. + * @internal + */ +export function utf8encodeIntoCompat(input: string, output: number[] | Uint8Array | undefined) { + const length = input.length; + let written = 0; for (let i = 0; i < length; i++) { - const charCode = input.charCodeAt(i); + let charCode = input.charCodeAt(i); + + // decode UCS2 surrogate pairs + if (/*is leading surrogate*/ (charCode & 0xd800) === 0xd800 && + /*is not last character*/ i < length - 1) { + const charCode2 = input.charCodeAt(i + 1); + if (/*is trailing surrogate*/ (charCode2 & 0xdc00) === 0xdc00) { + charCode = ((charCode & 0x3ff) << 10) + (charCode2 & 0x3ff) + 0x10000; + i++; + } + } // handle utf8 if (charCode < 0x80) { - output.push(charCode); + writeByte(output, written++, charCode); } else if (charCode < 0x800) { - output.push((charCode >> 6) | 0B11000000); - output.push((charCode & 0B00111111) | 0B10000000); + writeByte(output, written++, (charCode >> 6) | 0B11000000); + writeByte(output, written++, (charCode & 0B00111111) | 0B10000000); } else if (charCode < 0x10000) { - output.push((charCode >> 12) | 0B11100000); - output.push(((charCode >> 6) & 0B00111111) | 0B10000000); - output.push((charCode & 0B00111111) | 0B10000000); + writeByte(output, written++, (charCode >> 12) | 0B11100000); + writeByte(output, written++, ((charCode >> 6) & 0B00111111) | 0B10000000); + writeByte(output, written++, (charCode & 0B00111111) | 0B10000000); } else if (charCode < 0x20000) { - output.push((charCode >> 18) | 0B11110000); - output.push(((charCode >> 12) & 0B00111111) | 0B10000000); - output.push(((charCode >> 6) & 0B00111111) | 0B10000000); - output.push((charCode & 0B00111111) | 0B10000000); + writeByte(output, written++, (charCode >> 18) | 0B11110000); + writeByte(output, written++, ((charCode >> 12) & 0B00111111) | 0B10000000); + writeByte(output, written++, ((charCode >> 6) & 0B00111111) | 0B10000000); + writeByte(output, written++, (charCode & 0B00111111) | 0B10000000); } else { Debug.assert(false, "Unexpected code point"); } } + return written; +} + +/** + * Encodes a string into UTF-8 bytes. + * If `output` is a `Uint8Array`, it must have enough capacity to hold the output. + * `output` may be `undefined`, in which case only the total number of bytes necessary to encode the input will be + * calculated. + * @internal + */ +export const utf8encodeInto = (() => { + if (typeof TextEncoder === "function") { + const encoder = new TextEncoder(); + return (input: string, output: number[] | Uint8Array | undefined) => { + if (output === undefined || isArray(output)) { + return utf8encodeIntoCompat(input, output); + } + + const { written = 0 } = encoder.encodeInto(input, output); + return written; + }; + } + + return utf8encodeIntoCompat; +})(); + +/** + * Replace each instance of non-ascii characters by one, two, three, or four escape sequences + * representing the UTF-8 encoding of the character, and return the expanded char code list. + */ +function getExpandedCharCodes(input: string): number[] { + const output: number[] = []; + utf8encodeInto(input, output); return output; } @@ -7275,39 +7333,71 @@ export function convertToBase64(input: string): string { return result; } -function getStringFromExpandedCharCodes(codes: number[]): string { +/** + * NOTE: Use `utf8decode` instead. This function is a fallback implementation and is only exposed for testing + * purposes. + * @internal + */ +export function utf8decodeCompat(input: readonly number[] | Uint8Array) { let output = ""; let i = 0; - const length = codes.length; + const length = input.length; while (i < length) { - const charCode = codes[i]; - + const charCode = input[i]; + i++; if (charCode < 0x80) { output += String.fromCharCode(charCode); - i++; + continue; } - else if ((charCode & 0B11000000) === 0B11000000) { - let value = charCode & 0B00111111; - i++; - let nextCode: number = codes[i]; - while ((nextCode & 0B11000000) === 0B10000000) { - value = (value << 6) | (nextCode & 0B00111111); - i++; - nextCode = codes[i]; - } - // `value` may be greater than 10FFFF (the maximum unicode codepoint) - JS will just make this into an invalid character for us - output += String.fromCharCode(value); + + let value: number; + let remaining: number; + if ((charCode & 0B11111000) === 0B11110000) { + value = charCode & 0B00000111; + remaining = 3; + } + else if ((charCode & 0B11110000) === 0B11100000) { + value = charCode & 0B00001111; + remaining = 2; + } + else if ((charCode & 0B11100000) === 0B11000000) { + value = charCode & 0B00011111; + remaining = 1; } else { // We don't want to kill the process when decoding fails (due to a following char byte not // following a leading char), so we just print the (bad) value - output += String.fromCharCode(charCode); - i++; + value = charCode; + remaining = 0; } + + while (remaining > 0 && i < input.length) { + const nextCode = input[i]; + i++; + remaining--; + value = (value << 6) | (nextCode & 0B00111111); + } + + // `value` may be greater than 10FFFF (the maximum unicode codepoint) - JS will just make this into an invalid character for us + output += String.fromCodePoint(value); } return output; } +/** @internal */ +export const utf8decode = (() => { + if (typeof TextDecoder === "function") { + const decoder = new TextDecoder(); + return (input: readonly number[] | Uint8Array) => { + if (isArray(input)) { + return decoder.decode(new Uint8Array(input)); + } + return decoder.decode(input); + }; + } + return utf8decodeCompat; +})(); + /** @internal */ export function base64encode(host: { base64encode?(input: string): string } | undefined, input: string): string { if (host && host.base64encode) { @@ -7350,7 +7440,7 @@ export function base64decode(host: { base64decode?(input: string): string } | un } i += 4; } - return getStringFromExpandedCharCodes(expandedCharCodes); + return utf8decode(expandedCharCodes); } /** @internal */ @@ -8006,6 +8096,7 @@ export function getLeftmostExpression(node: Expression, stopAtCallExpressions: b /** @internal */ export interface ObjectAllocator { + getNodeArrayConstructor(): new (items: readonly T[], hasTrailingComma?: boolean) => NodeArray; getNodeConstructor(): new (kind: SyntaxKind, pos: number, end: number) => Node; getTokenConstructor(): new (kind: TKind, pos: number, end: number) => Token; getIdentifierConstructor(): new (kind: SyntaxKind.Identifier, pos: number, end: number) => Identifier; @@ -8017,7 +8108,7 @@ export interface ObjectAllocator { getSourceMapSourceConstructor(): new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource; } -function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { +const SymbolConstructor = function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { this.flags = flags; this.escapedName = name; this.declarations = undefined; @@ -8032,7 +8123,7 @@ function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { this.isReferenced = undefined; this.isAssigned = undefined; (this as any).links = undefined; // used by TransientSymbol -} +}; function Type(this: Type, checker: TypeChecker, flags: TypeFlags) { this.flags = flags; @@ -8090,14 +8181,61 @@ function SourceMapSource(this: SourceMapSource, fileName: string, text: string, this.skipTrivia = skipTrivia || (pos => pos); } +/** + * Emulates the prior behavior of `createNodeArray` to convert an existing array into a `NodeArray` while also fixing + * the prototype. The resulting array becoms the `this` of any derived class. + */ +const ArrayAdopter = function (elements: T[]) { // eslint-disable-line local/only-arrow-functions -- arrow functions aren't constructable. + if (elements !== emptyArray) { + Object.setPrototypeOf(elements, new.target.prototype); + return elements; + } + return Reflect.construct(Array, [0], new.target.prototype); +} as typeof Array & (new (elements: T[]) => T[]); + +Object.setPrototypeOf(ArrayAdopter, Array); +Object.setPrototypeOf(ArrayAdopter.prototype, Array.prototype); + +declare class NodeArrayImpl extends Array { + hasTrailingComma: boolean; + transformFlags: TransformFlags; + pos: number; + end: number; + constructor(elements: readonly T[] | NodeArray, hasTrailingComma?: boolean); +} + +const NodeArray = class extends ArrayAdopter implements MutableNodeArray { + declare hasTrailingComma: boolean; + declare transformFlags: TransformFlags; + declare pos: number; + declare end: number; + + constructor(elements: readonly T[] | NodeArray, hasTrailingComma?: boolean) { + const length = elements.length; + super(length >= 1 && length <= 4 ? elements.slice() : elements as T[]); + this.pos = -1; + this.end = -1; + this.hasTrailingComma = !!hasTrailingComma; + this.transformFlags = TransformFlags.None; + Debug.attachNodeArrayDebugInfo(this); + } + + static { + Object.defineProperty(this, Symbol.species, { configurable: true, value: Array }); + } + +} as typeof NodeArrayImpl; + + /** @internal */ export const objectAllocator: ObjectAllocator = { + getNodeArrayConstructor: () => NodeArray as any, getNodeConstructor: () => Node as any, getTokenConstructor: () => Token as any, getIdentifierConstructor: () => Identifier as any, getPrivateIdentifierConstructor: () => Node as any, getSourceFileConstructor: () => Node as any, - getSymbolConstructor: () => Symbol as any, + getSymbolConstructor: () => SymbolConstructor as any, getTypeConstructor: () => Type as any, getSignatureConstructor: () => Signature as any, getSourceMapSourceConstructor: () => SourceMapSource as any, @@ -8416,32 +8554,47 @@ function isFileForcedToBeModuleByFormat(file: SourceFile): true | undefined { return (file.impliedNodeFormat === ModuleKind.ESNext || (fileExtensionIsOneOf(file.fileName, [Extension.Cjs, Extension.Cts, Extension.Mjs, Extension.Mts]))) && !file.isDeclarationFile ? true : undefined; } +function forceSetExternalModuleIndicator(file: SourceFile) { + file.externalModuleIndicator = isFileProbablyExternalModule(file) || !file.isDeclarationFile || undefined; +} + +function legacySetExternalModuleIndicator(file: SourceFile) { + file.externalModuleIndicator = isFileProbablyExternalModule(file); +} + +function autoSetExternalModuleIndicatorNonJsx(file: SourceFile) { + file.externalModuleIndicator = isFileProbablyExternalModule(file) || isFileForcedToBeModuleByFormat(file); +} + +function autoSetExternalModuleIndicatorJsx(file: SourceFile) { + file.externalModuleIndicator = isFileProbablyExternalModule(file) || isFileModuleFromUsingJSXTag(file) || isFileForcedToBeModuleByFormat(file); +} + +/** @internal */ +export function isSetExternalModuleIndicatorCallbackFromOptions(cb: (file: SourceFile) => void) { + return cb === forceSetExternalModuleIndicator || + cb === legacySetExternalModuleIndicator || + cb === autoSetExternalModuleIndicatorNonJsx || + cb === autoSetExternalModuleIndicatorJsx; +} + /** @internal */ export function getSetExternalModuleIndicator(options: CompilerOptions): (file: SourceFile) => void { - // TODO: Should this callback be cached? switch (getEmitModuleDetectionKind(options)) { case ModuleDetectionKind.Force: // All non-declaration files are modules, declaration files still do the usual isFileProbablyExternalModule - return (file: SourceFile) => { - file.externalModuleIndicator = isFileProbablyExternalModule(file) || !file.isDeclarationFile || undefined; - }; + return forceSetExternalModuleIndicator; case ModuleDetectionKind.Legacy: // Files are modules if they have imports, exports, or import.meta - return (file: SourceFile) => { - file.externalModuleIndicator = isFileProbablyExternalModule(file); - }; + return legacySetExternalModuleIndicator; case ModuleDetectionKind.Auto: // If module is nodenext or node16, all esm format files are modules // If jsx is react-jsx or react-jsxdev then jsx tags force module-ness // otherwise, the presence of import or export statments (or import.meta) implies module-ness - const checks: ((file: SourceFile) => Node | true | undefined)[] = [isFileProbablyExternalModule]; if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) { - checks.push(isFileModuleFromUsingJSXTag); + return autoSetExternalModuleIndicatorJsx; } - checks.push(isFileForcedToBeModuleByFormat); - const combined = or(...checks); - const callback = (file: SourceFile) => void (file.externalModuleIndicator = combined(file)); - return callback; + return autoSetExternalModuleIndicatorNonJsx; } } @@ -8704,6 +8857,11 @@ export function getJSXRuntimeImport(base: string | undefined, options: CompilerO return base ? `${base}/${options.jsx === JsxEmit.ReactJSXDev ? "jsx-dev-runtime" : "jsx-runtime"}` : undefined; } +/** @internal */ +export function getMaxCpuCount(options: CompilerOptions | BuildOptions): number { + return options.maxCpuCount ?? 1; +} + /** @internal */ export function hasZeroOrOneAsteriskCharacter(str: string): boolean { let seenAsterisk = false; @@ -9843,7 +10001,13 @@ export function setParent(child: T | undefined, parent: T["paren /** @internal */ export function setParent(child: T | undefined, parent: T["parent"] | undefined): T | undefined { if (child && parent) { - (child as Mutable).parent = parent; + try { + (child as Mutable).parent = parent; + } + catch (e) { + e.message = Debug.format(e.message, child); + throw e; + } } return child; } @@ -10343,3 +10507,15 @@ export function getPropertyNameFromType(type: StringLiteralType | NumberLiteralT } return Debug.fail(); } + +/** + * Tests whether the provided string can be parsed as a number. + * @param s The string to test. + * @param roundTripOnly Indicates the resulting number matches the input when converted back to a string. + * @internal + */ +export function isValidNumberString(s: string, roundTripOnly: boolean): boolean { + if (s === "") return false; + const n = +s; + return isFinite(n) && (!roundTripOnly || "" + n === s); +} diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 56574d44d1d..bd3de5ae9d1 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1605,8 +1605,8 @@ export function isEntityName(node: Node): node is EntityName { || kind === SyntaxKind.Identifier; } -export function isPropertyName(node: Node): node is PropertyName { - const kind = node.kind; +/** @internal */ +export function isPropertyNameKind(kind: SyntaxKind): kind is PropertyName["kind"] { return kind === SyntaxKind.Identifier || kind === SyntaxKind.PrivateIdentifier || kind === SyntaxKind.StringLiteral @@ -1614,6 +1614,10 @@ export function isPropertyName(node: Node): node is PropertyName { || kind === SyntaxKind.ComputedPropertyName; } +export function isPropertyName(node: Node): node is PropertyName { + return isPropertyNameKind(node.kind); +} + export function isBindingName(node: Node): node is BindingName { const kind = node.kind; return kind === SyntaxKind.Identifier diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index a829f95d17f..a1715dbc519 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -18,6 +18,7 @@ import { createIncrementalCompilerHost, createIncrementalProgram, CreateProgram, + createRequestSourceFile, createWriteFileMeasuringIO, CustomTransformers, Debug, @@ -95,6 +96,7 @@ import { sys, System, targetOptionDeclaration, + ThreadPool, WatchCompilerHost, WatchCompilerHostOfConfigFile, WatchCompilerHostOfFilesAndCompilerOptions, @@ -105,6 +107,7 @@ import { WatchOptions, WatchStatusReporter, whitespaceOrMapCommentRegExp, + WorkerThreadsHost, WriteFileCallback, } from "./_namespaces/ts"; @@ -741,10 +744,12 @@ export function createWatchFactory(host: WatchFactoryHost & { tra export function createCompilerHostFromProgramHost(host: ProgramHost, getCompilerOptions: () => CompilerOptions, directoryStructureHost: DirectoryStructureHost = host): CompilerHost { const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames(); const compilerHost: CompilerHost = { + requestSourceFile: host.threadPool && createRequestSourceFile(host.threadPool, /*setParentNodes*/ undefined), getSourceFile: createGetSourceFile( (fileName, encoding) => !encoding ? compilerHost.readFile(fileName) : host.readFile(fileName, encoding), getCompilerOptions, - /*setParentNodes*/ undefined + /*setParentNodes*/ undefined, + /*overrideObjectAllocator*/ undefined, ), getDefaultLibLocation: maybeBind(host, host.getDefaultLibLocation), getDefaultLibFileName: options => host.getDefaultLibFileName(options), @@ -767,6 +772,8 @@ export function createCompilerHostFromProgramHost(host: ProgramHost, getCom createHash: maybeBind(host, host.createHash), readDirectory: maybeBind(host, host.readDirectory), storeFilesChangingSignatureDuringEmit: host.storeFilesChangingSignatureDuringEmit, + workerThreads: host.workerThreads, + threadPool: host.threadPool, }; return compilerHost; } @@ -820,6 +827,12 @@ export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost) { } return result; }; + const originalRequestSourceFile = compilerHost.requestSourceFile; + if (originalRequestSourceFile) { + compilerHost.requestSourceFile = (parserState, fileName, languageVersionOrOptions, shouldCreateNewSourceFile) => { + originalRequestSourceFile.call(compilerHost, parserState, fileName, languageVersionOrOptions, shouldCreateNewSourceFile, /*setFileVersion*/ true); + }; + } } /** @@ -827,9 +840,11 @@ export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost) { * * @internal */ -export function createProgramHost(system: System, createProgram: CreateProgram | undefined): ProgramHost { +export function createProgramHost(system: System, createProgram: CreateProgram | undefined, workerThreads: WorkerThreadsHost | undefined, threadPool: ThreadPool | undefined): ProgramHost { const getDefaultLibLocation = memoize(() => getDirectoryPath(normalizePath(system.getExecutingFilePath()))); return { + workerThreads, + threadPool, useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, getNewLine: () => system.newLine, getCurrentDirectory: memoize(() => system.getCurrentDirectory()), @@ -855,9 +870,9 @@ export function createProgramHost(system = sys, createProgram: CreateProgram | undefined, reportDiagnostic: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHost { +function createWatchCompilerHost(system = sys, createProgram: CreateProgram | undefined, reportDiagnostic: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, workerThreads?: WorkerThreadsHost, threadPool?: ThreadPool | undefined): WatchCompilerHost { const write = (s: string) => system.write(s + system.newLine); - const result = createProgramHost(system, createProgram) as WatchCompilerHost; + const result = createProgramHost(system, createProgram, workerThreads, threadPool) as WatchCompilerHost; copyProperties(result, createWatchHost(system, reportWatchStatus)); result.afterProgramCreate = builderProgram => { const compilerOptions = builderProgram.getCompilerOptions(); @@ -892,6 +907,8 @@ export interface CreateWatchCompilerHostInput { createProgram?: CreateProgram; reportDiagnostic?: DiagnosticReporter; reportWatchStatus?: WatchStatusReporter; + workerThreads?: WorkerThreadsHost; + threadPool?: ThreadPool; } /** @internal */ @@ -908,10 +925,11 @@ export interface CreateWatchCompilerHostOfConfigFileInput({ configFileName, optionsToExtend, watchOptionsToExtend, extraFileExtensions, - system, createProgram, reportDiagnostic, reportWatchStatus + system, createProgram, reportDiagnostic, reportWatchStatus, + workerThreads, threadPool, }: CreateWatchCompilerHostOfConfigFileInput): WatchCompilerHostOfConfigFile { const diagnosticReporter = reportDiagnostic || createDiagnosticReporter(system); - const host = createWatchCompilerHost(system, createProgram, diagnosticReporter, reportWatchStatus) as WatchCompilerHostOfConfigFile; + const host = createWatchCompilerHost(system, createProgram, diagnosticReporter, reportWatchStatus, workerThreads, threadPool) as WatchCompilerHostOfConfigFile; host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, diagnosticReporter, diagnostic); host.configFileName = configFileName; host.optionsToExtend = optionsToExtend; @@ -934,9 +952,10 @@ export interface CreateWatchCompilerHostOfFilesAndCompilerOptionsInput({ rootFiles, options, watchOptions, projectReferences, - system, createProgram, reportDiagnostic, reportWatchStatus + system, createProgram, reportDiagnostic, reportWatchStatus, + workerThreads, threadPool, }: CreateWatchCompilerHostOfFilesAndCompilerOptionsInput): WatchCompilerHostOfFilesAndCompilerOptions { - const host = createWatchCompilerHost(system, createProgram, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus) as WatchCompilerHostOfFilesAndCompilerOptions; + const host = createWatchCompilerHost(system, createProgram, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus, workerThreads, threadPool) as WatchCompilerHostOfFilesAndCompilerOptions; host.rootFiles = rootFiles; host.options = options; host.watchOptions = watchOptions; diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts index b216c8cb75d..24457865607 100644 --- a/src/compiler/watchPublic.ts +++ b/src/compiler/watchPublic.ts @@ -80,6 +80,7 @@ import { StringLiteralLike, sys, System, + ThreadPool, toPath, toPath as ts_toPath, updateErrorForNoInputFiles, @@ -92,6 +93,7 @@ import { WatchType, WatchTypeRegistry, WildcardDirectoryWatcher, + WorkerThreadsHost, } from "./_namespaces/ts"; export interface ReadBuildProgramHost { @@ -118,8 +120,11 @@ export function readBuilderProgram(compilerOptions: CompilerOptions, host: ReadB return createBuilderProgramUsingProgramBuildInfo(buildInfo, buildInfoPath, host); } -export function createIncrementalCompilerHost(options: CompilerOptions, system = sys): CompilerHost { - const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, system); +export function createIncrementalCompilerHost(options: CompilerOptions, system?: System): CompilerHost; +/** @internal */ +export function createIncrementalCompilerHost(options: CompilerOptions, system?: System, workerThreads?: WorkerThreadsHost, threadPool?: ThreadPool): CompilerHost; +export function createIncrementalCompilerHost(options: CompilerOptions, system = sys, workerThreads?: WorkerThreadsHost, threadPool?: ThreadPool): CompilerHost { + const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, system, workerThreads, threadPool); host.createHash = maybeBind(system, system.createHash); host.storeFilesChangingSignatureDuringEmit = system.storeFilesChangingSignatureDuringEmit; setGetSourceFileAsHashVersioned(host); @@ -164,6 +169,8 @@ export interface WatchHost { clearTimeout?(timeoutId: any): void; } export interface ProgramHost { + /** @internal */ workerThreads: WorkerThreadsHost | undefined; + /** @internal */ threadPool: ThreadPool | undefined; /** * Used to create the program when need for program creation or recreation detected */ diff --git a/src/compiler/worker.ts b/src/compiler/worker.ts new file mode 100644 index 00000000000..c5d05d825ed --- /dev/null +++ b/src/compiler/worker.ts @@ -0,0 +1,73 @@ +import { createCompilerHostWorker, Debug, isNodeLikeSystem, setGetSourceFileAsHashVersioned, sys, System, ThreadPoolState, ThreadPoolThread, workerThreads, WorkerThreadWorkerThreadsHost } from "./_namespaces/ts"; +import { SharedSourceFile } from "./sharing/sharedNode"; +import { getSharedObjectAllocator } from "./sharing/sharedObjectAllocator"; +import { SharedSourceFileEntry } from "./sharing/sharedParserState"; +import { Condition } from "./threading/condition"; +import { UniqueLock } from "./threading/uniqueLock"; + +/** @internal */ +export function executeWorker(_system: System, host: WorkerThreadWorkerThreadsHost) { + // if (process.execArgv.includes("--inspect-brk")) { + // const inspector = require("node:inspector") as typeof import("node:inspector"); + // inspector.open(); + // inspector.waitForDebugger(); + // } + + if (isNodeLikeSystem()) { + const fs: typeof import("fs") = require("fs"); + const util: typeof import("util") = require("util"); + Debug.loggingHost = { + log(_level, s) { + fs.writeSync(2, `[worker#${host.threadId}] ${s || ""}${sys.newLine}`); + }, + format(...args) { + return util.formatWithOptions({ colors: true }, ...args); + } + }; + } + + const { workerData } = host; + Debug.assert(workerData); + const { type } = workerData as { type: string }; + switch (type) { + case "ThreadPoolThread": { + const { state } = workerData as { state: ThreadPoolState }; + const thread = new ThreadPoolThread(state, (name, arg) => { + switch (name) { + case "Program.requestSourceFile": + programRequestSourceFile(arg as SharedSourceFileEntry); + break; + } + }); + thread.run(); + break; + } + default: + Debug.fail(`Unsupported worker type: '${type}'.`); + break; + } + + function programRequestSourceFile(entry: SharedSourceFileEntry) { + let ok = false; + let sharedFile: SharedSourceFile | undefined; + try { + // Debug.log.trace(`parsing: ${entry.fileName}`); + const overideObjectAllocator = getSharedObjectAllocator(); + const host = createCompilerHostWorker({}, entry.setParentNodes, sys, workerThreads, /*threadPool*/ undefined, overideObjectAllocator); + if (entry.setFileVersion) { + setGetSourceFileAsHashVersioned(host); + } + const file = host.getSourceFile(entry.fileName, entry.languageVersion, undefined, entry.shouldCreateNewSourceFile); + Debug.assert(file === undefined || file instanceof SharedSourceFile); + sharedFile = file; + ok = true; + } + finally { + using _ = new UniqueLock(entry.fileMutex); + entry.error = !ok; + entry.done = ok; + entry.file = sharedFile; + Condition.notify(entry.fileCondition); + } + } +} diff --git a/src/compiler/workerThreads.ts b/src/compiler/workerThreads.ts new file mode 100644 index 00000000000..df623414261 --- /dev/null +++ b/src/compiler/workerThreads.ts @@ -0,0 +1,169 @@ +import { Debug, isNodeLikeSystem, sys } from "./_namespaces/ts"; +import "./sharing/structs/sharedStructsGlobals"; + +/** @internal */ +export interface WorkerThreadsHost { + readonly workerData: unknown; + readonly parentPort: MessagePort | undefined; + readonly threadId: number; + isWorkerThread(): this is WorkerThreadWorkerThreadsHost; + isMainThread(): this is MainThreadWorkerThreadsHost; + createWorker(options?: WorkerOptions): Worker; + createMessageChannel(): MessageChannel; + getHostObject(factory: (host: WorkerThreadsHost) => T): T; + addMessageListener(source: MessagePort | Worker, handler: (value: unknown) => void): void; + removeMessageListener(source: MessagePort | Worker, handler: (value: unknown) => void): void; +} + +/** @internal */ +export interface WorkerThreadWorkerThreadsHost extends WorkerThreadsHost { + readonly parentPort: MessagePort; + isMainThread(): false; +} + +/** @internal */ +export interface MainThreadWorkerThreadsHost extends WorkerThreadsHost { + readonly parentPort: undefined; + isWorkerThread(): false; +} + +/** @internal */ +export type Transferrable = + | ArrayBuffer + | MessagePort + ; + +/** @internal */ +export interface WorkerOptions { + /** The path to the worker script. If not provided, then the typescript runtime is assumed. */ + file?: string; + /** Name of the worker for debugging purposes. */ + name?: string; + /** Initial data to pass to the worker. */ + workerData?: unknown; + /** Initial data to transfer to the worker. */ + transferList?: readonly Transferrable[]; +} + +/** @internal */ +export interface Worker { + postMessage(value: unknown, transferList?: readonly Transferrable[]): void; + terminate(): Promise; +} + +/** @internal */ +export interface MessageChannel { + readonly nativeMessageChannel: unknown; + readonly port1: MessagePort; + readonly port2: MessagePort; +} + +/** @internal */ +export interface MessagePort { + postMessage(value: unknown, transferList?: readonly Transferrable[]): void; + close(): void; +} + +function createNodeWorkerThreadHost(): WorkerThreadsHost { + const fs: typeof import("fs") = require("fs"); + const path: typeof import("path") = require("path"); + const worker_threads: typeof import("worker_threads") = require("worker_threads"); + const hostObjectCircularitySentinel = {}; + const hostObjects = new WeakMap(); + + function isWorkerThread() { + return !worker_threads.isMainThread; + } + + function isMainThread() { + return worker_threads.isMainThread; + } + + function getEntrypointPath() { + const file = sys.getExecutingFilePath(); + // The executing file path isn't valid when running unbundled, so try to use tsc.js if available. + // See `executingFilePath` in sys.ts for more information. + if (path.basename(file) === "__fake__.js") { + const tscPath = path.join(path.dirname(file), "tsc.js"); + if (fs.existsSync(tscPath)) { + return tscPath; + } + throw new Error("Cannot determine entrypoint path."); + } + return file; + } + + function createWorker({ file = getEntrypointPath(), name, workerData, transferList, }: WorkerOptions = {}): Worker { + const execArgv: string[] = []; + if (Debug.isDebugging) { + execArgv.push("--inspect"); + } + if (process.execArgv.includes("--enable-source-maps")) { + execArgv.push("--enable-source-maps"); + } + const worker = new worker_threads.Worker(file, { + name, + execArgv, + workerData, + transferList: transferList as unknown as import("worker_threads").TransferListItem[], + // stdout: true, + // stderr: true, + stdout: false, + stderr: false, + // ensure that we use the same seed value when hashing strings on all threads + env: { ...process.env, TS_STRING_SEED: `${sys.stringSeed}` } + }); + // worker.stdout.pipe(process.stdout, { end: false }); + // worker.stderr.pipe(process.stderr, { end: false }); + worker.unref(); + return worker as unknown as Worker; + } + + function createMessageChannel(): MessageChannel { + return new worker_threads.MessageChannel() as unknown as MessageChannel; + } + + function getHostObject(factory: (host: WorkerThreadsHost) => T): T { + let value = hostObjects.get(factory); + if (value === hostObjectCircularitySentinel) throw new TypeError("Circularity detected in host object allocation"); + if (value === undefined) { + hostObjects.set(factory, hostObjectCircularitySentinel); + hostObjects.set(factory, value = factory(workerThreads)); + } + return value as T; + } + + function addMessageListener(source: MessagePort | Worker, handler: (value: unknown) => void) { + (source as import("worker_threads").MessagePort | import("worker_threads").Worker).addListener("message", handler); + } + + function removeMessageListener(source: MessagePort | Worker, handler: (value: unknown) => void) { + (source as import("worker_threads").MessagePort | import("worker_threads").Worker).removeListener("message", handler); + } + + const workerThreads: WorkerThreadsHost = { + workerData: worker_threads.workerData, + parentPort: (worker_threads.parentPort ?? undefined) as MessagePort | undefined, + threadId: worker_threads.threadId, + isWorkerThread, + isMainThread, + createWorker, + createMessageChannel, + getHostObject, + addMessageListener, + removeMessageListener, + }; + return workerThreads; +} + +/** @internal */ +export let workerThreads: WorkerThreadsHost | undefined = (() => { + if (isNodeLikeSystem() && typeof SharedStructType === "function") { + return createNodeWorkerThreadHost(); + } +})(); + +/** @internal */ +export function setWorkerThreadsHost(value: WorkerThreadsHost | undefined) { + workerThreads = value; +} diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index 660effc571d..5bbcd398b33 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -29,7 +29,6 @@ import { createWatchCompilerHostOfConfigFile, createWatchCompilerHostOfFilesAndCompilerOptions, createWatchProgram, - createWatchStatusReporter as ts_createWatchStatusReporter, Debug, Diagnostic, DiagnosticMessage, @@ -54,6 +53,7 @@ import { getDiagnosticText, getErrorSummaryText, getLineStarts, + getMaxCpuCount, getNormalizedAbsolutePath, isIncrementalCompilation, isWatchSet, @@ -67,7 +67,6 @@ import { parseCommandLine, parseConfigFileWithSystem, ParsedCommandLine, - performIncrementalCompilation as ts_performIncrementalCompilation, Program, reduceLeftIterator, ReportEmitErrorSummary, @@ -82,12 +81,16 @@ import { supportedTSExtensionsFlat, sys, System, + ThreadPool, toPath, tracing, + createWatchStatusReporter as ts_createWatchStatusReporter, + performIncrementalCompilation as ts_performIncrementalCompilation, validateLocaleAndSetLanguage, version, WatchCompilerHost, WatchOptions, + WorkerThreadsHost, } from "./_namespaces/ts"; interface Statistic { @@ -556,6 +559,7 @@ function executeCommandLineWorker( sys: System, cb: ExecuteCommandLineCallbacks, commandLine: ParsedCommandLine, + workerThreads: WorkerThreadsHost | undefined, ) { let reportDiagnostic = createDiagnosticReporter(sys); if (commandLine.options.build) { @@ -634,6 +638,13 @@ function executeCommandLineWorker( return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } + let threadPool: ThreadPool | undefined; + const maxCpuCount = getMaxCpuCount(commandLine.options); + if (maxCpuCount > 1 && workerThreads?.isMainThread()) { + threadPool = new ThreadPool(maxCpuCount, workerThreads); + threadPool.start(); + } + const currentDirectory = sys.getCurrentDirectory(); const commandLineOptions = convertToOptionsWithAbsolutePaths( commandLine.options, @@ -671,6 +682,8 @@ function executeCommandLineWorker( commandLineOptions, commandLine.watchOptions, extendedConfigCache, + workerThreads, + threadPool, ); } else if (isIncrementalCompilation(configParseResult.options)) { @@ -678,7 +691,9 @@ function executeCommandLineWorker( sys, cb, reportDiagnostic, - configParseResult + configParseResult, + workerThreads, + threadPool, ); } else { @@ -686,7 +701,9 @@ function executeCommandLineWorker( sys, cb, reportDiagnostic, - configParseResult + configParseResult, + workerThreads, + threadPool, ); } } @@ -710,6 +727,8 @@ function executeCommandLineWorker( commandLine.fileNames, commandLineOptions, commandLine.watchOptions, + workerThreads, + threadPool, ); } else if (isIncrementalCompilation(commandLineOptions)) { @@ -717,7 +736,9 @@ function executeCommandLineWorker( sys, cb, reportDiagnostic, - { ...commandLine, options: commandLineOptions } + { ...commandLine, options: commandLineOptions }, + workerThreads, + threadPool, ); } else { @@ -725,7 +746,9 @@ function executeCommandLineWorker( sys, cb, reportDiagnostic, - { ...commandLine, options: commandLineOptions } + { ...commandLine, options: commandLineOptions }, + workerThreads, + threadPool, ); } } @@ -744,6 +767,7 @@ export function executeCommandLine( system: System, cb: ExecuteCommandLineCallbacks, commandLineArgs: readonly string[], + workerThreads?: WorkerThreadsHost ) { if (isBuild(commandLineArgs)) { const { buildOptions, watchOptions, projects, errors } = parseBuildCommand(commandLineArgs.slice(1)); @@ -754,7 +778,8 @@ export function executeCommandLine( buildOptions, watchOptions, projects, - errors + errors, + workerThreads )); } else { @@ -764,7 +789,8 @@ export function executeCommandLine( buildOptions, watchOptions, projects, - errors + errors, + workerThreads ); } } @@ -775,10 +801,11 @@ export function executeCommandLine( system, cb, commandLine, + workerThreads, )); } else { - return executeCommandLineWorker(system, cb, commandLine); + return executeCommandLineWorker(system, cb, commandLine, workerThreads); } } @@ -797,7 +824,8 @@ function performBuild( buildOptions: BuildOptions, watchOptions: WatchOptions | undefined, projects: string[], - errors: Diagnostic[] + errors: Diagnostic[], + workerThreads: WorkerThreadsHost | undefined, ) { // Update to pretty if host supports it const reportDiagnostic = updateReportDiagnostic( @@ -832,14 +860,23 @@ function performBuild( return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } + let threadPool: ThreadPool | undefined; + const maxCpuCount = getMaxCpuCount(buildOptions); + if (maxCpuCount > 1 && workerThreads?.isMainThread()) { + threadPool = new ThreadPool(maxCpuCount, workerThreads); + } + if (buildOptions.watch) { if (reportWatchModeWithoutSysSupport(sys, reportDiagnostic)) return; + threadPool?.start(); const buildHost = createSolutionBuilderWithWatchHost( sys, /*createProgram*/ undefined, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty(sys, buildOptions)), - createWatchStatusReporter(sys, buildOptions) + createWatchStatusReporter(sys, buildOptions), + workerThreads, + threadPool ); const solutionPerformance = enableSolutionPerformance(sys, buildOptions); updateSolutionBuilderHost(sys, cb, buildHost, solutionPerformance); @@ -861,12 +898,15 @@ function performBuild( return builder; } + threadPool?.start(); const buildHost = createSolutionBuilderHost( sys, /*createProgram*/ undefined, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty(sys, buildOptions)), - createReportErrorSummary(sys, buildOptions) + createReportErrorSummary(sys, buildOptions), + workerThreads, + threadPool ); const solutionPerformance = enableSolutionPerformance(sys, buildOptions); updateSolutionBuilderHost(sys, cb, buildHost, solutionPerformance); @@ -874,6 +914,7 @@ function performBuild( const exitStatus = buildOptions.clean ? builder.clean() : builder.build(); reportSolutionBuilderTimes(builder, solutionPerformance); dumpTracingLegend(); // Will no-op if there hasn't been any tracing + threadPool?.stop(); return sys.exit(exitStatus); } @@ -887,10 +928,12 @@ function performCompilation( sys: System, cb: ExecuteCommandLineCallbacks, reportDiagnostic: DiagnosticReporter, - config: ParsedCommandLine + config: ParsedCommandLine, + workerThreads: WorkerThreadsHost | undefined, + threadPool: ThreadPool | undefined, ) { const { fileNames, options, projectReferences } = config; - const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, sys); + const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, sys, workerThreads, threadPool); const currentDirectory = host.getCurrentDirectory(); const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, currentDirectory, getCanonicalFileName)); @@ -912,6 +955,7 @@ function performCompilation( ); reportStatistics(sys, program, /*solutionPerformance*/ undefined); cb(program); + threadPool?.stop(); return sys.exit(exitStatus); } @@ -919,11 +963,13 @@ function performIncrementalCompilation( sys: System, cb: ExecuteCommandLineCallbacks, reportDiagnostic: DiagnosticReporter, - config: ParsedCommandLine + config: ParsedCommandLine, + workerThreads: WorkerThreadsHost | undefined, + threadPool: ThreadPool | undefined, ) { const { options, fileNames, projectReferences } = config; enableStatisticsAndTracing(sys, options, /*isBuildMode*/ false); - const host = createIncrementalCompilerHost(options, sys); + const host = createIncrementalCompilerHost(options, sys, workerThreads, threadPool); const exitStatus = ts_performIncrementalCompilation({ host, system: sys, @@ -938,6 +984,7 @@ function performIncrementalCompilation( cb(builderProgram); } }); + threadPool?.stop(); return sys.exit(exitStatus); } @@ -996,6 +1043,8 @@ function createWatchOfConfigFile( optionsToExtend: CompilerOptions, watchOptionsToExtend: WatchOptions | undefined, extendedConfigCache: Map, + workerThreads: WorkerThreadsHost | undefined, + threadPool: ThreadPool | undefined, ) { const watchCompilerHost = createWatchCompilerHostOfConfigFile({ configFileName: configParseResult.options.configFilePath!, @@ -1003,7 +1052,9 @@ function createWatchOfConfigFile( watchOptionsToExtend, system, reportDiagnostic, - reportWatchStatus: createWatchStatusReporter(system, configParseResult.options) + reportWatchStatus: createWatchStatusReporter(system, configParseResult.options), + workerThreads, + threadPool, }); updateWatchCompilationHost(system, cb, watchCompilerHost); watchCompilerHost.configFileParsingResult = configParseResult; @@ -1018,6 +1069,8 @@ function createWatchOfFilesAndCompilerOptions( rootFiles: string[], options: CompilerOptions, watchOptions: WatchOptions | undefined, + workerThreads: WorkerThreadsHost | undefined, + threadPool: ThreadPool | undefined, ) { const watchCompilerHost = createWatchCompilerHostOfFilesAndCompilerOptions({ rootFiles, @@ -1025,7 +1078,9 @@ function createWatchOfFilesAndCompilerOptions( watchOptions, system, reportDiagnostic, - reportWatchStatus: createWatchStatusReporter(system, options) + reportWatchStatus: createWatchStatusReporter(system, options), + workerThreads, + threadPool, }); updateWatchCompilationHost(system, cb, watchCompilerHost); return createWatchProgram(watchCompilerHost); diff --git a/src/harness/fakesHosts.ts b/src/harness/fakesHosts.ts index 6155a2622b1..abf12f60a0e 100644 --- a/src/harness/fakesHosts.ts +++ b/src/harness/fakesHosts.ts @@ -554,6 +554,10 @@ export function patchHostForBuildInfoWrite(sys: T, version: } export class SolutionBuilderHost extends CompilerHost implements ts.SolutionBuilderHost { + /** @internal */ declare workerThreads: ts.WorkerThreadsHost | undefined; + /** @internal */ declare threadPool: ts.ThreadPool | undefined; + /** @internal */ declare membrane: ts.Membrane | undefined; + createProgram: ts.CreateProgram; private constructor(sys: System | vfs.FileSystem, options?: ts.CompilerOptions, setParentNodes?: boolean, createProgram?: ts.CreateProgram) { diff --git a/src/services/services.ts b/src/services/services.ts index b0e56cc0645..efd133b5cce 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -225,6 +225,7 @@ import { normalizePath, NumberLiteralType, NumericLiteral, + objectAllocator, ObjectAllocator, ObjectFlags, ObjectLiteralElement, @@ -1259,6 +1260,7 @@ class SourceMapSourceObject implements SourceMapSource { function getServicesObjectAllocator(): ObjectAllocator { return { + getNodeArrayConstructor: objectAllocator.getNodeArrayConstructor, getNodeConstructor: () => NodeObject, getTokenConstructor: () => TokenObject, diff --git a/src/testRunner/tests.ts b/src/testRunner/tests.ts index 1a3da111540..ba61cdf07d5 100644 --- a/src/testRunner/tests.ts +++ b/src/testRunner/tests.ts @@ -1,4 +1,5 @@ import "./unittests/asserts"; +import "./unittests/utf8"; import "./unittests/base64"; import "./unittests/builder"; import "./unittests/canWatch"; @@ -18,6 +19,8 @@ import "./unittests/programApi"; import "./unittests/publicApi"; import "./unittests/reuseProgramStructure"; import "./unittests/semver"; +import "./unittests/sharing/hashData"; +import "./unittests/sharing/resizableArray"; import "./unittests/transform"; import "./unittests/typeParameterIsPossiblyReferenced"; import "./unittests/config/commandLineParsing"; diff --git a/src/testRunner/unittests/sharing/hashData.ts b/src/testRunner/unittests/sharing/hashData.ts new file mode 100644 index 00000000000..275e6754ada --- /dev/null +++ b/src/testRunner/unittests/sharing/hashData.ts @@ -0,0 +1,15 @@ +import { HashData } from "../../../compiler/sharing/collections/hashData"; + +describe("unittests:: hashData", () => { + describe("findEntryIndex", () => { + it("when empty", () => { + const hashData = new HashData(0); + expect(HashData.findEntryIndex(hashData, 0)).to.equal(-1); + }); + it("when has element", () => { + const hashData = new HashData(0); + HashData.insertEntry(hashData, "a", "a"); + expect(HashData.findEntryIndex(hashData, "a")).to.equal(0); + }); + }); +}); \ No newline at end of file diff --git a/src/testRunner/unittests/sharing/resizableArray.ts b/src/testRunner/unittests/sharing/resizableArray.ts new file mode 100644 index 00000000000..347a59f1cb7 --- /dev/null +++ b/src/testRunner/unittests/sharing/resizableArray.ts @@ -0,0 +1,9 @@ +import { Membrane, ProxyResizableArray } from "../../_namespaces/ts"; + +describe("unittests:: resizableArray", () => { + it("push", () => { + const membrane = new Membrane(); + const array = new ProxyResizableArray(membrane); + array.push(1); + }); +}); \ No newline at end of file diff --git a/src/testRunner/unittests/utf8.ts b/src/testRunner/unittests/utf8.ts new file mode 100644 index 00000000000..d41b8fbc959 --- /dev/null +++ b/src/testRunner/unittests/utf8.ts @@ -0,0 +1,55 @@ +import { utf8decodeCompat, utf8encodeIntoCompat } from "../_namespaces/ts"; + +describe("unittests:: utf8", () => { + describe("utf8encodeIntoCompat matches TextEncoder.encode", () => { + const data = [ + "日本語", + "🐱", + "\ud83d\udc31", + "\u{1F431}", + "\x00\x01", + "\t\n\r\\\"'\u0062", + "====", + "", + ]; + for (const item of data) { + it(item, () => { + const buffer1 = new Uint8Array(65536); + const buffer2 = new Uint8Array(65536); + const written1 = utf8encodeIntoCompat(item, buffer1); + const { written: written2 } = new TextEncoder().encodeInto(item, buffer2); + const bytes1 = toBytes(buffer1.slice(0, written1)), bytes2 = toBytes(buffer2.slice(0, written2)); + assert.equal(bytes1, bytes2); + }); + } + }); + function toBytes(buffer: Uint8Array) { + let s = ""; + for (const byte of buffer) { + if (byte < 16) s += "0"; + s += byte.toString(16).toUpperCase(); + } + return s; + } + describe("utf8decodeCompat matches TextDecoder.decode", () => { + const data = [ + "日本語", + "🐱", + "\ud83d\udc31", + "\u{1F431}", + "\x00\x01", + "\t\n\r\\\"'\u0062", + "====", + "", + ]; + for (const item of data) { + it(item, () => { + const buffer = new Uint8Array(65536); + const { written } = new TextEncoder().encodeInto(item, buffer); + const decoded1 = utf8decodeCompat(buffer.slice(0, written)); + const decoded2 = new TextDecoder().decode(buffer.slice(0, written)); + assert.equal(decoded1, decoded2); + }); + } + }); +}); diff --git a/src/tsc/tsc.ts b/src/tsc/tsc.ts index ce8b356b685..415d2c6c8a2 100644 --- a/src/tsc/tsc.ts +++ b/src/tsc/tsc.ts @@ -9,6 +9,8 @@ ts.Debug.loggingHost = { } }; +ts.sys.tryEnableSharedStructs?.(); + if (ts.Debug.isDebugging) { ts.Debug.enableDebugInfo(); } @@ -21,4 +23,9 @@ if (ts.sys.setBlocking) { ts.sys.setBlocking(); } -ts.executeCommandLine(ts.sys, ts.noop, ts.sys.args); +if (ts.workerThreads?.isWorkerThread()) { + ts.executeWorker(ts.sys, ts.workerThreads); +} +else { + ts.executeCommandLine(ts.sys, ts.noop, ts.sys.args, ts.workerThreads); +} diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 0421149e4cf..5bb7f2adb1c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7218,6 +7218,7 @@ declare namespace ts { lib?: string[]; locale?: string; mapRoot?: string; + maxCpuCount?: number; maxNodeModuleJsDepth?: number; module?: ModuleKind; moduleResolution?: ModuleResolutionKind; @@ -7494,6 +7495,7 @@ declare namespace ts { readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; } interface CompilerHost extends ModuleResolutionHost { + requestSourceFile?(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, shouldCreateNewSourceFile?: boolean): void; getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; getSourceFileByPath?(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; getCancellationToken?(): CancellationToken; @@ -9874,6 +9876,7 @@ declare namespace ts { emitDeclarationOnly?: boolean; sourceMap?: boolean; inlineSourceMap?: boolean; + maxCpuCount?: number; traceResolution?: boolean; [option: string]: CompilerOptionsValue | undefined; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3be5dc3c257..d7b0165237f 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3158,6 +3158,7 @@ declare namespace ts { lib?: string[]; locale?: string; mapRoot?: string; + maxCpuCount?: number; maxNodeModuleJsDepth?: number; module?: ModuleKind; moduleResolution?: ModuleResolutionKind; @@ -3434,6 +3435,7 @@ declare namespace ts { readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; } interface CompilerHost extends ModuleResolutionHost { + requestSourceFile?(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, shouldCreateNewSourceFile?: boolean): void; getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; getSourceFileByPath?(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; getCancellationToken?(): CancellationToken; @@ -5814,6 +5816,7 @@ declare namespace ts { emitDeclarationOnly?: boolean; sourceMap?: boolean; inlineSourceMap?: boolean; + maxCpuCount?: number; traceResolution?: boolean; [option: string]: CompilerOptionsValue | undefined; } diff --git a/thread.log b/thread.log new file mode 100644 index 00000000000..e69de29bb2d