Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Relocate Debug namespace to reduce duplication (#31524)
- Loading branch information
Showing
13 changed files
with
274 additions
and
286 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,261 @@ | ||
/* @internal */ | ||
namespace ts { | ||
export namespace Debug { | ||
export let currentAssertionLevel = AssertionLevel.None; | ||
export let isDebugging = false; | ||
|
||
export function shouldAssert(level: AssertionLevel): boolean { | ||
return currentAssertionLevel >= level; | ||
} | ||
|
||
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): void { | ||
if (!expression) { | ||
if (verboseDebugInfo) { | ||
message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo()); | ||
} | ||
fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert); | ||
} | ||
} | ||
|
||
export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string): void { | ||
if (a !== b) { | ||
const message = msg ? msg2 ? `${msg} ${msg2}` : msg : ""; | ||
fail(`Expected ${a} === ${b}. ${message}`); | ||
} | ||
} | ||
|
||
export function assertLessThan(a: number, b: number, msg?: string): void { | ||
if (a >= b) { | ||
fail(`Expected ${a} < ${b}. ${msg || ""}`); | ||
} | ||
} | ||
|
||
export function assertLessThanOrEqual(a: number, b: number): void { | ||
if (a > b) { | ||
fail(`Expected ${a} <= ${b}`); | ||
} | ||
} | ||
|
||
export function assertGreaterThanOrEqual(a: number, b: number): void { | ||
if (a < b) { | ||
fail(`Expected ${a} >= ${b}`); | ||
} | ||
} | ||
|
||
export function fail(message?: string, stackCrawlMark?: AnyFunction): never { | ||
debugger; | ||
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure."); | ||
if ((<any>Error).captureStackTrace) { | ||
(<any>Error).captureStackTrace(e, stackCrawlMark || fail); | ||
} | ||
throw e; | ||
} | ||
|
||
export function assertDefined<T>(value: T | null | undefined, message?: string): T { | ||
if (value === undefined || value === null) return fail(message); | ||
return value; | ||
} | ||
|
||
export function assertEachDefined<T, A extends ReadonlyArray<T>>(value: A, message?: string): A { | ||
for (const v of value) { | ||
assertDefined(v, message); | ||
} | ||
return value; | ||
} | ||
|
||
export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never { | ||
const detail = typeof member === "object" && "kind" in member && "pos" in member && formatSyntaxKind ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member); | ||
return fail(`${message} ${detail}`, stackCrawlMark || assertNever); | ||
} | ||
|
||
export function getFunctionName(func: AnyFunction) { | ||
if (typeof func !== "function") { | ||
return ""; | ||
} | ||
else if (func.hasOwnProperty("name")) { | ||
return (<any>func).name; | ||
} | ||
else { | ||
const text = Function.prototype.toString.call(func); | ||
const match = /^function\s+([\w\$]+)\s*\(/.exec(text); | ||
return match ? match[1] : ""; | ||
} | ||
} | ||
|
||
export function formatSymbol(symbol: Symbol): string { | ||
return `{ name: ${unescapeLeadingUnderscores(symbol.escapedName)}; flags: ${formatSymbolFlags(symbol.flags)}; declarations: ${map(symbol.declarations, node => formatSyntaxKind(node.kind))} }`; | ||
} | ||
|
||
/** | ||
* Formats an enum value as a string for debugging and debug assertions. | ||
*/ | ||
export function formatEnum(value = 0, enumObject: any, isFlags?: boolean) { | ||
const members = getEnumMembers(enumObject); | ||
if (value === 0) { | ||
return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0"; | ||
} | ||
if (isFlags) { | ||
let result = ""; | ||
let remainingFlags = value; | ||
for (let i = members.length - 1; i >= 0 && remainingFlags !== 0; i--) { | ||
const [enumValue, enumName] = members[i]; | ||
if (enumValue !== 0 && (remainingFlags & enumValue) === enumValue) { | ||
remainingFlags &= ~enumValue; | ||
result = `${enumName}${result ? "|" : ""}${result}`; | ||
} | ||
} | ||
if (remainingFlags === 0) { | ||
return result; | ||
} | ||
} | ||
else { | ||
for (const [enumValue, enumName] of members) { | ||
if (enumValue === value) { | ||
return enumName; | ||
} | ||
} | ||
} | ||
return value.toString(); | ||
} | ||
|
||
function getEnumMembers(enumObject: any) { | ||
const result: [number, string][] = []; | ||
for (const name in enumObject) { | ||
const value = enumObject[name]; | ||
if (typeof value === "number") { | ||
result.push([value, name]); | ||
} | ||
} | ||
|
||
return stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0])); | ||
} | ||
|
||
export function formatSyntaxKind(kind: SyntaxKind | undefined): string { | ||
return formatEnum(kind, (<any>ts).SyntaxKind, /*isFlags*/ false); | ||
} | ||
|
||
export function formatNodeFlags(flags: NodeFlags | undefined): string { | ||
return formatEnum(flags, (<any>ts).NodeFlags, /*isFlags*/ true); | ||
} | ||
|
||
export function formatModifierFlags(flags: ModifierFlags | undefined): string { | ||
return formatEnum(flags, (<any>ts).ModifierFlags, /*isFlags*/ true); | ||
} | ||
|
||
export function formatTransformFlags(flags: TransformFlags | undefined): string { | ||
return formatEnum(flags, (<any>ts).TransformFlags, /*isFlags*/ true); | ||
} | ||
|
||
export function formatEmitFlags(flags: EmitFlags | undefined): string { | ||
return formatEnum(flags, (<any>ts).EmitFlags, /*isFlags*/ true); | ||
} | ||
|
||
export function formatSymbolFlags(flags: SymbolFlags | undefined): string { | ||
return formatEnum(flags, (<any>ts).SymbolFlags, /*isFlags*/ true); | ||
} | ||
|
||
export function formatTypeFlags(flags: TypeFlags | undefined): string { | ||
return formatEnum(flags, (<any>ts).TypeFlags, /*isFlags*/ true); | ||
} | ||
|
||
export function formatObjectFlags(flags: ObjectFlags | undefined): string { | ||
return formatEnum(flags, (<any>ts).ObjectFlags, /*isFlags*/ true); | ||
} | ||
|
||
export function failBadSyntaxKind(node: Node, message?: string): never { | ||
return fail( | ||
`${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`, | ||
failBadSyntaxKind); | ||
} | ||
|
||
export const assertEachNode = shouldAssert(AssertionLevel.Normal) | ||
? (nodes: Node[], test: (node: Node) => boolean, message?: string): void => assert( | ||
test === undefined || every(nodes, test), | ||
message || "Unexpected node.", | ||
() => `Node array did not pass test '${getFunctionName(test)}'.`, | ||
assertEachNode) | ||
: noop; | ||
|
||
export const assertNode = shouldAssert(AssertionLevel.Normal) | ||
? (node: Node | undefined, test: ((node: Node | undefined) => boolean) | undefined, message?: string): void => assert( | ||
test === undefined || test(node), | ||
message || "Unexpected node.", | ||
() => `Node ${formatSyntaxKind(node!.kind)} did not pass test '${getFunctionName(test!)}'.`, | ||
assertNode) | ||
: noop; | ||
|
||
export const assertOptionalNode = shouldAssert(AssertionLevel.Normal) | ||
? (node: Node, test: (node: Node) => boolean, message?: string): void => assert( | ||
test === undefined || node === undefined || test(node), | ||
message || "Unexpected node.", | ||
() => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`, | ||
assertOptionalNode) | ||
: noop; | ||
|
||
export const assertOptionalToken = shouldAssert(AssertionLevel.Normal) | ||
? (node: Node, kind: SyntaxKind, message?: string): void => assert( | ||
kind === undefined || node === undefined || node.kind === kind, | ||
message || "Unexpected node.", | ||
() => `Node ${formatSyntaxKind(node.kind)} was not a '${formatSyntaxKind(kind)}' token.`, | ||
assertOptionalToken) | ||
: noop; | ||
|
||
export const assertMissingNode = shouldAssert(AssertionLevel.Normal) | ||
? (node: Node, message?: string): void => assert( | ||
node === undefined, | ||
message || "Unexpected node.", | ||
() => `Node ${formatSyntaxKind(node.kind)} was unexpected'.`, | ||
assertMissingNode) | ||
: noop; | ||
|
||
let isDebugInfoEnabled = false; | ||
|
||
/** | ||
* Injects debug information into frequently used types. | ||
*/ | ||
export function enableDebugInfo() { | ||
if (isDebugInfoEnabled) return; | ||
|
||
// Add additional properties in debug mode to assist with debugging. | ||
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, { | ||
__debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } } | ||
}); | ||
|
||
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, { | ||
__debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } }, | ||
__debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((<ObjectType>this).objectFlags) : ""; } }, | ||
__debugTypeToString: { value(this: Type) { return this.checker.typeToString(this); } }, | ||
}); | ||
|
||
const nodeConstructors = [ | ||
objectAllocator.getNodeConstructor(), | ||
objectAllocator.getIdentifierConstructor(), | ||
objectAllocator.getTokenConstructor(), | ||
objectAllocator.getSourceFileConstructor() | ||
]; | ||
|
||
for (const ctor of nodeConstructors) { | ||
if (!ctor.prototype.hasOwnProperty("__debugKind")) { | ||
Object.defineProperties(ctor.prototype, { | ||
__debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } }, | ||
__debugNodeFlags: { get(this: Node) { return formatNodeFlags(this.flags); } }, | ||
__debugModifierFlags: { get(this: Node) { return formatModifierFlags(getModifierFlagsNoCache(this)); } }, | ||
__debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } }, | ||
__debugIsParseTreeNode: { get(this: Node) { return isParseTreeNode(this); } }, | ||
__debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } }, | ||
__debugGetText: { | ||
value(this: Node, includeTrivia?: boolean) { | ||
if (nodeIsSynthesized(this)) return ""; | ||
const parseNode = getParseTreeNode(this); | ||
const sourceFile = parseNode && getSourceFileOfNode(parseNode); | ||
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : ""; | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
|
||
isDebugInfoEnabled = true; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
|
||
"files": [ | ||
"core.ts", | ||
"debug.ts", | ||
"performance.ts", | ||
"semver.ts", | ||
|
||
|
Oops, something went wrong.