Skip to content

Commit e64724e

Browse files
committed
Transient node properties for transformations.
1 parent fb48731 commit e64724e

4 files changed

Lines changed: 136 additions & 39 deletions

File tree

src/compiler/core.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,7 @@ namespace ts {
10591059
this.excludeTransformFlags = TransformFlags.None;
10601060
this.parent = undefined;
10611061
this.original = undefined;
1062+
this.transformId = 0;
10621063
}
10631064

10641065
export let objectAllocator: ObjectAllocator = {
@@ -1181,16 +1182,20 @@ namespace ts {
11811182
}
11821183
}
11831184

1185+
/**
1186+
* Gets the names of all marks.
1187+
*/
1188+
export function getMarkNames() {
1189+
return getKeys(markCounts);
1190+
}
1191+
11841192
/**
11851193
* Gets the number of marks with the specified name.
11861194
*
11871195
* @param markName The name of the marks that should be counted.
11881196
*/
11891197
export function getCount(markName: string) {
1190-
if (enabled) {
1191-
return getProperty(markCounts, markName) || 0;
1192-
}
1193-
return 0;
1198+
return enabled && getProperty(markCounts, markName) || 0;
11941199
}
11951200

11961201
/**
@@ -1199,10 +1204,7 @@ namespace ts {
11991204
* @param markName The name of the mark.
12001205
*/
12011206
export function getTimestamp(markName: string) {
1202-
if (enabled) {
1203-
return getProperty(markTimestamps, markName) || 0;
1204-
}
1205-
return 0;
1207+
return enabled && getProperty(markTimestamps, markName) || 0;
12061208
}
12071209

12081210
/**
@@ -1221,8 +1223,9 @@ namespace ts {
12211223
}
12221224

12231225
function clearMark(markName: string) {
1224-
markTimestamps[markName] = 0;
1225-
markCounts[markName] = 0;
1226+
if (delete markTimestamps[markName]) {
1227+
delete markCounts[markName];
1228+
}
12261229
}
12271230

12281231
/**
@@ -1246,13 +1249,20 @@ namespace ts {
12461249
}
12471250
}
12481251

1252+
/**
1253+
* Gets the names of all recorded measures.
1254+
*/
1255+
export function getMeasureNames() {
1256+
return getKeys(measureDurations);
1257+
}
1258+
12491259
/**
12501260
* Gets the total duration of all measurements with the supplied name.
12511261
*
12521262
* @param measureName The name of the measure whose durations should be accumulated.
12531263
*/
12541264
export function getDuration(measureName: string) {
1255-
return getProperty(measureDurations, measureName) || 0;
1265+
return enabled && getProperty(measureDurations, measureName) || 0;
12561266
}
12571267

12581268
/**
@@ -1271,7 +1281,7 @@ namespace ts {
12711281
}
12721282

12731283
function clearMeasure(measureName: string) {
1274-
measureDurations[measureName] = 0;
1284+
delete measureDurations[measureName];
12751285
}
12761286

12771287
/**

src/compiler/transformer.ts

Lines changed: 85 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ namespace ts {
4545
return transformers;
4646
}
4747

48+
/**
49+
* Tracks a monotonically increasing transformation id used to associate a node with a specific
50+
* transformation. This ensures transient properties related to transformations can be safely
51+
* stored on source tree nodes that may be reused across multiple transformations (such as
52+
* with compile-on-save).
53+
*/
54+
let nextTransformId = 1;
55+
4856
/**
4957
* Transforms an array of SourceFiles by passing them through each transformer.
5058
*
@@ -54,12 +62,13 @@ namespace ts {
5462
* @param transforms An array of Transformers.
5563
*/
5664
export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]) {
57-
const nodeEmitFlags: Map<NodeEmitFlags> = {};
58-
const sourceMapRanges: Map<TextRange> = {};
59-
const commentRanges: Map<TextRange> = {};
65+
const transformId = nextTransformId++;
66+
const tokenSourceMapRanges: Map<TextRange> = { };
6067
const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
6168
const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
6269
const enabledSyntaxKindFeatures = new Array<SyntaxKindFeatureFlags>(SyntaxKind.Count);
70+
const sourceTreeNodesWithAnnotations: Node[] = [];
71+
6372
let lastNodeEmitFlagsNode: Node;
6473
let lastNodeEmitFlags: NodeEmitFlags;
6574
let lastSourceMapRangeNode: Node;
@@ -118,7 +127,20 @@ namespace ts {
118127
}
119128

120129
currentSourceFile = sourceFile;
121-
return transformation(sourceFile);
130+
sourceFile = transformation(sourceFile);
131+
132+
// Cleanup source tree nodes with annotations
133+
for (const node of sourceTreeNodesWithAnnotations) {
134+
if (node.transformId === transformId) {
135+
node.transformId = 0;
136+
node.emitFlags = 0;
137+
node.commentRange = undefined;
138+
node.sourceMapRange = undefined;
139+
}
140+
}
141+
142+
sourceTreeNodesWithAnnotations.length = 0;
143+
return sourceFile;
122144
}
123145

