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