diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 3945357b6f1..599a843df80 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -86,9 +86,9 @@ namespace ts { const binder = createBinder(); export function bindSourceFile(file: SourceFile, options: CompilerOptions) { - performance.mark("bindStart"); + const bindStart = performance.mark(); binder(file, options); - performance.measure("bindTime", "bindStart"); + performance.measure("bindTime", bindStart); } function createBinder(): (file: SourceFile, options: CompilerOptions) => void { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bb2aa685d64..98e2a63a6c9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16512,11 +16512,11 @@ namespace ts { } function checkSourceFile(node: SourceFile) { - performance.mark("checkStart"); + const checkStart = performance.mark(); checkSourceFileWorker(node); - performance.measure("checkTime", "checkStart"); + performance.measure("checkTime", checkStart); } // Fully type check a source file and collect the relevant diagnostics. diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index dbe2299ca7a..6f0cb94b7d7 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -82,12 +82,9 @@ namespace ts { function getLeadingComments(range: TextRange): CommentRange[]; function getLeadingComments(range: TextRange, contextNode: Node, ignoreNodeCallback: (contextNode: Node) => boolean, getTextRangeCallback: (contextNode: Node) => TextRange): CommentRange[]; function getLeadingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange) { - let comments: CommentRange[] = []; - let ignored = false; if (contextNode) { range = getTextRangeCallback(contextNode) || range; if (ignoreNodeCallback(contextNode)) { - ignored = true; // If the node will not be emitted in JS, remove all the comments (normal, // pinned and `///`) associated with the node, unless it is a triple slash // comment at the top of the file. @@ -101,16 +98,14 @@ namespace ts { // The first `///` will NOT be removed while the second one will be removed // even though both nodes will not be emitted. if (range.pos === 0) { - comments = filter(getLeadingCommentsOfPosition(0), isTripleSlashComment); + return filter(getLeadingCommentsOfPosition(0), isTripleSlashComment); } + + return; } } - if (!ignored) { - comments = getLeadingCommentsOfPosition(range.pos); - } - - return comments; + return getLeadingCommentsOfPosition(range.pos); } /** @@ -277,42 +272,42 @@ namespace ts { reset, setSourceFile, getLeadingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange): CommentRange[] { - performance.mark("commentStart"); + const commentStart = performance.mark(); const comments = getLeadingComments(range, contextNode, ignoreNodeCallback, getTextRangeCallback); - performance.measure("commentTime", "commentStart"); + performance.measure("commentTime", commentStart); return comments; }, getTrailingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange): CommentRange[] { - performance.mark("commentStart"); + const commentStart = performance.mark(); const comments = getTrailingComments(range, contextNode, ignoreNodeCallback, getTextRangeCallback); - performance.measure("commentTime", "commentStart"); + performance.measure("commentTime", commentStart); return comments; }, getTrailingCommentsOfPosition(pos: number): CommentRange[] { - performance.mark("commentStart"); + const commentStart = performance.mark(); const comments = getTrailingCommentsOfPosition(pos); - performance.measure("commentTime", "commentStart"); + performance.measure("commentTime", commentStart); return comments; }, emitLeadingComments(range: TextRange, comments: CommentRange[], contextNode?: Node, getTextRangeCallback?: (contextNode: Node) => TextRange): void { - performance.mark("commentStart"); + const commentStart = performance.mark(); emitLeadingComments(range, comments, contextNode, getTextRangeCallback); - performance.measure("commentTime", "commentStart"); + performance.measure("commentTime", commentStart); }, emitTrailingComments(range: TextRange, comments: CommentRange[]): void { - performance.mark("commentStart"); + const commentStart = performance.mark(); emitLeadingComments(range, comments); - performance.measure("commentTime", "commentStart"); + performance.measure("commentTime", commentStart); }, emitLeadingDetachedComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean): void { - performance.mark("commentStart"); + const commentStart = performance.mark(); emitLeadingDetachedComments(range, contextNode, ignoreNodeCallback); - performance.measure("commentTime", "commentStart"); + performance.measure("commentTime", commentStart); }, emitTrailingDetachedComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean): void { - performance.mark("commentStart"); + const commentStart = performance.mark(); emitTrailingDetachedComments(range, contextNode, ignoreNodeCallback); - performance.measure("commentTime", "commentStart"); + performance.measure("commentTime", commentStart); } }; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index f4b0f5126f5..063b619c690 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1139,158 +1139,64 @@ namespace ts { /** Performance measurements for the compiler. */ /*@internal*/ export namespace performance { - interface MarkData { - markName: string; - timestamp: number; - } - - interface MeasureData { - measureName: string; - startMarkName: string; - endMarkName: string; - timestamp: number; - marksOffset: number; - } - - export interface Measure { - name: string; - startTime: number; - duration: number; - } - - const markTimestamps: Map = {}; - const markCounts: Map = {}; - const measureDurations: Map = {}; - - let start = now(); + let counters: Map = {}; + let measures: Map = {}; let enabled = false; - /** Gets the current timer for performance measurements. */ - export function now() { - return Date.now(); - } - /** - * Adds a performance mark with the specified name. + * Increments a counter with the specified name. * - * @param markName The name of the performance mark. + * @param counterName The name of the counter. */ - export function mark(markName: string) { + export function increment(counterName: string) { if (enabled) { - markTimestamps[markName] = now(); - markCounts[markName] = getCount(markName) + 1; + counters[counterName] = (getProperty(counters, counterName) || 0) + 1; } } /** - * Gets the names of all marks. - */ - export function getMarkNames() { - return getKeys(markCounts); - } - - /** - * Gets the number of marks with the specified name. + * Gets the value of the counter with the specified name. * - * @param markName The name of the marks that should be counted. + * @param counterName The name of the counter. */ - export function getCount(markName: string) { - return enabled && getProperty(markCounts, markName) || 0; + export function getCount(counterName: string) { + return enabled && getProperty(counters, counterName) || 0; } /** - * Gets the most recent timestamp for the marks with the specified name. - * - * @param markName The name of the mark. + * Marks the start of a performance measurement. */ - export function getTimestamp(markName: string) { - return enabled && getProperty(markTimestamps, markName) || 0; - } - - /** - * Clears performance marks. - * - * @param markName The name of the mark whose time values should be cleared. If not - * specified, all marks will be cleared. - */ - export function clearMarks(markName?: string) { - if (markName === undefined) { - forEachKey(markTimestamps, clearMark); - } - else { - clearMark(markName); - } - } - - function clearMark(markName: string) { - if (delete markTimestamps[markName]) { - delete markCounts[markName]; - } + export function mark() { + return enabled ? Date.now() : 0; } /** * Adds a performance measurement with the specified name. * * @param measureName The name of the performance measurement. - * @param startMarkName The name of the starting mark. - * If provided, the most recent time value of the start mark is used. - * If not specified, the value is the time that the performance service was - * initialized or the last time it was reset. - * @param endMarkName The name of the ending mark. - * If provided, the most recent time value of the end mark is used. - * If not specified, the current time is used. + * @param marker The timestamp of the starting mark. */ - export function measure(measureName: string, startMarkName?: string, endMarkName?: string) { + export function measure(measureName: string, marker: number) { if (enabled) { - const startTime = startMarkName ? getTimestamp(startMarkName) : start; - const endTime = endMarkName ? getTimestamp(endMarkName) : now(); - const duration = endTime - startTime; - measureDurations[measureName] = getDuration(measureName) + duration; + measures[measureName] = (getProperty(measures, measureName) || 0) + (mark() - marker); } } - /** - * 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 enabled && getProperty(measureDurations, measureName) || 0; - } - - /** - * Clears performance measures. - * - * @param measureName The name of the measure whose durations should be cleared. If not - * specified, all measures will be cleared. - */ - export function clearMeasures(measureName?: string) { - if (measureName === undefined) { - forEachKey(measureDurations, clearMeasure); - } - else { - clearMeasure(measureName); - } - } - - function clearMeasure(measureName: string) { - delete measureDurations[measureName]; + return enabled && getProperty(measures, measureName) || 0; } /** * Resets all marks and measurements in the performance service. */ export function reset() { - clearMarks(); - clearMeasures(); - start = now(); + counters = {}; + measures = {}; } /** Enables performance measurements for the compiler. */ diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 40db3736b82..0516787130a 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -105,16 +105,6 @@ namespace ts { return clone; } - /** - * Gets a clone of a node with a unique node ID. - */ - export function getUniqueClone(node: T): T { - const clone = getMutableClone(node); - clone.id = 0; - getNodeId(clone); - return clone; - } - // Literals export function createLiteral(textSource: StringLiteral | Identifier, location?: TextRange): StringLiteral; @@ -1035,8 +1025,9 @@ namespace ts { } function createReactNamespace(reactNamespace: string, parent: JsxOpeningLikeElement) { - // Create an identifier and give it a parent. This allows us to resolve the react - // namespace during emit. + // To ensure the emit resolver can properly resolve the namespace, we need to + // treat this identifier as if it were a source tree node by clearing the `Synthesized` + // flag and setting a parent node. const react = createIdentifier(reactNamespace || "React"); react.flags &= ~NodeFlags.Synthesized; react.parent = parent; diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index 12d399b04d8..c577f7999f5 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -281,9 +281,9 @@ const _super = (function (geti, seti) { } function printSourceFileWithExtendedDiagnostics(node: SourceFile) { - performance.mark("printStart"); + const printStart = performance.mark(); printSourceFile(node); - performance.measure("printTime", "printStart"); + performance.measure("printTime", printStart); return node; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 38e118255d6..9fe339c3b85 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -783,9 +783,9 @@ namespace ts { function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile { let text: string; try { - performance.mark("ioReadStart"); + const ioReadStart = performance.mark(); text = sys.readFile(fileName, options.charset); - performance.measure("ioReadTime", "ioReadStart"); + performance.measure("ioReadTime", ioReadStart); } catch (e) { if (onError) { @@ -852,7 +852,7 @@ namespace ts { function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) { try { - performance.mark("ioWriteStart"); + const ioWriteStart = performance.mark(); ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) { @@ -862,7 +862,7 @@ namespace ts { sys.writeFile(fileName, data, writeByteOrderMark); } - performance.measure("ioWriteTime", "ioWriteStart"); + performance.measure("ioWriteTime", ioWriteStart); } catch (e) { if (onError) { @@ -997,7 +997,7 @@ namespace ts { let resolvedTypeReferenceDirectives: Map = {}; let fileProcessingDiagnostics = createDiagnosticCollection(); - performance.mark("programStart"); + const programStart = performance.mark(); host = host || createCompilerHost(options); @@ -1094,7 +1094,7 @@ namespace ts { verifyCompilerOptions(); - performance.measure("programTime", "programStart"); + performance.measure("programTime", programStart); return program; @@ -1335,7 +1335,7 @@ namespace ts { // checked is to not pass the file to getEmitResolver. const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile); - performance.mark("emitStart"); + const emitStart = performance.mark(); // TODO(rbuckton): remove USE_TRANSFORMS condition when we switch to transforms permanently. let useLegacyEmitter = options.useLegacyEmitter; @@ -1349,7 +1349,7 @@ namespace ts { getEmitHost(writeFileCallback), sourceFile); - performance.measure("emitTime", "emitStart"); + performance.measure("emitTime", emitStart); return emitResult; } diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 771cd7ee5ed..93676c1181d 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -782,30 +782,30 @@ namespace ts { getSourceMapData, setSourceFile, emitPos(pos: number): void { - performance.mark("sourcemapStart"); + const sourcemapStart = performance.mark(); emitPos(pos); - performance.measure("sourcemapTime", "sourcemapStart"); + performance.measure("sourceMapTime", sourcemapStart); }, emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { - performance.mark("sourcemapStart"); + const sourcemapStart = performance.mark(); emitStart(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback); - performance.measure("sourcemapTime", "sourcemapStart"); + performance.measure("sourceMapTime", sourcemapStart); }, emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { - performance.mark("sourcemapStart"); + const sourcemapStart = performance.mark(); emitEnd(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback); - performance.measure("sourcemapTime", "sourcemapStart"); + performance.measure("sourceMapTime", sourcemapStart); }, emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { - performance.mark("sourcemapStart"); + const sourcemapStart = performance.mark(); tokenStartPos = emitTokenStart(token, tokenStartPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback); - performance.measure("sourcemapTime", "sourcemapStart"); + performance.measure("sourceMapTime", sourcemapStart); return tokenStartPos; }, emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { - performance.mark("sourcemapStart"); + const sourcemapStart = performance.mark(); tokenEndPos = emitTokenEnd(token, tokenEndPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback); - performance.measure("sourcemapTime", "sourcemapStart"); + performance.measure("sourceMapTime", sourcemapStart); return tokenEndPos; }, changeEmitSourcePos, diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 527c3b00572..9103b09f6bb 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -1009,7 +1009,7 @@ namespace ts { } // `declarationName` is the name of the local declaration for the parameter. - const declarationName = getUniqueClone(parameter.name); + const declarationName = getMutableClone(parameter.name); setNodeEmitFlags(declarationName, NodeEmitFlags.NoSourceMap); // `expressionName` is the name of the parameter used in expressions. @@ -2940,7 +2940,7 @@ namespace ts { */ function getDeclarationName(node: DeclarationStatement | ClassExpression, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags?: NodeEmitFlags) { if (node.name && !isGeneratedIdentifier(node.name)) { - const name = getUniqueClone(node.name); + const name = getMutableClone(node.name); emitFlags |= getNodeEmitFlags(node.name); if (!allowSourceMaps) { emitFlags |= NodeEmitFlags.NoSourceMap; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 469c2fe8aba..1b0cb290ace 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -933,10 +933,10 @@ namespace ts { function transformParameterWithPropertyAssignment(node: ParameterDeclaration) { Debug.assert(isIdentifier(node.name)); const name = node.name as Identifier; - const propertyName = getUniqueClone(name); + const propertyName = getMutableClone(name); setNodeEmitFlags(propertyName, NodeEmitFlags.NoComments | NodeEmitFlags.NoSourceMap); - const localName = getUniqueClone(name); + const localName = getMutableClone(name); setNodeEmitFlags(localName, NodeEmitFlags.NoComments); return startOnNewLine( @@ -2953,7 +2953,7 @@ namespace ts { */ function getDeclarationName(node: DeclarationStatement | ClassExpression, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags?: NodeEmitFlags) { if (node.name) { - const name = getUniqueClone(node.name); + const name = getMutableClone(node.name); emitFlags |= getNodeEmitFlags(node.name); if (!allowSourceMaps) { emitFlags |= NodeEmitFlags.NoSourceMap; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 66d73399d62..1ce96eff259 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -586,6 +586,12 @@ namespace ts { reportStatisticalValue("Memory used", Math.round(memoryUsed / 1000) + "K"); } + if (compilerOptions.extendedDiagnostics) { + reportTimeStatistic("Print time", performance.getDuration("printTime")); + reportTimeStatistic("Comment time", performance.getDuration("commentTime")); + reportTimeStatistic("SourceMap time", performance.getDuration("sourceMapTime")); + } + // Individual component times. // Note: To match the behavior of previous versions of the compiler, the reported parse time includes // I/O read time and processing time for triple-slash references and module imports, and the reported @@ -602,44 +608,6 @@ namespace ts { reportTimeStatistic("Emit time", emitTime); reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime); - if (compilerOptions.extendedDiagnostics) { - sys.write("Extended Diagnostics:" + sys.newLine); - const marks: Mark[] = []; - for (const markName of performance.getMarkNames()) { - if (/^(ioReadStart|ioWriteStart|programStart|bindStart|checkStart|emitStart)$/.test(markName)) { - continue; - } - - marks.push({ markName, count: performance.getCount(markName) }); - } - - if (marks.length) { - marks.sort((x, y) => -compareValues(x.count, y.count)); - sys.write("Marks:" + sys.newLine); - for (const { markName, count } of marks) { - reportCountStatistic(" " + markName, count); - } - } - - const measures: Measure[] = []; - for (const measureName of performance.getMeasureNames()) { - if (/^(ioReadTime|ioWriteTime|programTime|bindTime|checkTime|emitTime)$/.test(measureName)) { - continue; - } - - measures.push({ measureName, duration: performance.getDuration(measureName) }); - } - - if (measures.length) { - measures.sort((x, y) => -compareValues(x.duration, y.duration)); - sys.write("Measures:" + sys.newLine); - for (const { measureName, duration } of measures) { - reportTimeStatistic(" " + measureName, duration); - } - } - } - - reportStatistics(); performance.disable(); performance.reset(); }