124146
/**
@@ -181,6 +203,28 @@ namespace ts {
181203
emit(node);
182204
}
183205

206+
/**
207+
* Associates a node with the current transformation, initializing
208+
* various transient transformation properties.
209+
*
210+
* @param node The node.
211+
*/
212+
function beforeSetAnnotation(node: Node) {
213+
if (node.transformId !== transformId) {
214+
node.transformId = transformId;
215+
if ((node.flags & NodeFlags.Synthesized) === 0) {
216+
node.emitFlags = 0;
217+
node.sourceMapRange = undefined;
218+
node.commentRange = undefined;
219+
220+
// To avoid holding onto transformation artifacts, we keep track of any
221+
// source tree node we are annotating. This allows us to clean them up after
222+
// all transformations have completed.
223+
sourceTreeNodesWithAnnotations.push(node);
224+
}
225+
}
226+
}
227+
184228
/**
185229
* Gets flags that control emit behavior of a node.
186230
*
@@ -196,15 +240,19 @@ namespace ts {
196240
return lastNodeEmitFlags;
197241
}
198242

199-
243+
// Get the emit flags for a node or from one of its original nodes.
200244
let flags: NodeEmitFlags;
201-
while (node) {
202-
let flags = node.id ? nodeEmitFlags[node.id] : undefined;
203-
if (flags !== undefined) {
204-
break;
245+
let current = node;
246+
while (current) {
247+
if (current.transformId === transformId) {
248+
const nodeEmitFlags = current.emitFlags;
249+
if (nodeEmitFlags) {
250+
flags = nodeEmitFlags & ~NodeEmitFlags.HasNodeEmitFlags;
251+
break;
252+
}
205253
}
206254

207-
node = node.original;
255+
current = current.original;
208256
}
209257

210258
// Cache the most recently requested value.
@@ -220,14 +268,17 @@ namespace ts {
220268
* @param emitFlags The NodeEmitFlags for the node.
221269
*/
222270
function setNodeEmitFlags<T extends Node>(node: T, emitFlags: NodeEmitFlags) {
271+
// Merge existing flags.
223272
if (emitFlags & NodeEmitFlags.Merge) {
224273
emitFlags = getNodeEmitFlags(node) | (emitFlags & ~NodeEmitFlags.Merge);
225274
}
226275

276+
beforeSetAnnotation(node);
277+
227278
// Cache the most recently requested value.
228279
lastNodeEmitFlagsNode = node;
229280
lastNodeEmitFlags = emitFlags;
230-
nodeEmitFlags[getNodeId(node)] = emitFlags;
281+
node.emitFlags = emitFlags | NodeEmitFlags.HasNodeEmitFlags;
231282
return node;
232283
}
233284

@@ -246,12 +297,15 @@ namespace ts {
246297
return lastSourceMapRange || node;
247298
}
248299

