Transient node properties for transformations.

This commit is contained in:
Ron Buckton 2016-05-24 16:27:39 -07:00
parent fb48731afa
commit e64724ea35
4 changed files with 136 additions and 39 deletions

View File

@ -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];
}
/**

View File

@ -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<NodeEmitFlags> = {};
const sourceMapRanges: Map<TextRange> = {};
const commentRanges: Map<TextRange> = {};
const transformId = nextTransformId++;
const tokenSourceMapRanges: Map<TextRange> = { };
const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
const enabledSyntaxKindFeatures = new Array<SyntaxKindFeatureFlags>(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<T extends Node>(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<T extends Node>(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<T extends Node>(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;
}

View File

@ -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();
}

View File

@ -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<T extends Node> extends Array<T>, 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` */