From e64724ea35af979931b07b96a31947c5b9ee2186 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 24 May 2016 16:27:39 -0700 Subject: [PATCH] Transient node properties for transformations. --- src/compiler/core.ts | 34 ++++++++---- src/compiler/transformer.ts | 107 ++++++++++++++++++++++++++++-------- src/compiler/tsc.ts | 28 ++++++++-- src/compiler/types.ts | 6 ++ 4 files changed, 136 insertions(+), 39 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 908de8d4806..aada521c2e4 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1059,6 +1059,7 @@ namespace ts { this.excludeTransformFlags = TransformFlags.None; this.parent = undefined; this.original = undefined; + this.transformId = 0; } export let objectAllocator: ObjectAllocator = { @@ -1181,16 +1182,20 @@ namespace ts { } } + /** + * Gets the names of all marks. + */ + export function getMarkNames() { + return getKeys(markCounts); + } + /** * Gets the number of marks with the specified name. * * @param markName The name of the marks that should be counted. */ export function getCount(markName: string) { - if (enabled) { - return getProperty(markCounts, markName) || 0; - } - return 0; + return enabled && getProperty(markCounts, markName) || 0; } /** @@ -1199,10 +1204,7 @@ namespace ts { * @param markName The name of the mark. */ export function getTimestamp(markName: string) { - if (enabled) { - return getProperty(markTimestamps, markName) || 0; - } - return 0; + return enabled && getProperty(markTimestamps, markName) || 0; } /** @@ -1221,8 +1223,9 @@ namespace ts { } function clearMark(markName: string) { - markTimestamps[markName] = 0; - markCounts[markName] = 0; + if (delete markTimestamps[markName]) { + delete markCounts[markName]; + } } /** @@ -1246,13 +1249,20 @@ namespace ts { } } + /** + * Gets the names of all recorded measures. + */ + export function getMeasureNames() { + return getKeys(measureDurations); + } + /** * Gets the total duration of all measurements with the supplied name. * * @param measureName The name of the measure whose durations should be accumulated. */ export function getDuration(measureName: string) { - return getProperty(measureDurations, measureName) || 0; + return enabled && getProperty(measureDurations, measureName) || 0; } /** @@ -1271,7 +1281,7 @@ namespace ts { } function clearMeasure(measureName: string) { - measureDurations[measureName] = 0; + delete measureDurations[measureName]; } /** diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index ae1ab6fbf62..9e58b02c208 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -45,6 +45,14 @@ namespace ts { return transformers; } + /** + * Tracks a monotonically increasing transformation id used to associate a node with a specific + * transformation. This ensures transient properties related to transformations can be safely + * stored on source tree nodes that may be reused across multiple transformations (such as + * with compile-on-save). + */ + let nextTransformId = 1; + /** * Transforms an array of SourceFiles by passing them through each transformer. * @@ -54,12 +62,13 @@ namespace ts { * @param transforms An array of Transformers. */ export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]) { - const nodeEmitFlags: Map = {}; - const sourceMapRanges: Map = {}; - const commentRanges: Map = {}; + const transformId = nextTransformId++; + const tokenSourceMapRanges: Map = { }; const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; const enabledSyntaxKindFeatures = new Array(SyntaxKind.Count); + const sourceTreeNodesWithAnnotations: Node[] = []; + let lastNodeEmitFlagsNode: Node; let lastNodeEmitFlags: NodeEmitFlags; let lastSourceMapRangeNode: Node; @@ -118,7 +127,20 @@ namespace ts { } currentSourceFile = sourceFile; - return transformation(sourceFile); + sourceFile = transformation(sourceFile); + + // Cleanup source tree nodes with annotations + for (const node of sourceTreeNodesWithAnnotations) { + if (node.transformId === transformId) { + node.transformId = 0; + node.emitFlags = 0; + node.commentRange = undefined; + node.sourceMapRange = undefined; + } + } + + sourceTreeNodesWithAnnotations.length = 0; + return sourceFile; } /** @@ -181,6 +203,28 @@ namespace ts { emit(node); } + /** + * Associates a node with the current transformation, initializing + * various transient transformation properties. + * + * @param node The node. + */ + function beforeSetAnnotation(node: Node) { + if (node.transformId !== transformId) { + node.transformId = transformId; + if ((node.flags & NodeFlags.Synthesized) === 0) { + node.emitFlags = 0; + node.sourceMapRange = undefined; + node.commentRange = undefined; + + // To avoid holding onto transformation artifacts, we keep track of any + // source tree node we are annotating. This allows us to clean them up after + // all transformations have completed. + sourceTreeNodesWithAnnotations.push(node); + } + } + } + /** * Gets flags that control emit behavior of a node. * @@ -196,15 +240,19 @@ namespace ts { return lastNodeEmitFlags; } - + // Get the emit flags for a node or from one of its original nodes. let flags: NodeEmitFlags; - while (node) { - let flags = node.id ? nodeEmitFlags[node.id] : undefined; - if (flags !== undefined) { - break; + let current = node; + while (current) { + if (current.transformId === transformId) { + const nodeEmitFlags = current.emitFlags; + if (nodeEmitFlags) { + flags = nodeEmitFlags & ~NodeEmitFlags.HasNodeEmitFlags; + break; + } } - node = node.original; + current = current.original; } // Cache the most recently requested value. @@ -220,14 +268,17 @@ namespace ts { * @param emitFlags The NodeEmitFlags for the node. */ function setNodeEmitFlags(node: T, emitFlags: NodeEmitFlags) { + // Merge existing flags. if (emitFlags & NodeEmitFlags.Merge) { emitFlags = getNodeEmitFlags(node) | (emitFlags & ~NodeEmitFlags.Merge); } + beforeSetAnnotation(node); + // Cache the most recently requested value. lastNodeEmitFlagsNode = node; lastNodeEmitFlags = emitFlags; - nodeEmitFlags[getNodeId(node)] = emitFlags; + node.emitFlags = emitFlags | NodeEmitFlags.HasNodeEmitFlags; return node; } @@ -246,12 +297,15 @@ namespace ts { return lastSourceMapRange || node; } + // Get the custom source map range for a node or from one of its original nodes. let range: TextRange; let current = node; while (current) { - range = current.id ? sourceMapRanges[current.id] : undefined; - if (range !== undefined) { - break; + if (current.transformId === transformId) { + range = current.sourceMapRange; + if (range !== undefined) { + break; + } } current = current.original; @@ -270,10 +324,12 @@ namespace ts { * @param range The text range. */ function setSourceMapRange(node: T, range: TextRange) { + beforeSetAnnotation(node); + // Cache the most recently requested value. lastSourceMapRangeNode = node; lastSourceMapRange = range; - sourceMapRanges[getNodeId(node)] = range; + node.sourceMapRange = range; return node; } @@ -290,13 +346,15 @@ namespace ts { // As a performance optimization, use the cached value of the most recent node. // This helps for cases where this function is called repeatedly for the same node. if (lastTokenSourceMapRangeNode === node && lastTokenSourceMapRangeToken === token) { - return lastSourceMapRange; + return lastTokenSourceMapRange; } + // Get the custom token source map range for a node or from one of its original nodes. + // Custom token ranges are not stored on the node to avoid the GC burden. let range: TextRange; let current = node; while (current) { - range = current.id ? sourceMapRanges[current.id + "-" + token] : undefined; + range = current.id ? tokenSourceMapRanges[current.id + "-" + token] : undefined; if (range !== undefined) { break; } @@ -323,7 +381,7 @@ namespace ts { lastTokenSourceMapRangeNode = node; lastTokenSourceMapRangeToken = token; lastTokenSourceMapRange = range; - sourceMapRanges[getNodeId(node) + "-" + token] = range; + tokenSourceMapRanges[getNodeId(node) + "-" + token] = range; return node; } @@ -342,12 +400,15 @@ namespace ts { return lastCommentMapRange || node; } + // Get the custom comment range for a node or from one of its original nodes. let range: TextRange; let current = node; while (current) { - range = current.id ? commentRanges[current.id] : undefined; - if (range !== undefined) { - break; + if (current.transformId === transformId) { + range = current.commentRange; + if (range !== undefined) { + break; + } } current = current.original; @@ -363,10 +424,12 @@ namespace ts { * Sets a custom text range to use when emitting comments. */ function setCommentRange(node: T, range: TextRange) { + beforeSetAnnotation(node); + // Cache the most recently requested value. lastCommentMapRangeNode = node; lastCommentMapRange = range; - commentRanges[getNodeId(node)] = range; + node.commentRange = range; return node; } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index ea317d03268..9d521c17f28 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -544,7 +544,7 @@ namespace ts { } function compile(fileNames: string[], compilerOptions: CompilerOptions, compilerHost: CompilerHost) { - if (compilerOptions.diagnostics) { + if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) { performance.enable(); performance.reset(); } @@ -558,7 +558,7 @@ namespace ts { }); } - if (compilerOptions.diagnostics) { + if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) { const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; reportCountStatistic("Files", program.getSourceFiles().length); reportCountStatistic("Lines", countLines(program)); @@ -577,9 +577,6 @@ namespace ts { // emit time includes I/O write time. We preserve this behavior so we can accurately compare times. reportTimeStatistic("I/O read", performance.getDuration("ioReadTime")); reportTimeStatistic("I/O write", performance.getDuration("ioWriteTime")); - reportTimeStatistic("Print time", performance.getDuration("printTime")); - reportTimeStatistic("Comment time", performance.getDuration("commentTime")); - reportTimeStatistic("Sourcemap time", performance.getDuration("sourcemapTime")); const programTime = performance.getDuration("programTime"); const bindTime = performance.getDuration("bindTime"); const checkTime = performance.getDuration("checkTime"); @@ -590,6 +587,27 @@ namespace ts { reportTimeStatistic("Emit time", emitTime); reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime); + if (compilerOptions.extendedDiagnostics) { + sys.write("Extended Diagnostics:" + sys.newLine); + sys.write("Marks:" + sys.newLine); + for (const markName of performance.getMarkNames()) { + if (/^(ioReadStart|ioWriteStart|programStart|bindStart|checkStart|emitStart)$/.test(markName)) { + continue; + } + + reportCountStatistic(" " + markName, performance.getCount(markName)); + } + + sys.write("Measures:" + sys.newLine); + for (const measureName of performance.getMeasureNames()) { + if (/^(ioReadTime|ioWriteTime|programTime|bindTime|checkTime|emitTime)$/.test(measureName)) { + continue; + } + + reportTimeStatistic(" " + measureName, performance.getDuration(measureName)); + } + } + performance.disable(); performance.reset(); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 110ca5f512d..2b601b9c1ea 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -470,6 +470,10 @@ namespace ts { /* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding) /* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding) /* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes) + /* @internal */ transformId?: number; // Associates transient transformation properties with a specific transformation (initialized by transformation). + /* @internal */ emitFlags?: NodeEmitFlags; // Transient emit flags for a synthesized node (initialized by transformation). + /* @internal */ sourceMapRange?: TextRange; // Transient custom sourcemap range for a synthesized node (initialized by transformation). + /* @internal */ commentRange?: TextRange; // Transient custom comment range for a synthesized node (initialized by transformation). } export interface NodeArray extends Array, TextRange { @@ -2957,6 +2961,8 @@ namespace ts { // align with the old emitter. SourceMapEmitOpenBraceAsToken = 1 << 21, // Emits the open brace of a block function body as a source mapped token. SourceMapAdjustRestParameterLoop = 1 << 22, // Emits adjusted source map positions for a ForStatement generated when transforming a rest parameter for ES5/3. + + HasNodeEmitFlags = 1 << 31, // Indicates the node has emit flags set. } /** Additional context provided to `visitEachChild` */