diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts index 70c7bf1799891..dac4f01c8292e 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -13,7 +13,6 @@ import { FlowSwitchClause, getEffectiveModifierFlagsNoCache, getEmitFlags, - getOwnKeys, getParseTreeNode, getSourceFileOfNode, getSourceTextOfNodeFromSourceFile, @@ -60,7 +59,6 @@ import { isUnionTypeNode, LiteralType, map, - MatchingKeys, maxBy, ModifierFlags, Node, @@ -68,7 +66,6 @@ import { NodeCheckFlags, NodeFlags, nodeIsSynthesized, - noop, objectAllocator, ObjectFlags, ObjectType, @@ -111,14 +108,22 @@ export interface LoggingHost { /** @internal */ export namespace Debug { - /* eslint-disable prefer-const */ - let currentAssertionLevel = AssertionLevel.None; - export let currentLogLevel = LogLevel.Warning; - export let isDebugging = false; - export let loggingHost: LoggingHost | undefined; - /* eslint-enable prefer-const */ + // Why var? It avoids TDZ checks in the runtime which can be costly. + // See: https://github.com/microsoft/TypeScript/issues/52924 + // TODO(jakebailey): restore to let/const once Debug is no longer a namespace. + /* eslint-disable no-var */ + var currentAssertionLevel = AssertionLevel.None; + export var currentLogLevel = LogLevel.Warning; + export var isDebugging = false; + export var loggingHost: LoggingHost | undefined; - type AssertionKeys = MatchingKeys; + var enumMemberCache = new Map>(); + + var isDebugInfoEnabled = false; + + var flowNodeProto: FlowNode | undefined; + var nodeArrayProto: NodeArray | undefined; + /* eslint-enable no-var */ export function shouldLog(level: LogLevel): boolean { return currentLogLevel <= level; @@ -152,47 +157,18 @@ export namespace Debug { } } - const assertionCache: Partial> = {}; - export function getAssertionLevel() { return currentAssertionLevel; } export function setAssertionLevel(level: AssertionLevel) { - const prevAssertionLevel = currentAssertionLevel; currentAssertionLevel = level; - - if (level > prevAssertionLevel) { - // restore assertion functions for the current assertion level (see `shouldAssertFunction`). - for (const key of getOwnKeys(assertionCache) as AssertionKeys[]) { - const cachedFunc = assertionCache[key]; - if (cachedFunc !== undefined && Debug[key] !== cachedFunc.assertion && level >= cachedFunc.level) { - (Debug as any)[key] = cachedFunc; - assertionCache[key] = undefined; - } - } - } } export function shouldAssert(level: AssertionLevel): boolean { return currentAssertionLevel >= level; } - /** - * Tests whether an assertion function should be executed. If it shouldn't, it is cached and replaced with `ts.noop`. - * Replaced assertion functions are restored when `Debug.setAssertionLevel` is set to a high enough level. - * @param level The minimum assertion level required. - * @param name The name of the current assertion function. - */ - function shouldAssertFunction(level: AssertionLevel, name: K): boolean { - if (!shouldAssert(level)) { - assertionCache[name] = { level, assertion: Debug[name] }; - (Debug as any)[name] = noop; - return false; - } - return true; - } - export function fail(message?: string, stackCrawlMark?: AnyFunction): never { // eslint-disable-next-line no-debugger debugger; @@ -281,7 +257,7 @@ export namespace Debug { export function assertEachNode(nodes: readonly T[] | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is readonly U[] | undefined; export function assertEachNode(nodes: readonly Node[], test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; export function assertEachNode(nodes: readonly Node[] | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertEachNode")) { + if (shouldAssert(AssertionLevel.Normal)) { assert( test === undefined || every(nodes, test), message || "Unexpected node.", @@ -294,7 +270,7 @@ export namespace Debug { export function assertNode(node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U; export function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; export function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertNode")) { + if (shouldAssert(AssertionLevel.Normal)) { assert( node !== undefined && (test === undefined || test(node)), message || "Unexpected node.", @@ -307,7 +283,7 @@ export namespace Debug { export function assertNotNode(node: T | undefined, test: (node: Node) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is Exclude; export function assertNotNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; export function assertNotNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertNotNode")) { + if (shouldAssert(AssertionLevel.Normal)) { assert( node === undefined || test === undefined || !test(node), message || "Unexpected node.", @@ -321,7 +297,7 @@ export namespace Debug { export function assertOptionalNode(node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U | undefined; export function assertOptionalNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; export function assertOptionalNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalNode")) { + if (shouldAssert(AssertionLevel.Normal)) { assert( test === undefined || node === undefined || test(node), message || "Unexpected node.", @@ -335,7 +311,7 @@ export namespace Debug { export function assertOptionalToken(node: T | undefined, kind: K, message?: string, stackCrawlMark?: AnyFunction): asserts node is Extract | undefined; export function assertOptionalToken(node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction): void; export function assertOptionalToken(node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalToken")) { + if (shouldAssert(AssertionLevel.Normal)) { assert( kind === undefined || node === undefined || node.kind === kind, message || "Unexpected node.", @@ -347,7 +323,7 @@ export namespace Debug { export function assertMissingNode(node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction): asserts node is undefined; export function assertMissingNode(node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertMissingNode")) { + if (shouldAssert(AssertionLevel.Normal)) { assert( node === undefined, message || "Unexpected node.", @@ -417,8 +393,6 @@ export namespace Debug { return value.toString(); } - const enumMemberCache = new Map>(); - function getEnumMembers(enumObject: any) { // Assuming enum objects do not change at runtime, we can cache the enum members list // to reuse later. This saves us from reconstructing this each and every time we call @@ -509,10 +483,6 @@ export namespace Debug { return formatEnum(facts, (ts as any).TypeFacts, /*isFlags*/ true); } - let isDebugInfoEnabled = false; - - let flowNodeProto: FlowNode | undefined; - function attachFlowNodeDebugInfoWorker(flowNode: FlowNode) { if (!("__debugFlowFlags" in flowNode)) { // eslint-disable-line local/no-in-operator Object.defineProperties(flowNode, { @@ -568,8 +538,6 @@ export namespace Debug { return flowNode; } - let nodeArrayProto: NodeArray | undefined; - function attachNodeArrayDebugInfoWorker(array: NodeArray) { if (!("__tsDebuggerDisplay" in array)) { // eslint-disable-line local/no-in-operator Object.defineProperties(array, {