300+
// Get the custom source map range for a node or from one of its original nodes.
249301
let range: TextRange;
250302
let current = node;
251303
while (current) {
252-
range = current.id ? sourceMapRanges[current.id] : undefined;
253-
if (range !== undefined) {
254-
break;
304+
if (current.transformId === transformId) {
305+
range = current.sourceMapRange;
306+
if (range !== undefined) {
307+
break;
308+
}
255309
}
256310

257311
current = current.original;
@@ -270,10 +324,12 @@ namespace ts {
270324
* @param range The text range.
271325
*/
272326
function setSourceMapRange<T extends Node>(node: T, range: TextRange) {
327+
beforeSetAnnotation(node);
328+
273329
// Cache the most recently requested value.
274330
lastSourceMapRangeNode = node;
275331
lastSourceMapRange = range;
276-
sourceMapRanges[getNodeId(node)] = range;
332+
node.sourceMapRange = range;
277333
return node;
278334
}
279335

@@ -290,13 +346,15 @@ namespace ts {
290346
// As a performance optimization, use the cached value of the most recent node.
291347
// This helps for cases where this function is called repeatedly for the same node.
292348
if (lastTokenSourceMapRangeNode === node && lastTokenSourceMapRangeToken === token) {
293-
return lastSourceMapRange;
349+
return lastTokenSourceMapRange;
294350
}
295351

352+
// Get the custom token source map range for a node or from one of its original nodes.
353+
// Custom token ranges are not stored on the node to avoid the GC burden.
296354
let range: TextRange;
297355
let current = node;
298356
while (current) {
299-
range = current.id ? sourceMapRanges[current.id + "-" + token] : undefined;
357+
range = current.id ? tokenSourceMapRanges[current.id + "-" + token] : undefined;
300358
if (range !== undefined) {
301359
break;
302360
}
@@ -323,7 +381,7 @@ namespace ts {
323381
lastTokenSourceMapRangeNode = node;
324382
lastTokenSourceMapRangeToken = token;
325383
lastTokenSourceMapRange = range;
326-
sourceMapRanges[getNodeId(node) + "-" + token] = range;
384+
tokenSourceMapRanges[getNodeId(node) + "-" + token] = range;
327385
return node;
328386
}
329387

@@ -342,12 +400,15 @@ namespace ts {
342400
return lastCommentMapRange || node;
343401
}
344402

403+
// Get the custom comment range for a node or from one of its original nodes.
345404
let range: TextRange;
346405
let current = node;
347406
while (current) {
348-
range = current.id ? commentRanges[current.id] : undefined;
349-
if (range !== undefined) {
350-
break;
407+
if (current.transformId === transformId) {
408+
range = current.commentRange;
409+
if (range !== undefined) {
410+
break;
411+
}
351412
}
352413

353414
current = current.original;
@@ -363,10 +424,12 @@ namespace ts {
363424
* Sets a custom text range to use when emitting comments.
364425
*/
365426
function setCommentRange<T extends Node>(node: T, range: TextRange) {
427+
beforeSetAnnotation(node);
428+
366429
// Cache the most recently requested value.
367430
lastCommentMapRangeNode = node;
368431
lastCommentMapRange = range;
369-
commentRanges[getNodeId(node)] = range;
432+
node.commentRange = range;
370433
return node;
371434
}
372435

src/compiler/tsc.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ namespace ts {
544544
}
545545

546546
function compile(fileNames: string[], compilerOptions: CompilerOptions, compilerHost: CompilerHost) {
547-
if (compilerOptions.diagnostics) {
547+
if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) {
548548
performance.enable();
549549
performance.reset();
550550
}
@@ -558,7 +558,7 @@ namespace ts {
558558
});
559559
}
560560

561-
if (compilerOptions.diagnostics) {
561+
if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) {
562562
const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1;
563563
reportCountStatistic("Files", program.getSourceFiles().length);
564564
reportCountStatistic("Lines", countLines(program));
@@ -577,9 +577,6 @@ namespace ts {
577577
// emit time includes I/O write time. We preserve this behavior so we can accurately compare times.
578578
reportTimeStatistic("I/O read", performance.getDuration("ioReadTime"));
579579
reportTimeStatistic("I/O write", performance.getDuration("ioWriteTime"));
580-
reportTimeStatistic("Print time", performance.getDuration("printTime"));
581-
reportTimeStatistic("Comment time", performance.getDuration("commentTime"));
582-
reportTimeStatistic("Sourcemap time", performance.getDuration("sourcemapTime"));
583580
const programTime = performance.getDuration("programTime");
584581
const bindTime = performance.getDuration("bindTime");
585582
const checkTime = performance.getDuration("checkTime");
@@ -590,6 +587,27 @@ namespace ts {
590587
reportTimeStatistic("Emit time", emitTime);
591588
reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime);
592589

590+
if (compilerOptions.extendedDiagnostics) {
591+
sys.write("Extended Diagnostics:" + sys.newLine);
592+
sys.write("Marks:" + sys.newLine);
593+
for (const markName of performance.getMarkNames()) {
594+
if (/^(ioReadStart|ioWriteStart|programStart|bindStart|checkStart|emitStart)$/.test(markName)) {
595+
continue;
596+
}
597+
598+
reportCountStatistic(" " + markName, performance.getCount(markName));
599+
}
600+
601+
sys.write("Measures:" + sys.newLine);
602+
for (const measureName of performance.getMeasureNames()) {
603+
if (/^(ioReadTime|ioWriteTime|programTime|bindTime|checkTime|emitTime)$/.test(measureName)) {
604+
continue;
605+
}
606+
607+
reportTimeStatistic(" " + measureName, performance.getDuration(measureName));
608+
}
609+
}
610+
593611
performance.disable();
594612
performance.reset();
595613
}

src/compiler/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,10 @@ namespace ts {
470470
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
471471
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
472472
/* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes)
473+
/* @internal */ transformId?: number; // Associates transient transformation properties with a specific transformation (initialized by transformation).
474+
/* @internal */ emitFlags?: NodeEmitFlags; // Transient emit flags for a synthesized node (initialized by transformation).
475+
/* @internal */ sourceMapRange?: TextRange; // Transient custom sourcemap range for a synthesized node (initialized by transformation).
476+
/* @internal */ commentRange?: TextRange; // Transient custom comment range for a synthesized node (initialized by transformation).
473477
}
474478

475479
export interface NodeArray<T extends Node> extends Array<T>, TextRange {
@@ -2957,6 +2961,8 @@ namespace ts {
29572961
// align with the old emitter.
29582962
SourceMapEmitOpenBraceAsToken = 1 << 21, // Emits the open brace of a block function body as a source mapped token.
29592963
SourceMapAdjustRestParameterLoop = 1 << 22, // Emits adjusted source map positions for a ForStatement generated when transforming a rest parameter for ES5/3.
2964+
2965+
HasNodeEmitFlags = 1 << 31, // Indicates the node has emit flags set.
29602966
}
29612967

29622968
/** Additional context provided to `visitEachChild` */

0 commit comments

Comments
 (0)