diff --git a/Herebyfile.mjs b/Herebyfile.mjs index 08f790ae624a9..4e39cf9a41788 100644 --- a/Herebyfile.mjs +++ b/Herebyfile.mjs @@ -364,15 +364,6 @@ function entrypointBuildTask(options) { return { build, bundle, shim, main, watch }; } -const { main: compilerDebug } = entrypointBuildTask({ - name: "compiler-debug", - buildDeps: [generateDiagnostics], - project: "src/debug", - srcEntrypoint: "./src/debug/compilerDebug.ts", - builtEntrypoint: "./built/local/debug/compilerDebug.js", - output: "./built/local/compiler-debug.js", -}); - const { main: tsc, watch: watchTsc } = entrypointBuildTask({ name: "tsc", @@ -382,7 +373,7 @@ const { main: tsc, watch: watchTsc } = entrypointBuildTask({ srcEntrypoint: "./src/tsc/tsc.ts", builtEntrypoint: "./built/local/tsc/tsc.js", output: "./built/local/tsc.js", - mainDeps: [generateLibs, compilerDebug], + mainDeps: [generateLibs], }); export { tsc, watchTsc }; @@ -395,7 +386,7 @@ const { main: services, build: buildServices, watch: watchServices } = entrypoin srcEntrypoint: "./src/typescript/typescript.ts", builtEntrypoint: "./built/local/typescript/typescript.js", output: "./built/local/typescript.js", - mainDeps: [generateLibs, compilerDebug], + mainDeps: [generateLibs], bundlerOptions: { exportIsTsObject: true }, }); export { services, watchServices }; @@ -420,7 +411,7 @@ const { main: tsserver, watch: watchTsserver } = entrypointBuildTask({ srcEntrypoint: "./src/tsserver/server.ts", builtEntrypoint: "./built/local/tsserver/server.js", output: "./built/local/tsserver.js", - mainDeps: [generateLibs, compilerDebug], + mainDeps: [generateLibs], // Even though this seems like an exectuable, so could be the default CJS, // this is used in the browser too. Do the same thing that we do for our // libraries and generate an IIFE with name `ts`, as to not pollute the global @@ -453,7 +444,7 @@ const { main: lssl, build: buildLssl, watch: watchLssl } = entrypointBuildTask({ srcEntrypoint: "./src/tsserverlibrary/tsserverlibrary.ts", builtEntrypoint: "./built/local/tsserverlibrary/tsserverlibrary.js", output: "./built/local/tsserverlibrary.js", - mainDeps: [generateLibs, compilerDebug], + mainDeps: [generateLibs], bundlerOptions: { exportIsTsObject: true }, }); export { lssl, watchLssl }; @@ -485,7 +476,7 @@ const { main: tests, watch: watchTests } = entrypointBuildTask({ srcEntrypoint: "./src/testRunner/_namespaces/Harness.ts", builtEntrypoint: "./built/local/testRunner/runner.js", output: testRunner, - mainDeps: [generateLibs, compilerDebug], + mainDeps: [generateLibs], bundlerOptions: { // Ensure we never drop any dead code, which might be helpful while debugging. treeShaking: false, diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts index 591cd8945d28f..adb2d539c8a4f 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -1,18 +1,18 @@ import * as ts from "./_namespaces/ts"; import { - AnyFunction, AssertionLevel, BigIntLiteralType, CheckMode, compareValues, EmitFlags, every, FlowFlags, FlowNode, - FlowNodeBase, formatStringFromArgs, getDirectoryPath, getEffectiveModifierFlagsNoCache, getEmitFlags, getOwnKeys, + AnyFunction, AssertionLevel, BigIntLiteralType, CheckMode, compareValues, EmitFlags, every, FlowFlags, FlowLabel, FlowNode, + FlowNodeBase, FlowSwitchClause, formatStringFromArgs, getEffectiveModifierFlagsNoCache, getEmitFlags, getOwnKeys, getParseTreeNode, getSourceFileOfNode, getSourceTextOfNodeFromSourceFile, hasProperty, idText, IntrinsicType, isArrayTypeNode, isBigIntLiteral, isCallSignatureDeclaration, isConditionalTypeNode, isConstructorDeclaration, - isConstructorTypeNode, isConstructSignatureDeclaration, isFunctionTypeNode, isGeneratedIdentifier, + isConstructorTypeNode, isConstructSignatureDeclaration, isDefaultClause, isFunctionTypeNode, isGeneratedIdentifier, isGetAccessorDeclaration, isIdentifier, isImportTypeNode, isIndexedAccessTypeNode, isIndexSignatureDeclaration, isInferTypeNode, isIntersectionTypeNode, isLiteralTypeNode, isMappedTypeNode, isNamedTupleMember, isNumericLiteral, isOptionalTypeNode, isParameter, isParenthesizedTypeNode, isParseTreeNode, isPrivateIdentifier, isRestTypeNode, isSetAccessorDeclaration, isStringLiteral, isThisTypeNode, isTupleTypeNode, isTypeLiteralNode, isTypeOperatorNode, isTypeParameterDeclaration, isTypePredicateNode, isTypeQueryNode, isTypeReferenceNode, isUnionTypeNode, LiteralType, map, Map, MatchingKeys, ModifierFlags, Node, NodeArray, NodeFlags, nodeIsSynthesized, noop, objectAllocator, - ObjectFlags, ObjectType, RelationComparisonResult, RequireResult, resolvePath, Signature, SignatureCheckMode, - SignatureFlags, SnippetKind, SortedReadonlyArray, stableSort, Symbol, SymbolFlags, symbolName, SyntaxKind, sys, + ObjectFlags, ObjectType, RelationComparisonResult, Set, Signature, SignatureCheckMode, + SignatureFlags, SnippetKind, SortedReadonlyArray, stableSort, Symbol, SymbolFlags, symbolName, SyntaxKind, TransformFlags, Type, TypeFacts, TypeFlags, TypeMapKind, TypeMapper, unescapeLeadingUnderscores, VarianceFlags, version, Version, zipWith, } from "./_namespaces/ts"; @@ -434,29 +434,6 @@ export namespace Debug { let isDebugInfoEnabled = false; - interface ExtendedDebugModule { - init(_ts: typeof ts): void; - formatControlFlowGraph(flowNode: FlowNode): string; - } - - let extendedDebugModule: ExtendedDebugModule | undefined; - - function extendedDebug() { - enableDebugInfo(); - if (!extendedDebugModule) { - throw new Error("Debugging helpers could not be loaded."); - } - return extendedDebugModule; - } - - export function printControlFlowGraph(flowNode: FlowNode) { - return console.log(formatControlFlowGraph(flowNode)); - } - - export function formatControlFlowGraph(flowNode: FlowNode) { - return extendedDebug().formatControlFlowGraph(flowNode); - } - let flowNodeProto: FlowNodeBase | undefined; function attachFlowNodeDebugInfoWorker(flowNode: FlowNodeBase) { @@ -717,21 +694,6 @@ export namespace Debug { } } - // attempt to load extended debugging information - try { - if (sys && sys.require) { - const basePath = getDirectoryPath(resolvePath(sys.getExecutingFilePath())); - const result = sys.require(basePath, "./compiler-debug") as RequireResult; - if (!result.error) { - result.module.init(ts); - extendedDebugModule = result.module; - } - } - } - catch { - // do nothing - } - isDebugInfoEnabled = true; } @@ -834,4 +796,385 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") } return mapper; } + + export function printControlFlowGraph(flowNode: FlowNode) { + return console.log(formatControlFlowGraph(flowNode)); + } + + export function formatControlFlowGraph(flowNode: FlowNode) { + let nextDebugFlowId = -1; + + function getDebugFlowNodeId(f: FlowNode) { + if (!f.id) { + f.id = nextDebugFlowId; + nextDebugFlowId--; + } + return f.id; + } + + const enum BoxCharacter { + lr = "─", + ud = "│", + dr = "╭", + dl = "╮", + ul = "╯", + ur = "╰", + udr = "├", + udl = "┤", + dlr = "┬", + ulr = "┴", + udlr = "╫", + } + + const enum Connection { + None = 0, + Up = 1 << 0, + Down = 1 << 1, + Left = 1 << 2, + Right = 1 << 3, + + UpDown = Up | Down, + LeftRight = Left | Right, + UpLeft = Up | Left, + UpRight = Up | Right, + DownLeft = Down | Left, + DownRight = Down | Right, + UpDownLeft = UpDown | Left, + UpDownRight = UpDown | Right, + UpLeftRight = Up | LeftRight, + DownLeftRight = Down | LeftRight, + UpDownLeftRight = UpDown | LeftRight, + + NoChildren = 1 << 4, + } + + interface FlowGraphNode { + id: number; + flowNode: FlowNode; + edges: FlowGraphEdge[]; + text: string; + lane: number; + endLane: number; + level: number; + circular: boolean | "circularity"; + } + + interface FlowGraphEdge { + source: FlowGraphNode; + target: FlowGraphNode; + } + + const hasAntecedentFlags = + FlowFlags.Assignment | + FlowFlags.Condition | + FlowFlags.SwitchClause | + FlowFlags.ArrayMutation | + FlowFlags.Call | + FlowFlags.ReduceLabel; + + const hasNodeFlags = + FlowFlags.Start | + FlowFlags.Assignment | + FlowFlags.Call | + FlowFlags.Condition | + FlowFlags.ArrayMutation; + + const links: Record = Object.create(/*o*/ null); // eslint-disable-line no-null/no-null + const nodes: FlowGraphNode[] = []; + const edges: FlowGraphEdge[] = []; + const root = buildGraphNode(flowNode, new Set()); + for (const node of nodes) { + node.text = renderFlowNode(node.flowNode, node.circular); + computeLevel(node); + } + + const height = computeHeight(root); + const columnWidths = computeColumnWidths(height); + computeLanes(root, 0); + return renderGraph(); + + function isFlowSwitchClause(f: FlowNode): f is FlowSwitchClause { + return !!(f.flags & FlowFlags.SwitchClause); + } + + function hasAntecedents(f: FlowNode): f is FlowLabel & { antecedents: FlowNode[] } { + return !!(f.flags & FlowFlags.Label) && !!(f as FlowLabel).antecedents; + } + + function hasAntecedent(f: FlowNode): f is Extract { + return !!(f.flags & hasAntecedentFlags); + } + + function hasNode(f: FlowNode): f is Extract { + return !!(f.flags & hasNodeFlags); + } + + function getChildren(node: FlowGraphNode) { + const children: FlowGraphNode[] = []; + for (const edge of node.edges) { + if (edge.source === node) { + children.push(edge.target); + } + } + return children; + } + + function getParents(node: FlowGraphNode) { + const parents: FlowGraphNode[] = []; + for (const edge of node.edges) { + if (edge.target === node) { + parents.push(edge.source); + } + } + return parents; + } + + function buildGraphNode(flowNode: FlowNode, seen: Set): FlowGraphNode { + const id = getDebugFlowNodeId(flowNode); + let graphNode = links[id]; + if (graphNode && seen.has(flowNode)) { + graphNode.circular = true; + graphNode = { + id: -1, + flowNode, + edges: [], + text: "", + lane: -1, + endLane: -1, + level: -1, + circular: "circularity" + }; + nodes.push(graphNode); + return graphNode; + } + seen.add(flowNode); + if (!graphNode) { + links[id] = graphNode = { id, flowNode, edges: [], text: "", lane: -1, endLane: -1, level: -1, circular: false }; + nodes.push(graphNode); + if (hasAntecedents(flowNode)) { + for (const antecedent of flowNode.antecedents) { + buildGraphEdge(graphNode, antecedent, seen); + } + } + else if (hasAntecedent(flowNode)) { + buildGraphEdge(graphNode, flowNode.antecedent, seen); + } + } + seen.delete(flowNode); + return graphNode; + } + + function buildGraphEdge(source: FlowGraphNode, antecedent: FlowNode, seen: Set) { + const target = buildGraphNode(antecedent, seen); + const edge: FlowGraphEdge = { source, target }; + edges.push(edge); + source.edges.push(edge); + target.edges.push(edge); + } + + function computeLevel(node: FlowGraphNode): number { + if (node.level !== -1) { + return node.level; + } + let level = 0; + for (const parent of getParents(node)) { + level = Math.max(level, computeLevel(parent) + 1); + } + return node.level = level; + } + + function computeHeight(node: FlowGraphNode): number { + let height = 0; + for (const child of getChildren(node)) { + height = Math.max(height, computeHeight(child)); + } + return height + 1; + } + + function computeColumnWidths(height: number) { + const columns: number[] = fill(Array(height), 0); + for (const node of nodes) { + columns[node.level] = Math.max(columns[node.level], node.text.length); + } + return columns; + } + + function computeLanes(node: FlowGraphNode, lane: number) { + if (node.lane === -1) { + node.lane = lane; + node.endLane = lane; + const children = getChildren(node); + for (let i = 0; i < children.length; i++) { + if (i > 0) lane++; + const child = children[i]; + computeLanes(child, lane); + if (child.endLane > node.endLane) { + lane = child.endLane; + } + } + node.endLane = lane; + } + } + + function getHeader(flags: FlowFlags) { + if (flags & FlowFlags.Start) return "Start"; + if (flags & FlowFlags.BranchLabel) return "Branch"; + if (flags & FlowFlags.LoopLabel) return "Loop"; + if (flags & FlowFlags.Assignment) return "Assignment"; + if (flags & FlowFlags.TrueCondition) return "True"; + if (flags & FlowFlags.FalseCondition) return "False"; + if (flags & FlowFlags.SwitchClause) return "SwitchClause"; + if (flags & FlowFlags.ArrayMutation) return "ArrayMutation"; + if (flags & FlowFlags.Call) return "Call"; + if (flags & FlowFlags.ReduceLabel) return "ReduceLabel"; + if (flags & FlowFlags.Unreachable) return "Unreachable"; + throw new Error(); + } + + function getNodeText(node: Node) { + const sourceFile = getSourceFileOfNode(node); + return getSourceTextOfNodeFromSourceFile(sourceFile, node, /*includeTrivia*/ false); + } + + function renderFlowNode(flowNode: FlowNode, circular: boolean | "circularity") { + let text = getHeader(flowNode.flags); + if (circular) { + text = `${text}#${getDebugFlowNodeId(flowNode)}`; + } + if (hasNode(flowNode)) { + if (flowNode.node) { + text += ` (${getNodeText(flowNode.node)})`; + } + } + else if (isFlowSwitchClause(flowNode)) { + const clauses: string[] = []; + for (let i = flowNode.clauseStart; i < flowNode.clauseEnd; i++) { + const clause = flowNode.switchStatement.caseBlock.clauses[i]; + if (isDefaultClause(clause)) { + clauses.push("default"); + } + else { + clauses.push(getNodeText(clause.expression)); + } + } + text += ` (${clauses.join(", ")})`; + } + return circular === "circularity" ? `Circular(${text})` : text; + } + + function renderGraph() { + const columnCount = columnWidths.length; + const laneCount = nodes.reduce((x, n) => Math.max(x, n.lane), 0) + 1; + const lanes: string[] = fill(Array(laneCount), ""); + const grid: (FlowGraphNode | undefined)[][] = columnWidths.map(() => Array(laneCount)); + const connectors: Connection[][] = columnWidths.map(() => fill(Array(laneCount), 0)); + + // build connectors + for (const node of nodes) { + grid[node.level][node.lane] = node; + const children = getChildren(node); + for (let i = 0; i < children.length; i++) { + const child = children[i]; + let connector: Connection = Connection.Right; + if (child.lane === node.lane) connector |= Connection.Left; + if (i > 0) connector |= Connection.Up; + if (i < children.length - 1) connector |= Connection.Down; + connectors[node.level][child.lane] |= connector; + } + if (children.length === 0) { + connectors[node.level][node.lane] |= Connection.NoChildren; + } + const parents = getParents(node); + for (let i = 0; i < parents.length; i++) { + const parent = parents[i]; + let connector: Connection = Connection.Left; + if (i > 0) connector |= Connection.Up; + if (i < parents.length - 1) connector |= Connection.Down; + connectors[node.level - 1][parent.lane] |= connector; + } + } + + // fill in missing connectors + for (let column = 0; column < columnCount; column++) { + for (let lane = 0; lane < laneCount; lane++) { + const left = column > 0 ? connectors[column - 1][lane] : 0; + const above = lane > 0 ? connectors[column][lane - 1] : 0; + let connector = connectors[column][lane]; + if (!connector) { + if (left & Connection.Right) connector |= Connection.LeftRight; + if (above & Connection.Down) connector |= Connection.UpDown; + connectors[column][lane] = connector; + } + } + } + + for (let column = 0; column < columnCount; column++) { + for (let lane = 0; lane < lanes.length; lane++) { + const connector = connectors[column][lane]; + const fill = connector & Connection.Left ? BoxCharacter.lr : " "; + const node = grid[column][lane]; + if (!node) { + if (column < columnCount - 1) { + writeLane(lane, repeat(fill, columnWidths[column] + 1)); + } + } + else { + writeLane(lane, node.text); + if (column < columnCount - 1) { + writeLane(lane, " "); + writeLane(lane, repeat(fill, columnWidths[column] - node.text.length)); + } + } + writeLane(lane, getBoxCharacter(connector)); + writeLane(lane, connector & Connection.Right && column < columnCount - 1 && !grid[column + 1][lane] ? BoxCharacter.lr : " "); + } + } + + return `\n${lanes.join("\n")}\n`; + + function writeLane(lane: number, text: string) { + lanes[lane] += text; + } + } + + function getBoxCharacter(connector: Connection) { + switch (connector) { + case Connection.UpDown: return BoxCharacter.ud; + case Connection.LeftRight: return BoxCharacter.lr; + case Connection.UpLeft: return BoxCharacter.ul; + case Connection.UpRight: return BoxCharacter.ur; + case Connection.DownLeft: return BoxCharacter.dl; + case Connection.DownRight: return BoxCharacter.dr; + case Connection.UpDownLeft: return BoxCharacter.udl; + case Connection.UpDownRight: return BoxCharacter.udr; + case Connection.UpLeftRight: return BoxCharacter.ulr; + case Connection.DownLeftRight: return BoxCharacter.dlr; + case Connection.UpDownLeftRight: return BoxCharacter.udlr; + } + return " "; + } + + function fill(array: T[], value: T) { + if (array.fill) { + array.fill(value); + } + else { + for (let i = 0; i < array.length; i++) { + array[i] = value; + } + } + return array; + } + + function repeat(ch: string, length: number) { + if (ch.repeat) { + return length > 0 ? ch.repeat(length) : ""; + } + let s = ""; + while (s.length < length) { + s += ch; + } + return s; + } + } } diff --git a/src/debug/compilerDebug.ts b/src/debug/compilerDebug.ts deleted file mode 100644 index 906a166332a0d..0000000000000 --- a/src/debug/compilerDebug.ts +++ /dev/null @@ -1,508 +0,0 @@ -interface Node { - kind: number; -} - -type FunctionExpression = Node; -type ArrowFunction = Node; -type MethodDeclaration = Node; -type Expression = Node; -type SourceFile = Node; -type VariableDeclaration = Node; -type BindingElement = Node; -type CallExpression = Node; -type BinaryExpression = Node; - -interface SwitchStatement extends Node { - caseBlock: CaseBlock; -} - -interface CaseBlock extends Node { - clauses: (CaseClause | DefaultClause)[]; -} - -interface CaseClause extends Node { - _caseclauseBrand: any; - expression: Expression; -} - -interface DefaultClause extends Node { - _defaultClauseBrand: any; -} - -interface TypeScriptModule { - readonly SyntaxKind: { - readonly CaseClause: number; - readonly DefaultClause: number; - }; - - readonly FlowFlags: { - readonly Unreachable: number, - readonly Start: number, - readonly BranchLabel: number, - readonly LoopLabel: number, - readonly Assignment: number, - readonly TrueCondition: number, - readonly FalseCondition: number, - readonly SwitchClause: number, - readonly ArrayMutation: number, - readonly Call: number, - readonly ReduceLabel: number, - readonly Referenced: number, - readonly Shared: number, - readonly Label: number, - readonly Condition: number, - }; - - getSourceFileOfNode(node: Node): SourceFile; - getSourceTextOfNodeFromSourceFile(sourceFile: SourceFile, node: Node, includeTrivia?: boolean): string; - isDefaultClause(node: Node): node is DefaultClause; -} - -type FlowNode = - | FlowStart - | FlowLabel - | FlowAssignment - | FlowCall - | FlowCondition - | FlowSwitchClause - | FlowArrayMutation - | FlowReduceLabel - ; - -interface FlowNodeBase { - flags: FlowFlags; - id?: number; -} - -interface FlowStart extends FlowNodeBase { - node?: FunctionExpression | ArrowFunction | MethodDeclaration; -} - -interface FlowLabel extends FlowNodeBase { - antecedents: FlowNode[] | undefined; -} - -interface FlowAssignment extends FlowNodeBase { - node: Expression | VariableDeclaration | BindingElement; - antecedent: FlowNode; -} - -interface FlowCall extends FlowNodeBase { - node: CallExpression; - antecedent: FlowNode; -} - -interface FlowCondition extends FlowNodeBase { - node: Expression; - antecedent: FlowNode; -} - -interface FlowSwitchClause extends FlowNodeBase { - switchStatement: SwitchStatement; - clauseStart: number; - clauseEnd: number; - antecedent: FlowNode; -} - -interface FlowArrayMutation extends FlowNodeBase { - node: CallExpression | BinaryExpression; - antecedent: FlowNode; -} - -/** @internal */ -export interface FlowReduceLabel extends FlowNodeBase { - target: FlowLabel; - antecedents: FlowNode[]; - antecedent: FlowNode; -} - -type FlowFlags = number; -let FlowFlags: TypeScriptModule["FlowFlags"]; -let getSourceFileOfNode: TypeScriptModule["getSourceFileOfNode"]; -let getSourceTextOfNodeFromSourceFile: TypeScriptModule["getSourceTextOfNodeFromSourceFile"]; -let isDefaultClause: TypeScriptModule["isDefaultClause"]; - -/** @internal */ -export function init(ts: TypeScriptModule) { - FlowFlags = ts.FlowFlags; - getSourceFileOfNode = ts.getSourceFileOfNode; - getSourceTextOfNodeFromSourceFile = ts.getSourceTextOfNodeFromSourceFile; - isDefaultClause = ts.isDefaultClause; -} - -let nextDebugFlowId = -1; - -function getDebugFlowNodeId(f: FlowNode) { - if (!f.id) { - f.id = nextDebugFlowId; - nextDebugFlowId--; - } - return f.id; -} - -/** @internal */ -export function formatControlFlowGraph(flowNode: FlowNode) { - const enum BoxCharacter { - lr = "─", - ud = "│", - dr = "╭", - dl = "╮", - ul = "╯", - ur = "╰", - udr = "├", - udl = "┤", - dlr = "┬", - ulr = "┴", - udlr = "╫", - } - - const enum Connection { - Up = 1 << 0, - Down = 1 << 1, - Left = 1 << 2, - Right = 1 << 3, - - UpDown = Up | Down, - LeftRight = Left | Right, - UpLeft = Up | Left, - UpRight = Up | Right, - DownLeft = Down | Left, - DownRight = Down | Right, - UpDownLeft = UpDown | Left, - UpDownRight = UpDown | Right, - UpLeftRight = Up | LeftRight, - DownLeftRight = Down | LeftRight, - UpDownLeftRight = UpDown | LeftRight, - - NoChildren = 1 << 4, - } - - interface FlowGraphNode { - id: number; - flowNode: FlowNode; - edges: FlowGraphEdge[]; - text: string; - lane: number; - endLane: number; - level: number; - circular: boolean | "circularity"; - } - - interface FlowGraphEdge { - source: FlowGraphNode; - target: FlowGraphNode; - } - - const hasAntecedentFlags = - FlowFlags.Assignment | - FlowFlags.Condition | - FlowFlags.SwitchClause | - FlowFlags.ArrayMutation | - FlowFlags.Call | - FlowFlags.ReduceLabel; - - const hasNodeFlags = - FlowFlags.Start | - FlowFlags.Assignment | - FlowFlags.Call | - FlowFlags.Condition | - FlowFlags.ArrayMutation; - - const links: Record = Object.create(/*o*/ null); // eslint-disable-line no-null/no-null - const nodes: FlowGraphNode[] = []; - const edges: FlowGraphEdge[] = []; - const root = buildGraphNode(flowNode, new Set()); - for (const node of nodes) { - node.text = renderFlowNode(node.flowNode, node.circular); - computeLevel(node); - } - - const height = computeHeight(root); - const columnWidths = computeColumnWidths(height); - computeLanes(root, 0); - return renderGraph(); - - function isFlowSwitchClause(f: FlowNode): f is FlowSwitchClause { - return !!(f.flags & FlowFlags.SwitchClause); - } - - function hasAntecedents(f: FlowNode): f is FlowLabel & { antecedents: FlowNode[] } { - return !!(f.flags & FlowFlags.Label) && !!(f as FlowLabel).antecedents; - } - - function hasAntecedent(f: FlowNode): f is Extract { - return !!(f.flags & hasAntecedentFlags); - } - - function hasNode(f: FlowNode): f is Extract { - return !!(f.flags & hasNodeFlags); - } - - function getChildren(node: FlowGraphNode) { - const children: FlowGraphNode[] = []; - for (const edge of node.edges) { - if (edge.source === node) { - children.push(edge.target); - } - } - return children; - } - - function getParents(node: FlowGraphNode) { - const parents: FlowGraphNode[] = []; - for (const edge of node.edges) { - if (edge.target === node) { - parents.push(edge.source); - } - } - return parents; - } - - function buildGraphNode(flowNode: FlowNode, seen: Set): FlowGraphNode { - const id = getDebugFlowNodeId(flowNode); - let graphNode = links[id]; - if (graphNode && seen.has(flowNode)) { - graphNode.circular = true; - graphNode = { - id: -1, - flowNode, - edges: [], - text: "", - lane: -1, - endLane: -1, - level: -1, - circular: "circularity" - }; - nodes.push(graphNode); - return graphNode; - } - seen.add(flowNode); - if (!graphNode) { - links[id] = graphNode = { id, flowNode, edges: [], text: "", lane: -1, endLane: -1, level: -1, circular: false }; - nodes.push(graphNode); - if (hasAntecedents(flowNode)) { - for (const antecedent of flowNode.antecedents) { - buildGraphEdge(graphNode, antecedent, seen); - } - } - else if (hasAntecedent(flowNode)) { - buildGraphEdge(graphNode, flowNode.antecedent, seen); - } - } - seen.delete(flowNode); - return graphNode; - } - - function buildGraphEdge(source: FlowGraphNode, antecedent: FlowNode, seen: Set) { - const target = buildGraphNode(antecedent, seen); - const edge: FlowGraphEdge = { source, target }; - edges.push(edge); - source.edges.push(edge); - target.edges.push(edge); - } - - function computeLevel(node: FlowGraphNode): number { - if (node.level !== -1) { - return node.level; - } - let level = 0; - for (const parent of getParents(node)) { - level = Math.max(level, computeLevel(parent) + 1); - } - return node.level = level; - } - - function computeHeight(node: FlowGraphNode): number { - let height = 0; - for (const child of getChildren(node)) { - height = Math.max(height, computeHeight(child)); - } - return height + 1; - } - - function computeColumnWidths(height: number) { - const columns: number[] = fill(Array(height), 0); - for (const node of nodes) { - columns[node.level] = Math.max(columns[node.level], node.text.length); - } - return columns; - } - - function computeLanes(node: FlowGraphNode, lane: number) { - if (node.lane === -1) { - node.lane = lane; - node.endLane = lane; - const children = getChildren(node); - for (let i = 0; i < children.length; i++) { - if (i > 0) lane++; - const child = children[i]; - computeLanes(child, lane); - if (child.endLane > node.endLane) { - lane = child.endLane; - } - } - node.endLane = lane; - } - } - - function getHeader(flags: FlowFlags) { - if (flags & FlowFlags.Start) return "Start"; - if (flags & FlowFlags.BranchLabel) return "Branch"; - if (flags & FlowFlags.LoopLabel) return "Loop"; - if (flags & FlowFlags.Assignment) return "Assignment"; - if (flags & FlowFlags.TrueCondition) return "True"; - if (flags & FlowFlags.FalseCondition) return "False"; - if (flags & FlowFlags.SwitchClause) return "SwitchClause"; - if (flags & FlowFlags.ArrayMutation) return "ArrayMutation"; - if (flags & FlowFlags.Call) return "Call"; - if (flags & FlowFlags.ReduceLabel) return "ReduceLabel"; - if (flags & FlowFlags.Unreachable) return "Unreachable"; - throw new Error(); - } - - function getNodeText(node: Node) { - const sourceFile = getSourceFileOfNode(node); - return getSourceTextOfNodeFromSourceFile(sourceFile, node, /*includeTrivia*/ false); - } - - function renderFlowNode(flowNode: FlowNode, circular: boolean | "circularity") { - let text = getHeader(flowNode.flags); - if (circular) { - text = `${text}#${getDebugFlowNodeId(flowNode)}`; - } - if (hasNode(flowNode)) { - if (flowNode.node) { - text += ` (${getNodeText(flowNode.node)})`; - } - } - else if (isFlowSwitchClause(flowNode)) { - const clauses: string[] = []; - for (let i = flowNode.clauseStart; i < flowNode.clauseEnd; i++) { - const clause = flowNode.switchStatement.caseBlock.clauses[i]; - if (isDefaultClause(clause)) { - clauses.push("default"); - } - else { - clauses.push(getNodeText(clause.expression)); - } - } - text += ` (${clauses.join(", ")})`; - } - return circular === "circularity" ? `Circular(${text})` : text; - } - - function renderGraph() { - const columnCount = columnWidths.length; - const laneCount = nodes.reduce((x, n) => Math.max(x, n.lane), 0) + 1; - const lanes: string[] = fill(Array(laneCount), ""); - const grid: (FlowGraphNode | undefined)[][] = columnWidths.map(() => Array(laneCount)); - const connectors: Connection[][] = columnWidths.map(() => fill(Array(laneCount), 0)); - - // build connectors - for (const node of nodes) { - grid[node.level][node.lane] = node; - const children = getChildren(node); - for (let i = 0; i < children.length; i++) { - const child = children[i]; - let connector: Connection = Connection.Right; - if (child.lane === node.lane) connector |= Connection.Left; - if (i > 0) connector |= Connection.Up; - if (i < children.length - 1) connector |= Connection.Down; - connectors[node.level][child.lane] |= connector; - } - if (children.length === 0) { - connectors[node.level][node.lane] |= Connection.NoChildren; - } - const parents = getParents(node); - for (let i = 0; i < parents.length; i++) { - const parent = parents[i]; - let connector: Connection = Connection.Left; - if (i > 0) connector |= Connection.Up; - if (i < parents.length - 1) connector |= Connection.Down; - connectors[node.level - 1][parent.lane] |= connector; - } - } - - // fill in missing connectors - for (let column = 0; column < columnCount; column++) { - for (let lane = 0; lane < laneCount; lane++) { - const left = column > 0 ? connectors[column - 1][lane] : 0; - const above = lane > 0 ? connectors[column][lane - 1] : 0; - let connector = connectors[column][lane]; - if (!connector) { - if (left & Connection.Right) connector = Connection.LeftRight; - if (above & Connection.Down) connector |= Connection.UpDown; - connectors[column][lane] = connector; - } - } - } - - for (let column = 0; column < columnCount; column++) { - for (let lane = 0; lane < lanes.length; lane++) { - const connector = connectors[column][lane]; - const fill = connector & Connection.Left ? BoxCharacter.lr : " "; - const node = grid[column][lane]; - if (!node) { - if (column < columnCount - 1) { - writeLane(lane, repeat(fill, columnWidths[column] + 1)); - } - } - else { - writeLane(lane, node.text); - if (column < columnCount - 1) { - writeLane(lane, " "); - writeLane(lane, repeat(fill, columnWidths[column] - node.text.length)); - } - } - writeLane(lane, getBoxCharacter(connector)); - writeLane(lane, connector & Connection.Right && column < columnCount - 1 && !grid[column + 1][lane] ? BoxCharacter.lr : " "); - } - } - - return `\n${lanes.join("\n")}\n`; - - function writeLane(lane: number, text: string) { - lanes[lane] += text; - } - } - - function getBoxCharacter(connector: Connection) { - switch (connector) { - case Connection.UpDown: return BoxCharacter.ud; - case Connection.LeftRight: return BoxCharacter.lr; - case Connection.UpLeft: return BoxCharacter.ul; - case Connection.UpRight: return BoxCharacter.ur; - case Connection.DownLeft: return BoxCharacter.dl; - case Connection.DownRight: return BoxCharacter.dr; - case Connection.UpDownLeft: return BoxCharacter.udl; - case Connection.UpDownRight: return BoxCharacter.udr; - case Connection.UpLeftRight: return BoxCharacter.ulr; - case Connection.DownLeftRight: return BoxCharacter.dlr; - case Connection.UpDownLeftRight: return BoxCharacter.udlr; - } - return " "; - } - - function fill(array: T[], value: T) { - if (array.fill) { - array.fill(value); - } - else { - for (let i = 0; i < array.length; i++) { - array[i] = value; - } - } - return array; - } - - function repeat(ch: string, length: number) { - if (ch.repeat) { - return length > 0 ? ch.repeat(length) : ""; - } - let s = ""; - while (s.length < length) { - s += ch; - } - return s; - } -} diff --git a/src/debug/tsconfig.json b/src/debug/tsconfig.json deleted file mode 100644 index ab17c2cab1a99..0000000000000 --- a/src/debug/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../tsconfig-base", - "compilerOptions": { - "target": "es2019", - "lib": ["es2019"], - }, - "include": ["**/*"] -} diff --git a/src/tsconfig.json b/src/tsconfig.json index 213e1eda3c3b2..1cfab491559d3 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -4,7 +4,6 @@ "references": [ { "path": "./cancellationToken" }, { "path": "./compiler" }, - { "path": "./debug" }, { "path": "./deprecatedCompat" }, { "path": "./executeCommandLine" }, { "path": "./harness" },