• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts {
3    export enum LogLevel {
4        Off,
5        Error,
6        Warning,
7        Info,
8        Verbose
9    }
10
11    export interface LoggingHost {
12        log(level: LogLevel, s: string): void;
13    }
14
15    export interface DeprecationOptions {
16        message?: string;
17        error?: boolean;
18        since?: Version | string;
19        warnAfter?: Version | string;
20        errorAfter?: Version | string;
21        typeScriptVersion?: Version | string;
22        name?: string;
23    }
24
25    export namespace Debug {
26        let typeScriptVersion: Version | undefined;
27
28        /* eslint-disable prefer-const */
29        let currentAssertionLevel = AssertionLevel.None;
30        export let currentLogLevel = LogLevel.Warning;
31        export let isDebugging = false;
32        export let loggingHost: LoggingHost | undefined;
33        export let enableDeprecationWarnings = true;
34        /* eslint-enable prefer-const */
35
36        type AssertionKeys = MatchingKeys<typeof Debug, AnyFunction>;
37        export function getTypeScriptVersion() {
38            return typeScriptVersion ?? (typeScriptVersion = new Version(version));
39        }
40
41        export function shouldLog(level: LogLevel): boolean {
42            return currentLogLevel <= level;
43        }
44
45        function logMessage(level: LogLevel, s: string): void {
46            if (loggingHost && shouldLog(level)) {
47                loggingHost.log(level, s);
48            }
49        }
50
51        export function log(s: string): void {
52            logMessage(LogLevel.Info, s);
53        }
54
55        export namespace log {
56            export function error(s: string): void {
57                logMessage(LogLevel.Error, s);
58            }
59
60            export function warn(s: string): void {
61                logMessage(LogLevel.Warning, s);
62            }
63
64            export function log(s: string): void {
65                logMessage(LogLevel.Info, s);
66            }
67
68            export function trace(s: string): void {
69                logMessage(LogLevel.Verbose, s);
70            }
71        }
72
73        const assertionCache: Partial<Record<AssertionKeys, { level: AssertionLevel, assertion: AnyFunction }>> = {};
74
75        export function getAssertionLevel() {
76            return currentAssertionLevel;
77        }
78
79        export function setAssertionLevel(level: AssertionLevel) {
80            const prevAssertionLevel = currentAssertionLevel;
81            currentAssertionLevel = level;
82
83            if (level > prevAssertionLevel) {
84                // restore assertion functions for the current assertion level (see `shouldAssertFunction`).
85                for (const key of getOwnKeys(assertionCache) as AssertionKeys[]) {
86                    const cachedFunc = assertionCache[key];
87                    if (cachedFunc !== undefined && Debug[key] !== cachedFunc.assertion && level >= cachedFunc.level) {
88                        (Debug as any)[key] = cachedFunc;
89                        assertionCache[key] = undefined;
90                    }
91                }
92            }
93        }
94
95        export function shouldAssert(level: AssertionLevel): boolean {
96            return currentAssertionLevel >= level;
97        }
98
99        /**
100         * Tests whether an assertion function should be executed. If it shouldn't, it is cached and replaced with `ts.noop`.
101         * Replaced assertion functions are restored when `Debug.setAssertionLevel` is set to a high enough level.
102         * @param level The minimum assertion level required.
103         * @param name The name of the current assertion function.
104         */
105        function shouldAssertFunction<K extends AssertionKeys>(level: AssertionLevel, name: K): boolean {
106            if (!shouldAssert(level)) {
107                assertionCache[name] = { level, assertion: Debug[name] };
108                (Debug as any)[name] = noop;
109                return false;
110            }
111            return true;
112        }
113
114        export function fail(message?: string, stackCrawlMark?: AnyFunction): never {
115            debugger;
116            const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
117            if ((Error as any).captureStackTrace) {
118                (Error as any).captureStackTrace(e, stackCrawlMark || fail);
119            }
120            throw e;
121        }
122
123        export function failBadSyntaxKind(node: Node, message?: string, stackCrawlMark?: AnyFunction): never {
124            return fail(
125                `${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`,
126                stackCrawlMark || failBadSyntaxKind);
127        }
128
129        export function assert(expression: unknown, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): asserts expression {
130            if (!expression) {
131                message = message ? `False expression: ${message}` : "False expression.";
132                if (verboseDebugInfo) {
133                    message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
134                }
135                fail(message, stackCrawlMark || assert);
136            }
137        }
138
139        export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string, stackCrawlMark?: AnyFunction): void {
140            if (a !== b) {
141                const message = msg ? msg2 ? `${msg} ${msg2}` : msg : "";
142                fail(`Expected ${a} === ${b}. ${message}`, stackCrawlMark || assertEqual);
143            }
144        }
145
146        export function assertLessThan(a: number, b: number, msg?: string, stackCrawlMark?: AnyFunction): void {
147            if (a >= b) {
148                fail(`Expected ${a} < ${b}. ${msg || ""}`, stackCrawlMark || assertLessThan);
149            }
150        }
151
152        export function assertLessThanOrEqual(a: number, b: number, stackCrawlMark?: AnyFunction): void {
153            if (a > b) {
154                fail(`Expected ${a} <= ${b}`, stackCrawlMark || assertLessThanOrEqual);
155            }
156        }
157
158        export function assertGreaterThanOrEqual(a: number, b: number, stackCrawlMark?: AnyFunction): void {
159            if (a < b) {
160                fail(`Expected ${a} >= ${b}`, stackCrawlMark || assertGreaterThanOrEqual);
161            }
162        }
163
164        export function assertIsDefined<T>(value: T, message?: string, stackCrawlMark?: AnyFunction): asserts value is NonNullable<T> {
165            // eslint-disable-next-line no-null/no-null
166            if (value === undefined || value === null) {
167                fail(message, stackCrawlMark || assertIsDefined);
168            }
169        }
170
171        export function checkDefined<T>(value: T | null | undefined, message?: string, stackCrawlMark?: AnyFunction): T {
172            assertIsDefined(value, message, stackCrawlMark || checkDefined);
173            return value;
174        }
175
176        export function assertEachIsDefined<T extends Node>(value: NodeArray<T>, message?: string, stackCrawlMark?: AnyFunction): asserts value is NodeArray<T>;
177        export function assertEachIsDefined<T>(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction): asserts value is readonly NonNullable<T>[];
178        export function assertEachIsDefined<T>(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction) {
179            for (const v of value) {
180                assertIsDefined(v, message, stackCrawlMark || assertEachIsDefined);
181            }
182        }
183
184        export function checkEachDefined<T, A extends readonly T[]>(value: A, message?: string, stackCrawlMark?: AnyFunction): A {
185            assertEachIsDefined(value, message, stackCrawlMark || checkEachDefined);
186            return value;
187        }
188
189        export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never {
190            const detail = typeof member === "object" && hasProperty(member, "kind") && hasProperty(member, "pos") ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member);
191            return fail(`${message} ${detail}`, stackCrawlMark || assertNever);
192        }
193
194        export function assertEachNode<T extends Node, U extends T>(nodes: NodeArray<T>, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is NodeArray<U>;
195        export function assertEachNode<T extends Node, U extends T>(nodes: readonly T[], test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is readonly U[];
196        export function assertEachNode<T extends Node, U extends T>(nodes: NodeArray<T> | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is NodeArray<U> | undefined;
197        export function assertEachNode<T extends Node, U extends T>(nodes: readonly T[] | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is readonly U[] | undefined;
198        export function assertEachNode(nodes: readonly Node[], test: (node: Node) => boolean, message?: string, stackCrawlMark?: AnyFunction): void;
199        export function assertEachNode(nodes: readonly Node[] | undefined, test: (node: Node) => boolean, message?: string, stackCrawlMark?: AnyFunction) {
200            if (shouldAssertFunction(AssertionLevel.Normal, "assertEachNode")) {
201                assert(
202                    test === undefined || every(nodes, test),
203                    message || "Unexpected node.",
204                    () => `Node array did not pass test '${getFunctionName(test)}'.`,
205                    stackCrawlMark || assertEachNode);
206            }
207        }
208
209        export function assertNode<T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U;
210        export function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void;
211        export function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) {
212            if (shouldAssertFunction(AssertionLevel.Normal, "assertNode")) {
213                assert(
214                    node !== undefined && (test === undefined || test(node)),
215                    message || "Unexpected node.",
216                    () => `Node ${formatSyntaxKind(node?.kind)} did not pass test '${getFunctionName(test!)}'.`,
217                    stackCrawlMark || assertNode);
218            }
219        }
220
221        export function assertNotNode<T extends Node, U extends T>(node: T | undefined, test: (node: Node) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is Exclude<T, U>;
222        export function assertNotNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void;
223        export function assertNotNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) {
224            if (shouldAssertFunction(AssertionLevel.Normal, "assertNotNode")) {
225                assert(
226                    node === undefined || test === undefined || !test(node),
227                    message || "Unexpected node.",
228                    () => `Node ${formatSyntaxKind(node!.kind)} should not have passed test '${getFunctionName(test!)}'.`,
229                    stackCrawlMark || assertNotNode);
230            }
231        }
232
233        export function assertOptionalNode<T extends Node, U extends T>(node: T, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U;
234        export function assertOptionalNode<T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U | undefined;
235        export function assertOptionalNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void;
236        export function assertOptionalNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) {
237            if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalNode")) {
238                assert(
239                    test === undefined || node === undefined || test(node),
240                    message || "Unexpected node.",
241                    () => `Node ${formatSyntaxKind(node?.kind)} did not pass test '${getFunctionName(test!)}'.`,
242                    stackCrawlMark || assertOptionalNode);
243            }
244        }
245
246        export function assertOptionalToken<T extends Node, K extends SyntaxKind>(node: T, kind: K, message?: string, stackCrawlMark?: AnyFunction): asserts node is Extract<T, { readonly kind: K }>;
247        export function assertOptionalToken<T extends Node, K extends SyntaxKind>(node: T | undefined, kind: K, message?: string, stackCrawlMark?: AnyFunction): asserts node is Extract<T, { readonly kind: K }> | undefined;
248        export function assertOptionalToken(node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction): void;
249        export function assertOptionalToken(node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction) {
250            if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalToken")) {
251                assert(
252                    kind === undefined || node === undefined || node.kind === kind,
253                    message || "Unexpected node.",
254                    () => `Node ${formatSyntaxKind(node?.kind)} was not a '${formatSyntaxKind(kind)}' token.`,
255                    stackCrawlMark || assertOptionalToken);
256            }
257        }
258
259        export function assertMissingNode(node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction): asserts node is undefined;
260        export function assertMissingNode(node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction) {
261            if (shouldAssertFunction(AssertionLevel.Normal, "assertMissingNode")) {
262                assert(
263                    node === undefined,
264                    message || "Unexpected node.",
265                    () => `Node ${formatSyntaxKind(node!.kind)} was unexpected'.`,
266                    stackCrawlMark || assertMissingNode);
267            }
268        }
269
270        /**
271         * Asserts a value has the specified type in typespace only (does not perform a runtime assertion).
272         * This is useful in cases where we switch on `node.kind` and can be reasonably sure the type is accurate, and
273         * as a result can reduce the number of unnecessary casts.
274         */
275        export function type<T>(value: unknown): asserts value is T;
276        export function type(_value: unknown) { }
277
278        export function getFunctionName(func: AnyFunction) {
279            if (typeof func !== "function") {
280                return "";
281            }
282            else if (hasProperty(func, "name")) {
283                return (func as any).name;
284            }
285            else {
286                const text = Function.prototype.toString.call(func);
287                const match = /^function\s+([\w\$]+)\s*\(/.exec(text);
288                return match ? match[1] : "";
289            }
290        }
291
292        export function formatSymbol(symbol: Symbol): string {
293            return `{ name: ${unescapeLeadingUnderscores(symbol.escapedName)}; flags: ${formatSymbolFlags(symbol.flags)}; declarations: ${map(symbol.declarations, node => formatSyntaxKind(node.kind))} }`;
294        }
295
296        /**
297         * Formats an enum value as a string for debugging and debug assertions.
298         */
299        export function formatEnum(value = 0, enumObject: any, isFlags?: boolean) {
300            const members = getEnumMembers(enumObject);
301            if (value === 0) {
302                return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0";
303            }
304            if (isFlags) {
305                const result: string[] = [];
306                let remainingFlags = value;
307                for (const [enumValue, enumName] of members) {
308                    if (enumValue > value) {
309                        break;
310                    }
311                    if (enumValue !== 0 && enumValue & value) {
312                        result.push(enumName);
313                        remainingFlags &= ~enumValue;
314                    }
315                }
316                if (remainingFlags === 0) {
317                    return result.join("|");
318                }
319            }
320            else {
321                for (const [enumValue, enumName] of members) {
322                    if (enumValue === value) {
323                        return enumName;
324                    }
325                }
326            }
327            return value.toString();
328        }
329
330        const enumMemberCache = new Map<any, SortedReadonlyArray<[number, string]>>();
331
332        function getEnumMembers(enumObject: any) {
333            // Assuming enum objects do not change at runtime, we can cache the enum members list
334            // to reuse later. This saves us from reconstructing this each and every time we call
335            // a formatting function (which can be expensive for large enums like SyntaxKind).
336            const existing = enumMemberCache.get(enumObject);
337            if (existing) {
338                return existing;
339            }
340
341            const result: [number, string][] = [];
342            for (const name in enumObject) {
343                const value = enumObject[name];
344                if (typeof value === "number") {
345                    result.push([value, name]);
346                }
347            }
348
349            const sorted = stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0]));
350            enumMemberCache.set(enumObject, sorted);
351            return sorted;
352        }
353
354        export function formatSyntaxKind(kind: SyntaxKind | undefined): string {
355            return formatEnum(kind, (ts as any).SyntaxKind, /*isFlags*/ false);
356        }
357
358        export function formatSnippetKind(kind: SnippetKind | undefined): string {
359            return formatEnum(kind, (ts as any).SnippetKind, /*isFlags*/ false);
360        }
361
362        export function formatNodeFlags(flags: NodeFlags | undefined): string {
363            return formatEnum(flags, (ts as any).NodeFlags, /*isFlags*/ true);
364        }
365
366        export function formatModifierFlags(flags: ModifierFlags | undefined): string {
367            return formatEnum(flags, (ts as any).ModifierFlags, /*isFlags*/ true);
368        }
369
370        export function formatTransformFlags(flags: TransformFlags | undefined): string {
371            return formatEnum(flags, (ts as any).TransformFlags, /*isFlags*/ true);
372        }
373
374        export function formatEmitFlags(flags: EmitFlags | undefined): string {
375            return formatEnum(flags, (ts as any).EmitFlags, /*isFlags*/ true);
376        }
377
378        export function formatSymbolFlags(flags: SymbolFlags | undefined): string {
379            return formatEnum(flags, (ts as any).SymbolFlags, /*isFlags*/ true);
380        }
381
382        export function formatTypeFlags(flags: TypeFlags | undefined): string {
383            return formatEnum(flags, (ts as any).TypeFlags, /*isFlags*/ true);
384        }
385
386        export function formatSignatureFlags(flags: SignatureFlags | undefined): string {
387            return formatEnum(flags, (ts as any).SignatureFlags, /*isFlags*/ true);
388        }
389
390        export function formatObjectFlags(flags: ObjectFlags | undefined): string {
391            return formatEnum(flags, (ts as any).ObjectFlags, /*isFlags*/ true);
392        }
393
394        export function formatFlowFlags(flags: FlowFlags | undefined): string {
395            return formatEnum(flags, (ts as any).FlowFlags, /*isFlags*/ true);
396        }
397
398        export function formatRelationComparisonResult(result: RelationComparisonResult | undefined): string {
399            return formatEnum(result, (ts as any).RelationComparisonResult, /*isFlags*/ true);
400        }
401
402        export function formatCheckMode(mode: CheckMode | undefined): string {
403            return formatEnum(mode, (ts as any).CheckMode, /*isFlags*/ true);
404        }
405
406        export function formatSignatureCheckMode(mode: SignatureCheckMode | undefined): string {
407            return formatEnum(mode, (ts as any).SignatureCheckMode, /*isFlags*/ true);
408        }
409
410        export function formatTypeFacts(facts: TypeFacts | undefined): string {
411            return formatEnum(facts, (ts as any).TypeFacts, /*isFlags*/ true);
412        }
413
414        let isDebugInfoEnabled = false;
415
416        interface ExtendedDebugModule {
417            init(_ts: typeof ts): void;
418            formatControlFlowGraph(flowNode: FlowNode): string;
419        }
420
421        let extendedDebugModule: ExtendedDebugModule | undefined;
422
423        function extendedDebug() {
424            enableDebugInfo();
425            if (!extendedDebugModule) {
426                throw new Error("Debugging helpers could not be loaded.");
427            }
428            return extendedDebugModule;
429        }
430
431        export function printControlFlowGraph(flowNode: FlowNode) {
432            return console.log(formatControlFlowGraph(flowNode));
433        }
434
435        export function formatControlFlowGraph(flowNode: FlowNode) {
436            return extendedDebug().formatControlFlowGraph(flowNode);
437        }
438
439        let flowNodeProto: FlowNodeBase | undefined;
440
441        function attachFlowNodeDebugInfoWorker(flowNode: FlowNodeBase) {
442            if (!("__debugFlowFlags" in flowNode)) { // eslint-disable-line local/no-in-operator
443                Object.defineProperties(flowNode, {
444                    // for use with vscode-js-debug's new customDescriptionGenerator in launch.json
445                    __tsDebuggerDisplay: {
446                        value(this: FlowNodeBase) {
447                            const flowHeader =
448                                this.flags & FlowFlags.Start ? "FlowStart" :
449                                this.flags & FlowFlags.BranchLabel ? "FlowBranchLabel" :
450                                this.flags & FlowFlags.LoopLabel ? "FlowLoopLabel" :
451                                this.flags & FlowFlags.Assignment ? "FlowAssignment" :
452                                this.flags & FlowFlags.TrueCondition ? "FlowTrueCondition" :
453                                this.flags & FlowFlags.FalseCondition ? "FlowFalseCondition" :
454                                this.flags & FlowFlags.SwitchClause ? "FlowSwitchClause" :
455                                this.flags & FlowFlags.ArrayMutation ? "FlowArrayMutation" :
456                                this.flags & FlowFlags.Call ? "FlowCall" :
457                                this.flags & FlowFlags.ReduceLabel ? "FlowReduceLabel" :
458                                this.flags & FlowFlags.Unreachable ? "FlowUnreachable" :
459                                "UnknownFlow";
460                            const remainingFlags = this.flags & ~(FlowFlags.Referenced - 1);
461                            return `${flowHeader}${remainingFlags ? ` (${formatFlowFlags(remainingFlags)})`: ""}`;
462                        }
463                    },
464                    __debugFlowFlags: { get(this: FlowNodeBase) { return formatEnum(this.flags, (ts as any).FlowFlags, /*isFlags*/ true); } },
465                    __debugToString: { value(this: FlowNodeBase) { return formatControlFlowGraph(this); } }
466                });
467            }
468        }
469
470        export function attachFlowNodeDebugInfo(flowNode: FlowNodeBase) {
471            if (isDebugInfoEnabled) {
472                if (typeof Object.setPrototypeOf === "function") {
473                    // if we're in es2015, attach the method to a shared prototype for `FlowNode`
474                    // so the method doesn't show up in the watch window.
475                    if (!flowNodeProto) {
476                        flowNodeProto = Object.create(Object.prototype) as FlowNodeBase;
477                        attachFlowNodeDebugInfoWorker(flowNodeProto);
478                    }
479                    Object.setPrototypeOf(flowNode, flowNodeProto);
480                }
481                else {
482                    // not running in an es2015 environment, attach the method directly.
483                    attachFlowNodeDebugInfoWorker(flowNode);
484                }
485            }
486        }
487
488        let nodeArrayProto: NodeArray<Node> | undefined;
489
490        function attachNodeArrayDebugInfoWorker(array: NodeArray<Node>) {
491            if (!("__tsDebuggerDisplay" in array)) { // eslint-disable-line local/no-in-operator
492                Object.defineProperties(array, {
493                    __tsDebuggerDisplay: {
494                        value(this: NodeArray<Node>, defaultValue: string) {
495                            // An `Array` with extra properties is rendered as `[A, B, prop1: 1, prop2: 2]`. Most of
496                            // these aren't immediately useful so we trim off the `prop1: ..., prop2: ...` part from the
497                            // formatted string.
498                            // This regex can trigger slow backtracking because of overlapping potential captures.
499                            // We don't care, this is debug code that's only enabled with a debugger attached -
500                            // we're just taking note of it for anyone checking regex performance in the future.
501                            defaultValue = String(defaultValue).replace(/(?:,[\s\w\d_]+:[^,]+)+\]$/, "]");
502                            return `NodeArray ${defaultValue}`;
503                        }
504                    }
505                });
506            }
507        }
508
509        export function attachNodeArrayDebugInfo(array: NodeArray<Node>) {
510            if (isDebugInfoEnabled) {
511                if (typeof Object.setPrototypeOf === "function") {
512                    // if we're in es2015, attach the method to a shared prototype for `NodeArray`
513                    // so the method doesn't show up in the watch window.
514                    if (!nodeArrayProto) {
515                        nodeArrayProto = Object.create(Array.prototype) as NodeArray<Node>;
516                        attachNodeArrayDebugInfoWorker(nodeArrayProto);
517                    }
518                    Object.setPrototypeOf(array, nodeArrayProto);
519                }
520                else {
521                    // not running in an es2015 environment, attach the method directly.
522                    attachNodeArrayDebugInfoWorker(array);
523                }
524            }
525        }
526
527        /**
528         * Injects debug information into frequently used types.
529         */
530        export function enableDebugInfo() {
531            if (isDebugInfoEnabled) return;
532
533            // avoid recomputing
534            let weakTypeTextMap: WeakMap<Type, string> | undefined;
535            let weakNodeTextMap: WeakMap<Node, string> | undefined;
536
537            function getWeakTypeTextMap() {
538                if (weakTypeTextMap === undefined) {
539                    if (typeof WeakMap === "function") weakTypeTextMap = new WeakMap();
540                }
541                return weakTypeTextMap;
542            }
543
544            function getWeakNodeTextMap() {
545                if (weakNodeTextMap === undefined) {
546                    if (typeof WeakMap === "function") weakNodeTextMap = new WeakMap();
547                }
548                return weakNodeTextMap;
549            }
550
551
552            // Add additional properties in debug mode to assist with debugging.
553            Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
554                // for use with vscode-js-debug's new customDescriptionGenerator in launch.json
555                __tsDebuggerDisplay: {
556                    value(this: Symbol) {
557                        const symbolHeader =
558                            this.flags & SymbolFlags.Transient ? "TransientSymbol" :
559                            "Symbol";
560                        const remainingSymbolFlags = this.flags & ~SymbolFlags.Transient;
561                        return `${symbolHeader} '${symbolName(this)}'${remainingSymbolFlags ? ` (${formatSymbolFlags(remainingSymbolFlags)})` : ""}`;
562                    }
563                },
564                __debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
565            });
566
567            Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
568                // for use with vscode-js-debug's new customDescriptionGenerator in launch.json
569                __tsDebuggerDisplay: {
570                    value(this: Type) {
571                        const typeHeader =
572                            this.flags & TypeFlags.Nullable ? "NullableType" :
573                            this.flags & TypeFlags.StringOrNumberLiteral ? `LiteralType ${JSON.stringify((this as LiteralType).value)}` :
574                            this.flags & TypeFlags.BigIntLiteral ? `LiteralType ${(this as BigIntLiteralType).value.negative ? "-" : ""}${(this as BigIntLiteralType).value.base10Value}n` :
575                            this.flags & TypeFlags.UniqueESSymbol ? "UniqueESSymbolType" :
576                            this.flags & TypeFlags.Enum ? "EnumType" :
577                            this.flags & TypeFlags.Intrinsic ? `IntrinsicType ${(this as IntrinsicType).intrinsicName}` :
578                            this.flags & TypeFlags.Union ? "UnionType" :
579                            this.flags & TypeFlags.Intersection ? "IntersectionType" :
580                            this.flags & TypeFlags.Index ? "IndexType" :
581                            this.flags & TypeFlags.IndexedAccess ? "IndexedAccessType" :
582                            this.flags & TypeFlags.Conditional ? "ConditionalType" :
583                            this.flags & TypeFlags.Substitution ? "SubstitutionType" :
584                            this.flags & TypeFlags.TypeParameter ? "TypeParameter" :
585                            this.flags & TypeFlags.Object ?
586                                (this as ObjectType).objectFlags & ObjectFlags.ClassOrInterface ? "InterfaceType" :
587                                (this as ObjectType).objectFlags & ObjectFlags.Reference ? "TypeReference" :
588                                (this as ObjectType).objectFlags & ObjectFlags.Tuple ? "TupleType" :
589                                (this as ObjectType).objectFlags & ObjectFlags.Anonymous ? "AnonymousType" :
590                                (this as ObjectType).objectFlags & ObjectFlags.Mapped ? "MappedType" :
591                                (this as ObjectType).objectFlags & ObjectFlags.ReverseMapped ? "ReverseMappedType" :
592                                (this as ObjectType).objectFlags & ObjectFlags.EvolvingArray ? "EvolvingArrayType" :
593                                "ObjectType" :
594                            "Type";
595                        const remainingObjectFlags = this.flags & TypeFlags.Object ? (this as ObjectType).objectFlags & ~ObjectFlags.ObjectTypeKindMask : 0;
596                        return `${typeHeader}${this.symbol ? ` '${symbolName(this.symbol)}'` : ""}${remainingObjectFlags ? ` (${formatObjectFlags(remainingObjectFlags)})` : ""}`;
597                    }
598                },
599                __debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } },
600                __debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((this as ObjectType).objectFlags) : ""; } },
601                __debugTypeToString: {
602                    value(this: Type) {
603                        // avoid recomputing
604                        const map = getWeakTypeTextMap();
605                        let text = map?.get(this);
606                        if (text === undefined) {
607                            text = this.checker.typeToString(this);
608                            map?.set(this, text);
609                        }
610                        return text;
611                    }
612                },
613            });
614
615            Object.defineProperties(objectAllocator.getSignatureConstructor().prototype, {
616                __debugFlags: { get(this: Signature) { return formatSignatureFlags(this.flags); } },
617                __debugSignatureToString: { value(this: Signature) { return this.checker?.signatureToString(this); } }
618            });
619
620            const nodeConstructors = [
621                objectAllocator.getNodeConstructor(),
622                objectAllocator.getIdentifierConstructor(),
623                objectAllocator.getTokenConstructor(),
624                objectAllocator.getSourceFileConstructor()
625            ];
626
627            for (const ctor of nodeConstructors) {
628                if (!hasProperty(ctor.prototype, "__debugKind")) {
629                    Object.defineProperties(ctor.prototype, {
630                        // for use with vscode-js-debug's new customDescriptionGenerator in launch.json
631                        __tsDebuggerDisplay: {
632                            value(this: Node) {
633                                const nodeHeader =
634                                    isGeneratedIdentifier(this) ? "GeneratedIdentifier" :
635                                    isIdentifier(this) ? `Identifier '${idText(this)}'` :
636                                    isPrivateIdentifier(this) ? `PrivateIdentifier '${idText(this)}'` :
637                                    isStringLiteral(this) ? `StringLiteral ${JSON.stringify(this.text.length < 10 ? this.text : this.text.slice(10) + "...")}` :
638                                    isNumericLiteral(this) ? `NumericLiteral ${this.text}` :
639                                    isBigIntLiteral(this) ? `BigIntLiteral ${this.text}n` :
640                                    isTypeParameterDeclaration(this) ? "TypeParameterDeclaration" :
641                                    isParameter(this) ? "ParameterDeclaration" :
642                                    isConstructorDeclaration(this) ? "ConstructorDeclaration" :
643                                    isGetAccessorDeclaration(this) ? "GetAccessorDeclaration" :
644                                    isSetAccessorDeclaration(this) ? "SetAccessorDeclaration" :
645                                    isCallSignatureDeclaration(this) ? "CallSignatureDeclaration" :
646                                    isConstructSignatureDeclaration(this) ? "ConstructSignatureDeclaration" :
647                                    isIndexSignatureDeclaration(this) ? "IndexSignatureDeclaration" :
648                                    isTypePredicateNode(this) ? "TypePredicateNode" :
649                                    isTypeReferenceNode(this) ? "TypeReferenceNode" :
650                                    isFunctionTypeNode(this) ? "FunctionTypeNode" :
651                                    isConstructorTypeNode(this) ? "ConstructorTypeNode" :
652                                    isTypeQueryNode(this) ? "TypeQueryNode" :
653                                    isTypeLiteralNode(this) ? "TypeLiteralNode" :
654                                    isArrayTypeNode(this) ? "ArrayTypeNode" :
655                                    isTupleTypeNode(this) ? "TupleTypeNode" :
656                                    isOptionalTypeNode(this) ? "OptionalTypeNode" :
657                                    isRestTypeNode(this) ? "RestTypeNode" :
658                                    isUnionTypeNode(this) ? "UnionTypeNode" :
659                                    isIntersectionTypeNode(this) ? "IntersectionTypeNode" :
660                                    isConditionalTypeNode(this) ? "ConditionalTypeNode" :
661                                    isInferTypeNode(this) ? "InferTypeNode" :
662                                    isParenthesizedTypeNode(this) ? "ParenthesizedTypeNode" :
663                                    isThisTypeNode(this) ? "ThisTypeNode" :
664                                    isTypeOperatorNode(this) ? "TypeOperatorNode" :
665                                    isIndexedAccessTypeNode(this) ? "IndexedAccessTypeNode" :
666                                    isMappedTypeNode(this) ? "MappedTypeNode" :
667                                    isLiteralTypeNode(this) ? "LiteralTypeNode" :
668                                    isNamedTupleMember(this) ? "NamedTupleMember" :
669                                    isImportTypeNode(this) ? "ImportTypeNode" :
670                                    formatSyntaxKind(this.kind);
671                                return `${nodeHeader}${this.flags ? ` (${formatNodeFlags(this.flags)})` : ""}`;
672                            }
673                        },
674                        __debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } },
675                        __debugNodeFlags: { get(this: Node) { return formatNodeFlags(this.flags); } },
676                        __debugModifierFlags: { get(this: Node) { return formatModifierFlags(getEffectiveModifierFlagsNoCache(this)); } },
677                        __debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
678                        __debugIsParseTreeNode: { get(this: Node) { return isParseTreeNode(this); } },
679                        __debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
680                        __debugGetText: {
681                            value(this: Node, includeTrivia?: boolean) {
682                                if (nodeIsSynthesized(this)) return "";
683                                // avoid recomputing
684                                const map = getWeakNodeTextMap();
685                                let text = map?.get(this);
686                                if (text === undefined) {
687                                    const parseNode = getParseTreeNode(this);
688                                    const sourceFile = parseNode && getSourceFileOfNode(parseNode);
689                                    text = sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
690                                    map?.set(this, text);
691                                }
692                                return text;
693                            }
694                        }
695                    });
696                }
697            }
698
699            // attempt to load extended debugging information
700            try {
701                if (sys && sys.require) {
702                    const basePath = getDirectoryPath(resolvePath(sys.getExecutingFilePath()));
703                    const result = sys.require(basePath, "./compiler-debug") as RequireResult<ExtendedDebugModule>;
704                    if (!result.error) {
705                        result.module.init(ts);
706                        extendedDebugModule = result.module;
707                    }
708                }
709            }
710            catch {
711                // do nothing
712            }
713
714            isDebugInfoEnabled = true;
715        }
716
717        function formatDeprecationMessage(name: string, error: boolean | undefined, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) {
718            let deprecationMessage = error ? "DeprecationError: " : "DeprecationWarning: ";
719            deprecationMessage += `'${name}' `;
720            deprecationMessage += since ? `has been deprecated since v${since}` : "is deprecated";
721            deprecationMessage += error ? " and can no longer be used." : errorAfter ? ` and will no longer be usable after v${errorAfter}.` : ".";
722            deprecationMessage += message ? ` ${formatStringFromArgs(message, [name], 0)}` : "";
723            return deprecationMessage;
724        }
725
726        function createErrorDeprecation(name: string, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) {
727            const deprecationMessage = formatDeprecationMessage(name, /*error*/ true, errorAfter, since, message);
728            return () => {
729                throw new TypeError(deprecationMessage);
730            };
731        }
732
733        function createWarningDeprecation(name: string, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) {
734            let hasWrittenDeprecation = false;
735            return () => {
736                if (enableDeprecationWarnings && !hasWrittenDeprecation) {
737                    log.warn(formatDeprecationMessage(name, /*error*/ false, errorAfter, since, message));
738                    hasWrittenDeprecation = true;
739                }
740            };
741        }
742
743        export function createDeprecation(name: string, options: DeprecationOptions & { error: true }): () => never;
744        export function createDeprecation(name: string, options?: DeprecationOptions): () => void;
745        export function createDeprecation(name: string, options: DeprecationOptions = {}) {
746            const version = typeof options.typeScriptVersion === "string" ? new Version(options.typeScriptVersion) : options.typeScriptVersion ?? getTypeScriptVersion();
747            const errorAfter = typeof options.errorAfter === "string" ? new Version(options.errorAfter) : options.errorAfter;
748            const warnAfter = typeof options.warnAfter === "string" ? new Version(options.warnAfter) : options.warnAfter;
749            const since = typeof options.since === "string" ? new Version(options.since) : options.since ?? warnAfter;
750            const error = options.error || errorAfter && version.compareTo(errorAfter) <= 0;
751            const warn = !warnAfter || version.compareTo(warnAfter) >= 0;
752            return error ? createErrorDeprecation(name, errorAfter, since, options.message) :
753                warn ? createWarningDeprecation(name, errorAfter, since, options.message) :
754                noop;
755        }
756
757        function wrapFunction<F extends (...args: any[]) => any>(deprecation: () => void, func: F): F {
758            return function (this: unknown) {
759                deprecation();
760                return func.apply(this, arguments);
761            } as F;
762        }
763
764        export function deprecate<F extends (...args: any[]) => any>(func: F, options?: DeprecationOptions): F {
765            const deprecation = createDeprecation(options?.name ?? getFunctionName(func), options);
766            return wrapFunction(deprecation, func);
767        }
768
769        export function formatVariance(varianceFlags: VarianceFlags) {
770            const variance = varianceFlags & VarianceFlags.VarianceMask;
771            let result =
772                variance === VarianceFlags.Invariant ? "in out" :
773                variance === VarianceFlags.Bivariant ? "[bivariant]" :
774                variance === VarianceFlags.Contravariant ? "in" :
775                variance === VarianceFlags.Covariant ? "out" :
776                variance === VarianceFlags.Independent ? "[independent]" : "";
777            if (varianceFlags & VarianceFlags.Unmeasurable) {
778                result += " (unmeasurable)";
779            }
780            else if (varianceFlags & VarianceFlags.Unreliable) {
781                result += " (unreliable)";
782            }
783            return result;
784        }
785
786        export type DebugType = Type & { __debugTypeToString(): string }; // eslint-disable-line @typescript-eslint/naming-convention
787        export class DebugTypeMapper {
788            declare kind: TypeMapKind;
789            __debugToString(): string { // eslint-disable-line @typescript-eslint/naming-convention
790                type<TypeMapper>(this);
791                switch (this.kind) {
792                    case TypeMapKind.Function: return this.debugInfo?.() || "(function mapper)";
793                    case TypeMapKind.Simple: return `${(this.source as DebugType).__debugTypeToString()} -> ${(this.target as DebugType).__debugTypeToString()}`;
794                    case TypeMapKind.Array: return zipWith<DebugType, DebugType | string, unknown>(
795                        this.sources as readonly DebugType[],
796                        this.targets as readonly DebugType[] || map(this.sources, () => "any"),
797                        (s, t) => `${s.__debugTypeToString()} -> ${typeof t === "string" ? t : t.__debugTypeToString()}`).join(", ");
798                    case TypeMapKind.Deferred: return zipWith(
799                        this.sources,
800                        this.targets,
801                        (s, t) => `${(s as DebugType).__debugTypeToString()} -> ${(t() as DebugType).__debugTypeToString()}`).join(", ");
802                    case TypeMapKind.Merged:
803                    case TypeMapKind.Composite: return `m1: ${(this.mapper1 as unknown as DebugTypeMapper).__debugToString().split("\n").join("\n    ")}
804m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n").join("\n    ")}`;
805                    default: return assertNever(this);
806                }
807            }
808        }
809
810        export function attachDebugPrototypeIfDebug(mapper: TypeMapper): TypeMapper {
811            if (isDebugging) {
812                return Object.setPrototypeOf(mapper, DebugTypeMapper.prototype);
813            }
814            return mapper;
815        }
816    }
817}
818