Merge pull request #8824 from Microsoft/transforms-fixPerformance

[Transforms] Performance fixes
This commit is contained in:
Ron Buckton 2016-05-27 16:19:48 -07:00
commit 2ed97896d7
22 changed files with 730 additions and 346 deletions

View File

@ -3,8 +3,6 @@
/* @internal */
namespace ts {
export let bindTime = 0;
export const enum ModuleInstanceState {
NonInstantiated = 0,
Instantiated = 1,
@ -96,9 +94,9 @@ namespace ts {
const binder = createBinder();
export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
const start = new Date().getTime();
const bindStart = performance.mark();
binder(file, options);
bindTime += new Date().getTime() - start;
performance.measure("bindTime", bindStart);
}
function createBinder(): (file: SourceFile, options: CompilerOptions) => void {

View File

@ -14,8 +14,6 @@ namespace ts {
return node.id;
}
export let checkTime = 0;
export function getSymbolId(symbol: Symbol): number {
if (!symbol.id) {
symbol.id = nextSymbolId;
@ -16066,11 +16064,11 @@ namespace ts {
}
function checkSourceFile(node: SourceFile) {
const start = new Date().getTime();
const checkStart = performance.mark();
checkSourceFileWorker(node);
checkTime += new Date().getTime() - start;
performance.measure("checkTime", checkStart);
}
// Fully type check a source file and collect the relevant diagnostics.

View File

@ -27,6 +27,11 @@ namespace ts {
name: "diagnostics",
type: "boolean",
},
{
name: "extendedDiagnostics",
type: "boolean",
experimental: true,
},
{
name: "emitBOM",
type: "boolean"

View File

@ -7,7 +7,6 @@ namespace ts {
setSourceFile(sourceFile: SourceFile): void;
getLeadingComments(range: TextRange): CommentRange[];
getLeadingComments(range: TextRange, contextNode: Node, ignoreNodeCallback: (contextNode: Node) => boolean, getTextRangeCallback: (contextNode: Node) => TextRange): CommentRange[];
getLeadingCommentsOfPosition(pos: number): CommentRange[];
getTrailingComments(range: TextRange): CommentRange[];
getTrailingComments(range: TextRange, contextNode: Node, ignoreNodeCallback: (contextNode: Node) => boolean, getTextRangeCallback: (contextNode: Node) => TextRange): CommentRange[];
getTrailingCommentsOfPosition(pos: number): CommentRange[];
@ -32,20 +31,23 @@ namespace ts {
// This maps start->end for a comment range. See `hasConsumedCommentRange` and
// `consumeCommentRange` for usage.
let consumedCommentRanges: number[];
let leadingCommentRangePositions: boolean[];
let trailingCommentRangePositions: boolean[];
let consumedCommentRanges: Map<number>;
let leadingCommentRangePositions: Map<boolean>;
let trailingCommentRangePositions: Map<boolean>;
return compilerOptions.removeComments
const commentWriter = compilerOptions.removeComments
? createCommentRemovingWriter()
: createCommentPreservingWriter();
return compilerOptions.extendedDiagnostics
? createCommentWriterWithExtendedDiagnostics(commentWriter)
: commentWriter;
function createCommentRemovingWriter(): CommentWriter {
return {
reset,
setSourceFile,
getLeadingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange): CommentRange[] { return undefined; },
getLeadingCommentsOfPosition(pos: number): CommentRange[] { return undefined; },
getTrailingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange): CommentRange[] { return undefined; },
getTrailingCommentsOfPosition(pos: number): CommentRange[] { return undefined; },
emitLeadingComments(range: TextRange, comments: CommentRange[], contextNode?: Node, getTextRangeCallback?: (contextNode: Node) => TextRange): void { },
@ -69,7 +71,6 @@ namespace ts {
reset,
setSourceFile,
getLeadingComments,
getLeadingCommentsOfPosition,
getTrailingComments,
getTrailingCommentsOfPosition,
emitLeadingComments,
@ -100,11 +101,10 @@ namespace ts {
return filter(getLeadingCommentsOfPosition(0), isTripleSlashComment);
}
return undefined;
return;
}
}
return getLeadingCommentsOfPosition(range.pos);
}
@ -127,15 +127,21 @@ namespace ts {
function getTrailingComments(range: TextRange): CommentRange[];
function getTrailingComments(range: TextRange, contextNode: Node, ignoreNodeCallback: (contextNode: Node) => boolean, getTextRangeCallback: (contextNode: Node) => TextRange): CommentRange[];
function getTrailingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange) {
let ignored = false;
if (contextNode) {
if (ignoreNodeCallback(contextNode)) {
return undefined;
ignored = true;
}
else {
range = getTextRangeCallback(contextNode) || range;
}
range = getTextRangeCallback(contextNode) || range;
}
return getTrailingCommentsOfPosition(range.end);
let comments: CommentRange[];
if (!ignored) {
comments = getTrailingCommentsOfPosition(range.end);
}
return comments;
}
function getLeadingCommentsOfPosition(pos: number) {
@ -249,6 +255,63 @@ namespace ts {
}
}
function createCommentWriterWithExtendedDiagnostics(writer: CommentWriter): CommentWriter {
const {
reset,
setSourceFile,
getLeadingComments,
getTrailingComments,
getTrailingCommentsOfPosition,
emitLeadingComments,
emitTrailingComments,
emitLeadingDetachedComments,
emitTrailingDetachedComments
} = writer;
return {
reset,
setSourceFile,
getLeadingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange): CommentRange[] {
const commentStart = performance.mark();
const comments = getLeadingComments(range, contextNode, ignoreNodeCallback, getTextRangeCallback);
performance.measure("commentTime", commentStart);
return comments;
},
getTrailingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange): CommentRange[] {
const commentStart = performance.mark();
const comments = getTrailingComments(range, contextNode, ignoreNodeCallback, getTextRangeCallback);
performance.measure("commentTime", commentStart);
return comments;
},
getTrailingCommentsOfPosition(pos: number): CommentRange[] {
const commentStart = performance.mark();
const comments = getTrailingCommentsOfPosition(pos);
performance.measure("commentTime", commentStart);
return comments;
},
emitLeadingComments(range: TextRange, comments: CommentRange[], contextNode?: Node, getTextRangeCallback?: (contextNode: Node) => TextRange): void {
const commentStart = performance.mark();
emitLeadingComments(range, comments, contextNode, getTextRangeCallback);
performance.measure("commentTime", commentStart);
},
emitTrailingComments(range: TextRange, comments: CommentRange[]): void {
const commentStart = performance.mark();
emitLeadingComments(range, comments);
performance.measure("commentTime", commentStart);
},
emitLeadingDetachedComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean): void {
const commentStart = performance.mark();
emitLeadingDetachedComments(range, contextNode, ignoreNodeCallback);
performance.measure("commentTime", commentStart);
},
emitTrailingDetachedComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean): void {
const commentStart = performance.mark();
emitTrailingDetachedComments(range, contextNode, ignoreNodeCallback);
performance.measure("commentTime", commentStart);
}
};
}
function reset() {
currentSourceFile = undefined;
currentText = undefined;
@ -264,9 +327,9 @@ namespace ts {
currentText = sourceFile.text;
currentLineMap = getLineStarts(sourceFile);
detachedCommentsInfo = undefined;
consumedCommentRanges = [];
leadingCommentRangePositions = [];
trailingCommentRangePositions = [];
consumedCommentRanges = {};
leadingCommentRangePositions = {};
trailingCommentRangePositions = {};
}
function hasDetachedComments(pos: number) {

View File

@ -1049,6 +1049,7 @@ namespace ts {
}
function Node(kind: SyntaxKind, pos: number, end: number) {
this.id = 0;
this.kind = kind;
this.pos = pos;
this.end = end;
@ -1058,6 +1059,7 @@ namespace ts {
this.excludeTransformFlags = TransformFlags.None;
this.parent = undefined;
this.original = undefined;
this.transformId = 0;
}
export let objectAllocator: ObjectAllocator = {
@ -1118,6 +1120,18 @@ namespace ts {
}
}
export function getEnvironmentVariable(name: string, host?: CompilerHost) {
if (host && host.getEnvironmentVariable) {
return host.getEnvironmentVariable(name);
}
if (sys && sys.getEnvironmentVariable) {
return sys.getEnvironmentVariable(name);
}
return "";
}
export function copyListRemovingItem<T>(item: T, list: T[]) {
const copiedList: T[] = [];
for (const e of list) {
@ -1134,4 +1148,80 @@ namespace ts {
: ((fileName) => fileName.toLowerCase());
}
/** Performance measurements for the compiler. */
/*@internal*/
export namespace performance {
let counters: Map<number>;
let measures: Map<number>;
/**
* Increments a counter with the specified name.
*
* @param counterName The name of the counter.
*/
export function increment(counterName: string) {
if (counters) {
counters[counterName] = (getProperty(counters, counterName) || 0) + 1;
}
}
/**
* Gets the value of the counter with the specified name.
*
* @param counterName The name of the counter.
*/
export function getCount(counterName: string) {
return counters && getProperty(counters, counterName) || 0;
}
/**
* Marks the start of a performance measurement.
*/
export function mark() {
return measures ? Date.now() : 0;
}
/**
* Adds a performance measurement with the specified name.
*
* @param measureName The name of the performance measurement.
* @param marker The timestamp of the starting mark.
*/
export function measure(measureName: string, marker: number) {
if (measures) {
measures[measureName] = (getProperty(measures, measureName) || 0) + (mark() - marker);
}
}
/**
* 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 measures && getProperty(measures, measureName) || 0;
}
/** Enables (and resets) performance measurements for the compiler. */
export function enable() {
counters = { };
measures = {
programTime: 0,
parseTime: 0,
bindTime: 0,
emitTime: 0,
ioReadTime: 0,
ioWriteTime: 0,
printTime: 0,
commentTime: 0,
sourceMapTime: 0
};
}
/** Disables (and clears) performance measurements for the compiler. */
export function disable() {
counters = undefined;
measures = undefined;
}
}
}

View File

@ -524,7 +524,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
const writer = createTextWriter(newLine);
const { write, writeTextOfNode, writeLine, increaseIndent, decreaseIndent } = writer;
let sourceMap = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? createSourceMapWriter(host, writer) : getNullSourceMapWriter();
let sourceMap = createSourceMapWriter(host, writer);
let { setSourceFile, emitStart, emitEnd, emitPos } = sourceMap;
let currentSourceFile: SourceFile;

View File

@ -8,7 +8,7 @@ namespace ts {
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
function createNode(kind: SyntaxKind, location?: TextRange, flags?: NodeFlags, emitOptions?: NodeEmitOptions): Node {
function createNode(kind: SyntaxKind, location?: TextRange, flags?: NodeFlags): Node {
const ConstructorForKind = kind === SyntaxKind.SourceFile
? (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))
: (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()));
@ -17,13 +17,7 @@ namespace ts {
? new ConstructorForKind(kind, location.pos, location.end)
: new ConstructorForKind(kind, /*pos*/ -1, /*end*/ -1);
if (flags) {
node.flags = flags;
}
if (emitOptions) {
node.emitOptions = emitOptions;
}
node.flags = flags | NodeFlags.Synthesized;
return node;
}
@ -68,12 +62,11 @@ namespace ts {
/**
* Creates a shallow, memberwise clone of a node with no source map location.
*/
export function getSynthesizedClone<T extends Node>(node: T, emitOptions?: NodeEmitOptions): T {
export function getSynthesizedClone<T extends Node>(node: T): T {
// We don't use "clone" from core.ts here, as we need to preserve the prototype chain of
// the original node. We also need to exclude specific properties and only include own-
// properties (to skip members already defined on the shared prototype).
const clone = <T>createNode(node.kind, /*location*/ undefined, /*flags*/ undefined, emitOptions);
clone.flags = node.flags;
const clone = <T>createNode(node.kind, /*location*/ undefined, node.flags);
clone.original = node;
for (const key in node) {
@ -90,46 +83,36 @@ namespace ts {
/**
* Creates a shallow, memberwise clone of a node for mutation.
*/
export function getMutableClone<T extends Node>(node: T, emitOptions?: NodeEmitOptions): T {
const clone = getSynthesizedClone(node, emitOptions);
export function getMutableClone<T extends Node>(node: T): T {
const clone = getSynthesizedClone(node);
clone.pos = node.pos;
clone.end = node.end;
clone.parent = node.parent;
return clone;
}
/**
* Gets a clone of a node with a unique node ID.
*/
export function getUniqueClone<T extends Node>(node: T, emitOptions?: NodeEmitOptions): T {
const clone = getMutableClone(node, emitOptions);
clone.id = undefined;
getNodeId(clone);
return clone;
}
// Literals
export function createLiteral(textSource: StringLiteral | Identifier, location?: TextRange, emitOptions?: NodeEmitOptions): StringLiteral;
export function createLiteral(value: string, location?: TextRange, emitOptions?: NodeEmitOptions): StringLiteral;
export function createLiteral(value: number, location?: TextRange, emitOptions?: NodeEmitOptions): LiteralExpression;
export function createLiteral(value: string | number | boolean, location?: TextRange, emitOptions?: NodeEmitOptions): PrimaryExpression;
export function createLiteral(value: string | number | boolean | StringLiteral | Identifier, location?: TextRange, emitOptions?: NodeEmitOptions): PrimaryExpression {
export function createLiteral(textSource: StringLiteral | Identifier, location?: TextRange): StringLiteral;
export function createLiteral(value: string, location?: TextRange): StringLiteral;
export function createLiteral(value: number, location?: TextRange): LiteralExpression;
export function createLiteral(value: string | number | boolean, location?: TextRange): PrimaryExpression;
export function createLiteral(value: string | number | boolean | StringLiteral | Identifier, location?: TextRange): PrimaryExpression {
if (typeof value === "number") {
const node = <LiteralExpression>createNode(SyntaxKind.NumericLiteral, location, /*flags*/ undefined, emitOptions);
const node = <LiteralExpression>createNode(SyntaxKind.NumericLiteral, location, /*flags*/ undefined);
node.text = value.toString();
return node;
}
else if (typeof value === "boolean") {
return <PrimaryExpression>createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword, location, /*flags*/ undefined, emitOptions);
return <PrimaryExpression>createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword, location, /*flags*/ undefined);
}
else if (typeof value === "string") {
const node = <StringLiteral>createNode(SyntaxKind.StringLiteral, location, /*flags*/ undefined, emitOptions);
const node = <StringLiteral>createNode(SyntaxKind.StringLiteral, location, /*flags*/ undefined);
node.text = value;
return node;
}
else {
const node = <StringLiteral>createNode(SyntaxKind.StringLiteral, location, /*flags*/ undefined, emitOptions);
const node = <StringLiteral>createNode(SyntaxKind.StringLiteral, location, /*flags*/ undefined);
node.textSourceNode = value;
node.text = value.text;
return node;
@ -138,17 +121,23 @@ namespace ts {
// Identifiers
let nextAutoGenerateId = 0;
export function createIdentifier(text: string, location?: TextRange): Identifier {
const node = <Identifier>createNode(SyntaxKind.Identifier, location);
node.text = escapeIdentifier(text);
node.originalKeywordKind = stringToToken(text);
node.autoGenerateKind = GeneratedIdentifierKind.None;
node.autoGenerateId = 0;
return node;
}
export function createTempVariable(recordTempVariable: (node: Identifier) => void, location?: TextRange): Identifier {
const name = <Identifier>createNode(SyntaxKind.Identifier, location);
name.text = "";
name.originalKeywordKind = SyntaxKind.Unknown;
name.autoGenerateKind = GeneratedIdentifierKind.Auto;
getNodeId(name);
name.autoGenerateId = nextAutoGenerateId++;
if (recordTempVariable) {
recordTempVariable(name);
}
@ -157,24 +146,29 @@ namespace ts {
export function createLoopVariable(location?: TextRange): Identifier {
const name = <Identifier>createNode(SyntaxKind.Identifier, location);
name.text = "";
name.originalKeywordKind = SyntaxKind.Unknown;
name.autoGenerateKind = GeneratedIdentifierKind.Loop;
getNodeId(name);
name.autoGenerateId = nextAutoGenerateId++;
return name;
}
export function createUniqueName(text: string, location?: TextRange): Identifier {
const name = <Identifier>createNode(SyntaxKind.Identifier, location);
name.text = text;
name.originalKeywordKind = SyntaxKind.Unknown;
name.autoGenerateKind = GeneratedIdentifierKind.Unique;
getNodeId(name);
name.autoGenerateId = nextAutoGenerateId++;
return name;
}
export function getGeneratedNameForNode(node: Node, location?: TextRange): Identifier {
const name = <Identifier>createNode(SyntaxKind.Identifier, location);
name.autoGenerateKind = GeneratedIdentifierKind.Node;
name.original = node;
getNodeId(name);
name.text = "";
name.originalKeywordKind = SyntaxKind.Unknown;
name.autoGenerateKind = GeneratedIdentifierKind.Node;
name.autoGenerateId = nextAutoGenerateId++;
return name;
}
@ -286,8 +280,8 @@ namespace ts {
return node;
}
export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange, emitOptions?: NodeEmitOptions) {
const node = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, location, /*flags*/ undefined, emitOptions);
export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange) {
const node = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, location, /*flags*/ undefined);
node.expression = parenthesizeForAccess(expression);
node.dotToken = createSynthesizedNode(SyntaxKind.DotToken);
node.name = typeof name === "string" ? createIdentifier(name) : name;
@ -504,8 +498,8 @@ namespace ts {
return node;
}
export function createFor(initializer: ForInitializer, condition: Expression, incrementor: Expression, statement: Statement, location?: TextRange, emitOptions?: NodeEmitOptions) {
const node = <ForStatement>createNode(SyntaxKind.ForStatement, location, /*flags*/ undefined, emitOptions);
export function createFor(initializer: ForInitializer, condition: Expression, incrementor: Expression, statement: Statement, location?: TextRange) {
const node = <ForStatement>createNode(SyntaxKind.ForStatement, location, /*flags*/ undefined);
node.initializer = initializer;
node.condition = condition;
node.incrementor = incrementor;
@ -735,9 +729,11 @@ namespace ts {
return node;
}
export function createMemberAccessForPropertyName(target: Expression, memberName: PropertyName, location?: TextRange): MemberExpression {
export function createMemberAccessForPropertyName(target: Expression, memberName: PropertyName, setNodeEmitFlags: (node: Node, flags: NodeEmitFlags) => void, location?: TextRange): MemberExpression {
if (isIdentifier(memberName)) {
return createPropertyAccess(target, memberName, location, { flags: NodeEmitFlags.NoNestedSourceMaps | NodeEmitFlags.Merge });
const expression = createPropertyAccess(target, memberName, location);
setNodeEmitFlags(expression, NodeEmitFlags.NoNestedSourceMaps);
return expression;
}
else if (isComputedPropertyName(memberName)) {
return createElementAccess(target, memberName.expression, location);
@ -815,9 +811,11 @@ 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;
return react;
}
@ -1170,26 +1168,26 @@ namespace ts {
return reduceLeft(expressions, createComma);
}
export function createExpressionFromEntityName(node: EntityName | Expression, emitOptions?: NodeEmitOptions): Expression {
export function createExpressionFromEntityName(node: EntityName | Expression): Expression {
if (isQualifiedName(node)) {
const left = createExpressionFromEntityName(node.left, emitOptions);
const right = getMutableClone(node.right, emitOptions && clone(emitOptions));
return createPropertyAccess(left, right, /*location*/ node, emitOptions && clone(emitOptions));
const left = createExpressionFromEntityName(node.left);
const right = getMutableClone(node.right);
return createPropertyAccess(left, right, /*location*/ node);
}
else {
return getMutableClone(node, emitOptions && clone(emitOptions));
return getMutableClone(node);
}
}
export function createExpressionForPropertyName(memberName: PropertyName, emitOptions?: NodeEmitOptions): Expression {
export function createExpressionForPropertyName(memberName: PropertyName): Expression {
if (isIdentifier(memberName)) {
return createLiteral(memberName, /*location*/ undefined, emitOptions);
return createLiteral(memberName, /*location*/ undefined);
}
else if (isComputedPropertyName(memberName)) {
return getMutableClone(memberName.expression, emitOptions);
return getMutableClone(memberName.expression);
}
else {
return getMutableClone(memberName, emitOptions);
return getMutableClone(memberName);
}
}

View File

@ -2034,7 +2034,7 @@ namespace ts {
// FormalParameter [Yield,Await]:
// BindingElement[?Yield,?Await]
node.name = parseIdentifierOrPattern();
if (getFullWidth(node.name) === 0 && node.flags === 0 && isModifierKind(token)) {
if (getFullWidth(node.name) === 0 && !hasModifiers(node) && isModifierKind(token)) {
// in cases like
// 'use strict'
// function foo(static)

View File

@ -151,7 +151,7 @@ const _super = (function (geti, seti) {
decreaseIndent
} = writer;
const sourceMap = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? createSourceMapWriter(host, writer) : getNullSourceMapWriter();
const sourceMap = createSourceMapWriter(host, writer);
const {
emitStart,
emitEnd,
@ -180,7 +180,8 @@ const _super = (function (geti, seti) {
let isEmitNotificationEnabled: (node: Node) => boolean;
let onSubstituteNode: (node: Node, isExpression: boolean) => Node;
let onEmitNode: (node: Node, emit: (node: Node) => void) => void;
let nodeToGeneratedName: string[];
let nodeIdToGeneratedName: string[];
let autoGeneratedIdToGeneratedName: string[];
let generatedNameSet: Map<string>;
let tempFlags: TempFlags;
let currentSourceFile: SourceFile;
@ -197,7 +198,8 @@ const _super = (function (geti, seti) {
function doPrint(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
nodeToGeneratedName = [];
nodeIdToGeneratedName = [];
autoGeneratedIdToGeneratedName = [];
generatedNameSet = {};
isOwnFileEmit = !isBundledEmit;
@ -265,7 +267,7 @@ const _super = (function (geti, seti) {
isEmitNotificationEnabled = context.isEmitNotificationEnabled;
onSubstituteNode = context.onSubstituteNode;
onEmitNode = context.onEmitNode;
return printSourceFile;
return compilerOptions.extendedDiagnostics ? printSourceFileWithExtendedDiagnostics : printSourceFile;
}
function printSourceFile(node: SourceFile) {
@ -278,6 +280,13 @@ const _super = (function (geti, seti) {
return node;
}
function printSourceFileWithExtendedDiagnostics(node: SourceFile) {
const printStart = performance.mark();
printSourceFile(node);
performance.measure("printTime", printStart);
return node;
}
/**
* Emits a node.
*/
@ -2706,6 +2715,11 @@ const _super = (function (geti, seti) {
return makeUniqueName("class");
}
/**
* Generates a unique name from a node.
*
* @param node A node.
*/
function generateNameForNode(node: Node): string {
switch (node.kind) {
case SyntaxKind.Identifier:
@ -2727,46 +2741,69 @@ const _super = (function (geti, seti) {
}
}
function generateIdentifier(node: Identifier) {
switch (node.autoGenerateKind) {
/**
* Generates a unique identifier for a node.
*
* @param name A generated name.
*/
function generateName(name: Identifier) {
switch (name.autoGenerateKind) {
case GeneratedIdentifierKind.Auto:
return makeTempVariableName(TempFlags.Auto);
case GeneratedIdentifierKind.Loop:
return makeTempVariableName(TempFlags._i);
case GeneratedIdentifierKind.Unique:
return makeUniqueName(node.text);
case GeneratedIdentifierKind.Node:
return generateNameForNode(getSourceNodeForGeneratedName(node));
return makeUniqueName(name.text);
}
Debug.fail("Unsupported GeneratedIdentifierKind.");
}
function getGeneratedIdentifier(node: Identifier) {
const id = getNodeIdForGeneratedIdentifier(node);
return nodeToGeneratedName[id] || (nodeToGeneratedName[id] = unescapeIdentifier(generateIdentifier(node)));
}
/**
* Gets the node from which a name should be generated.
*
* @param name A generated name wrapper.
*/
function getNodeForGeneratedName(name: Identifier) {
const autoGenerateId = name.autoGenerateId;
let node = name as Node;
let original = node.original;
while (original) {
node = original;
function getSourceNodeForGeneratedName(name: Identifier) {
let node: Node = name;
while (node.original !== undefined) {
const nodeId = node.id;
node = node.original;
// If "node" is not the exact clone of "original" identifier, use "original" identifier to generate the name
if (isIdentifier(node) && node.autoGenerateKind === GeneratedIdentifierKind.Node && node.id !== nodeId) {
// if "node" is a different generated name (having a different
// "autoGenerateId"), use it and stop traversing.
if (isIdentifier(node)
&& node.autoGenerateKind === GeneratedIdentifierKind.Node
&& node.autoGenerateId !== autoGenerateId) {
break;
}
original = node.original;
}
// otherwise, return the original node for the source;
return node;
}
function getNodeIdForGeneratedIdentifier(node: Identifier) {
switch (node.autoGenerateKind) {
case GeneratedIdentifierKind.Auto:
case GeneratedIdentifierKind.Loop:
case GeneratedIdentifierKind.Unique:
return getNodeId(node);
case GeneratedIdentifierKind.Node:
return getNodeId(getSourceNodeForGeneratedName(node));
/**
* Gets the generated identifier text from a generated identifier.
*
* @param name The generated identifier.
*/
function getGeneratedIdentifier(name: Identifier) {
if (name.autoGenerateKind === GeneratedIdentifierKind.Node) {
// Generated names generate unique names based on their original node
// and are cached based on that node's id
const node = getNodeForGeneratedName(name);
const nodeId = getNodeId(node);
return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(node)));
}
else {
// Auto, Loop, and Unique names are cached based on their unique
// autoGenerateId.
const autoGenerateId = name.autoGenerateId;
return autoGeneratedIdToGeneratedName[autoGenerateId] || (autoGeneratedIdToGeneratedName[autoGenerateId] = unescapeIdentifier(generateName(name)));
}
}
}

View File

@ -4,11 +4,6 @@
/// <reference path="printer.ts" />
namespace ts {
/* @internal */ export let programTime = 0;
/* @internal */ export let emitTime = 0;
/* @internal */ export let ioReadTime = 0;
/* @internal */ export let ioWriteTime = 0;
/** The version of the TypeScript compiler release */
const emptyArray: any[] = [];
@ -781,9 +776,9 @@ namespace ts {
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile {
let text: string;
try {
const start = new Date().getTime();
const ioReadStart = performance.mark();
text = sys.readFile(fileName, options.charset);
ioReadTime += new Date().getTime() - start;
performance.measure("ioReadTime", ioReadStart);
}
catch (e) {
if (onError) {
@ -850,7 +845,7 @@ namespace ts {
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
try {
const start = new Date().getTime();
const ioWriteStart = performance.mark();
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) {
@ -860,7 +855,7 @@ namespace ts {
sys.writeFile(fileName, data, writeByteOrderMark);
}
ioWriteTime += new Date().getTime() - start;
performance.measure("ioWriteTime", ioWriteStart);
}
catch (e) {
if (onError) {
@ -962,7 +957,7 @@ namespace ts {
let resolvedTypeReferenceDirectives: Map<ResolvedTypeReferenceDirective> = {};
let fileProcessingDiagnostics = createDiagnosticCollection();
const start = new Date().getTime();
const programStart = performance.mark();
host = host || createCompilerHost(options);
@ -1055,7 +1050,7 @@ namespace ts {
verifyCompilerOptions();
programTime += new Date().getTime() - start;
performance.measure("programTime", programStart);
return program;
@ -1288,7 +1283,7 @@ namespace ts {
// checked is to not pass the file to getEmitResolver.
const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile);
const start = new Date().getTime();
const emitStart = performance.mark();
// TODO(rbuckton): remove USE_TRANSFORMS condition when we switch to transforms permanently.
let useLegacyEmitter = options.useLegacyEmitter;
@ -1302,7 +1297,8 @@ namespace ts {
getEmitHost(writeFileCallback),
sourceFile);
emitTime += new Date().getTime() - start;
performance.measure("emitTime", emitStart);
return emitResult;
}
@ -2076,7 +2072,7 @@ namespace ts {
}
// Cannot specify module gen that isn't amd or system with --out
// Report this error if user specified --module moduleKind
// Report this error if user specified --module moduleKind
// or if there is external module in compilation which defaults to commonjs
const emitModuleKind = getEmitModuleKind(options);
if (outFile && (options.module || firstExternalModuleSourceFile) && !(emitModuleKind === ModuleKind.AMD || emitModuleKind === ModuleKind.System)) {

View File

@ -593,7 +593,7 @@ namespace ts {
function getCommentRanges(text: string, pos: number, trailing: boolean): CommentRange[] {
let result: CommentRange[];
let collecting = trailing || pos === 0;
while (pos < text.length) {
while (pos >= 0 && pos < text.length) {
const ch = text.charCodeAt(pos);
switch (ch) {
case CharacterCodes.carriageReturn:

View File

@ -170,6 +170,20 @@ namespace ts {
getSourceMappingURL(): string;
}
export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
const compilerOptions = host.getCompilerOptions();
if (compilerOptions.sourceMap || compilerOptions.inlineSourceMap) {
if (compilerOptions.extendedDiagnostics) {
return createSourceMapWriterWithExtendedDiagnostics(host, writer);
}
return createSourceMapWriterWorker(host, writer);
}
else {
return getNullSourceMapWriter();
}
}
let nullSourceMapWriter: SourceMapWriter;
export function getNullSourceMapWriter(): SourceMapWriter {
@ -182,8 +196,8 @@ namespace ts {
emitPos(pos: number): void { },
emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { },
emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { },
emitTokenStart(token: SyntaxKind, pos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind, pos: number) => TextRange): number { return -1; },
emitTokenEnd(token: SyntaxKind, end: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind, pos: number) => TextRange): number { return -1; },
emitTokenStart(token: SyntaxKind, pos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { return -1; },
emitTokenEnd(token: SyntaxKind, end: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { return -1; },
changeEmitSourcePos(): void { },
stopOverridingSpan(): void { },
getText(): string { return undefined; },
@ -203,7 +217,7 @@ namespace ts {
sourceIndex: 0
};
export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
function createSourceMapWriterWorker(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
const compilerOptions = host.getCompilerOptions();
let currentSourceFile: SourceFile;
let currentSourceText: string;
@ -746,6 +760,61 @@ namespace ts {
}
}
function createSourceMapWriterWithExtendedDiagnostics(host: EmitHost, writer: EmitTextWriter): SourceMapWriter {
const {
initialize,
reset,
getSourceMapData,
setSourceFile,
emitPos,
emitStart,
emitEnd,
emitTokenStart,
emitTokenEnd,
changeEmitSourcePos,
stopOverridingSpan,
getText,
getSourceMappingURL,
} = createSourceMapWriterWorker(host, writer);
return {
initialize,
reset,
getSourceMapData,
setSourceFile,
emitPos(pos: number): void {
const sourcemapStart = performance.mark();
emitPos(pos);
performance.measure("sourceMapTime", sourcemapStart);
},
emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void {
const sourcemapStart = performance.mark();
emitStart(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback);
performance.measure("sourceMapTime", sourcemapStart);
},
emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void {
const sourcemapStart = performance.mark();
emitEnd(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback);
performance.measure("sourceMapTime", sourcemapStart);
},
emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
const sourcemapStart = performance.mark();
tokenStartPos = emitTokenStart(token, tokenStartPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback);
performance.measure("sourceMapTime", sourcemapStart);
return tokenStartPos;
},
emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number {
const sourcemapStart = performance.mark();
tokenEndPos = emitTokenEnd(token, tokenEndPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback);
performance.measure("sourceMapTime", sourcemapStart);
return tokenEndPos;
},
changeEmitSourcePos,
stopOverridingSpan,
getText,
getSourceMappingURL,
};
}
const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
function base64FormatEncode(inValue: number) {

View File

@ -23,6 +23,100 @@ namespace ts {
EmitNotifications = 1 << 1,
}
/* @internal */
export interface TransformationContext extends LexicalEnvironment {
getCompilerOptions(): CompilerOptions;
getEmitResolver(): EmitResolver;
getEmitHost(): EmitHost;
/**
* Gets flags used to customize later transformations or emit.
*/
getNodeEmitFlags(node: Node): NodeEmitFlags;
/**
* Sets flags used to customize later transformations or emit.
*/
setNodeEmitFlags<T extends Node>(node: T, flags: NodeEmitFlags): T;
/**
* Gets the TextRange to use for source maps for the node.
*/
getSourceMapRange(node: Node): TextRange;
/**
* Sets the TextRange to use for source maps for the node.
*/
setSourceMapRange<T extends Node>(node: T, range: TextRange): T;
/**
* Gets the TextRange to use for source maps for a token of a node.
*/
getTokenSourceMapRange(node: Node, token: SyntaxKind): TextRange;
/**
* Sets the TextRange to use for source maps for a token of a node.
*/
setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: TextRange): T;
/**
* Gets the TextRange to use for comments for the node.
*/
getCommentRange(node: Node): TextRange;
/**
* Sets the TextRange to use for comments for the node.
*/
setCommentRange<T extends Node>(node: T, range: TextRange): T;
/**
* Hoists a function declaration to the containing scope.
*/
hoistFunctionDeclaration(node: FunctionDeclaration): void;
/**
* Hoists a variable declaration to the containing scope.
*/
hoistVariableDeclaration(node: Identifier): void;
/**
* Enables expression substitutions in the pretty printer for the provided SyntaxKind.
*/
enableSubstitution(kind: SyntaxKind): void;
/**
* Determines whether expression substitutions are enabled for the provided node.
*/
isSubstitutionEnabled(node: Node): boolean;
/**
* Hook used by transformers to substitute expressions just before they
* are emitted by the pretty printer.
*/
onSubstituteNode?: (node: Node, isExpression: boolean) => Node;
/**
* Enables before/after emit notifications in the pretty printer for the provided
* SyntaxKind.
*/
enableEmitNotification(kind: SyntaxKind): void;
/**
* Determines whether before/after emit notifications should be raised in the pretty
* printer when it emits a node.
*/
isEmitNotificationEnabled(node: Node): boolean;
/**
* Hook used to allow transformers to capture state before or after
* the printer emits a node.
*/
onEmitNode?: (node: Node, emit: (node: Node) => void) => void;
}
/* @internal */
export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile;
export function getTransformers(compilerOptions: CompilerOptions) {
const jsx = compilerOptions.jsx;
const languageVersion = getEmitScriptTarget(compilerOptions);
@ -45,6 +139,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,10 +156,22 @@ namespace ts {
* @param transforms An array of Transformers.
*/
export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]) {
const nodeEmitOptions: NodeEmitOptions[] = [];
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;
let lastSourceMapRange: TextRange;
let lastTokenSourceMapRangeNode: Node;
let lastTokenSourceMapRangeToken: SyntaxKind;
let lastTokenSourceMapRange: TextRange;
let lastCommentMapRangeNode: Node;
let lastCommentMapRange: TextRange;
let lexicalEnvironmentStackOffset = 0;
let hoistedVariableDeclarations: VariableDeclaration[];
let hoistedFunctionDeclarations: FunctionDeclaration[];
@ -107,7 +221,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;
}
/**
@ -170,143 +297,233 @@ namespace ts {
emit(node);
}
function getEmitOptions(node: Node, create?: boolean) {
let options = isSourceTreeNode(node)
? nodeEmitOptions[getNodeId(node)]
: node.emitOptions;
if (!options && create) {
options = { };
if (isSourceTreeNode(node)) {
nodeEmitOptions[getNodeId(node)] = options;
}
else {
node.emitOptions = options;
/**
* 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);
}
}
return options;
}
/**
* Gets flags that control emit behavior of a node.
*
* If the node does not have its own NodeEmitFlags set, the node emit flags of its
* original pointer are used.
*
* @param node The node.
*/
function getNodeEmitFlags(node: Node) {
while (node) {
const options = getEmitOptions(node, /*create*/ false);
if (options && options.flags !== undefined) {
if (options.flags & NodeEmitFlags.Merge) {
options.flags = (options.flags | getNodeEmitFlags(node.original)) & ~NodeEmitFlags.Merge;
}
return options.flags;
}
node = node.original;
// 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 (lastNodeEmitFlagsNode === node) {
return lastNodeEmitFlags;
}
return undefined;
// Get the emit flags for a node or from one of its original nodes.
let flags: NodeEmitFlags;
let current = node;
while (current) {
if (current.transformId === transformId) {
const nodeEmitFlags = current.emitFlags;
if (nodeEmitFlags) {
flags = nodeEmitFlags & ~NodeEmitFlags.HasNodeEmitFlags;
break;
}
}
current = current.original;
}
// Cache the most recently requested value.
lastNodeEmitFlagsNode = node;
lastNodeEmitFlags = flags;
return flags;
}
/**
* Sets flags that control emit behavior of a node.
*
* @param node The node.
* @param emitFlags The NodeEmitFlags for the node.
*/
function setNodeEmitFlags<T extends Node>(node: T, flags: NodeEmitFlags) {
const options = getEmitOptions(node, /*create*/ true);
if (flags & NodeEmitFlags.Merge) {
flags = options.flags | (flags & ~NodeEmitFlags.Merge);
function setNodeEmitFlags<T extends Node>(node: T, emitFlags: NodeEmitFlags) {
// Merge existing flags.
if (emitFlags & NodeEmitFlags.Merge) {
emitFlags = getNodeEmitFlags(node) | (emitFlags & ~NodeEmitFlags.Merge);
}
options.flags = flags;
beforeSetAnnotation(node);
// Cache the most recently requested value.
lastNodeEmitFlagsNode = node;
lastNodeEmitFlags = emitFlags;
node.emitFlags = emitFlags | NodeEmitFlags.HasNodeEmitFlags;
return node;
}
/**
* Gets a custom text range to use when emitting source maps.
*
* If a node does not have its own custom source map text range, the custom source map
* text range of its original pointer is used.
*
* @param node The node.
*/
function getSourceMapRange(node: Node) {
// 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 (lastSourceMapRangeNode === node) {
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) {
const options = getEmitOptions(current);
if (options && options.sourceMapRange !== undefined) {
return options.sourceMapRange;
if (current.transformId === transformId) {
range = current.sourceMapRange;
if (range !== undefined) {
break;
}
}
current = current.original;
}
return node;
// Cache the most recently requested value.
lastSourceMapRangeNode = node;
lastSourceMapRange = range;
return range || node;
}
/**
* Sets a custom text range to use when emitting source maps.
*
* @param node The node.
* @param range The text range.
*/
function setSourceMapRange<T extends Node>(node: T, range: TextRange) {
getEmitOptions(node, /*create*/ true).sourceMapRange = range;
beforeSetAnnotation(node);
// Cache the most recently requested value.
lastSourceMapRangeNode = node;
lastSourceMapRange = range;
node.sourceMapRange = range;
return node;
}
function getTokenSourceMapRanges(node: Node) {
/**
* Gets the TextRange to use for source maps for a token of a node.
*
* If a node does not have its own custom source map text range for a token, the custom
* source map text range for the token of its original pointer is used.
*
* @param node The node.
* @param token The token.
*/
function getTokenSourceMapRange(node: Node, token: SyntaxKind) {
// 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 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) {
const options = getEmitOptions(current);
if (options && options.tokenSourceMapRange) {
return options.tokenSourceMapRange;
range = current.id ? tokenSourceMapRanges[current.id + "-" + token] : undefined;
if (range !== undefined) {
break;
}
current = current.original;
}
return undefined;
}
/**
* Gets the TextRange to use for source maps for a token of a node.
*/
function getTokenSourceMapRange(node: Node, token: SyntaxKind) {
const ranges = getTokenSourceMapRanges(node);
if (ranges) {
return ranges[token];
}
return undefined;
// Cache the most recently requested value.
lastTokenSourceMapRangeNode = node;
lastTokenSourceMapRangeToken = token;
lastTokenSourceMapRange = range;
return range;
}
/**
* Sets the TextRange to use for source maps for a token of a node.
*
* @param node The node.
* @param token The token.
* @param range The text range.
*/
function setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: TextRange) {
const options = getEmitOptions(node, /*create*/ true);
if (!options.tokenSourceMapRange) {
const existingRanges = getTokenSourceMapRanges(node);
const ranges = existingRanges ? clone(existingRanges) : { };
options.tokenSourceMapRange = ranges;
}
options.tokenSourceMapRange[token] = range;
// Cache the most recently requested value.
lastTokenSourceMapRangeNode = node;
lastTokenSourceMapRangeToken = token;
lastTokenSourceMapRange = range;
tokenSourceMapRanges[getNodeId(node) + "-" + token] = range;
return node;
}
/**
* Gets a custom text range to use when emitting comments.
*
* If a node does not have its own custom source map text range, the custom source map
* text range of its original pointer is used.
*
* @param node The node.
*/
function getCommentRange(node: Node) {
// 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 (lastCommentMapRangeNode === node) {
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) {
const options = getEmitOptions(current, /*create*/ false);
if (options && options.commentRange !== undefined) {
return options.commentRange;
if (current.transformId === transformId) {
range = current.commentRange;
if (range !== undefined) {
break;
}
}
current = current.original;
}
return node;
// Cache the most recently requested value.
lastCommentMapRangeNode = node;
lastCommentMapRange = range;
return range || node;
}
/**
* Sets a custom text range to use when emitting comments.
*/
function setCommentRange<T extends Node>(node: T, range: TextRange) {
getEmitOptions(node, /*create*/ true).commentRange = range;
beforeSetAnnotation(node);
// Cache the most recently requested value.
lastCommentMapRangeNode = node;
lastCommentMapRange = range;
node.commentRange = range;
return node;
}

View File

@ -251,7 +251,9 @@ namespace ts {
emitArrayLiteralAssignment(<ArrayLiteralExpression>target, value, location);
}
else {
const name = getSynthesizedClone(<Identifier>target, { sourceMapRange: target, commentRange: target });
const name = getMutableClone(<Identifier>target);
context.setSourceMapRange(name, target);
context.setCommentRange(name, target);
emitAssignment(name, value, location, /*original*/ undefined);
}
}

View File

@ -164,7 +164,7 @@ namespace ts {
let currentNode: Node;
let enclosingBlockScopeContainer: Node;
let enclosingBlockScopeContainerParent: Node;
let containingNonArrowFunction: FunctionLikeDeclaration;
let containingNonArrowFunction: FunctionLikeDeclaration | ClassElement;
/**
* Used to track if we are emitting body of the converted loop
@ -1003,7 +1003,7 @@ namespace ts {
}
// `declarationName` is the name of the local declaration for the parameter.
const declarationName = getUniqueClone(<Identifier>parameter.name);
const declarationName = getMutableClone(<Identifier>parameter.name);
setNodeEmitFlags(declarationName, NodeEmitFlags.NoSourceMap);
// `expressionName` is the name of the parameter used in expressions.
@ -1150,6 +1150,7 @@ namespace ts {
createMemberAccessForPropertyName(
receiver,
visitNode(member.name, visitor, isPropertyName),
setNodeEmitFlags,
/*location*/ member.name
),
func
@ -2311,7 +2312,8 @@ namespace ts {
return createAssignment(
createMemberAccessForPropertyName(
receiver,
visitNode(property.name, visitor, isPropertyName)
visitNode(property.name, visitor, isPropertyName),
setNodeEmitFlags
),
visitNode(property.initializer, visitor, isExpression),
/*location*/ property
@ -2329,7 +2331,8 @@ namespace ts {
return createAssignment(
createMemberAccessForPropertyName(
receiver,
visitNode(property.name, visitor, isPropertyName)
visitNode(property.name, visitor, isPropertyName),
setNodeEmitFlags
),
getSynthesizedClone(property.name),
/*location*/ property
@ -2347,7 +2350,8 @@ namespace ts {
return createAssignment(
createMemberAccessForPropertyName(
receiver,
visitNode(method.name, visitor, isPropertyName)
visitNode(method.name, visitor, isPropertyName),
setNodeEmitFlags
),
transformFunctionLikeToExpression(method, /*location*/ method, /*name*/ undefined),
/*location*/ method
@ -2912,7 +2916,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;

View File

@ -100,7 +100,7 @@ namespace ts {
exportFunctionForFile = createUniqueName("exports");
contextObjectForFile = createUniqueName("context");
exportFunctionForFileMap[getNodeId(node)] = exportFunctionForFile;
exportFunctionForFileMap[getOriginalNodeId(node)] = exportFunctionForFile;
const dependencyGroups = collectDependencyGroups(externalImports);
@ -968,7 +968,7 @@ namespace ts {
function onEmitNode(node: Node, emit: (node: Node) => void): void {
if (node.kind === SyntaxKind.SourceFile) {
exportFunctionForFile = exportFunctionForFileMap[getNodeId(node)];
exportFunctionForFile = exportFunctionForFileMap[getOriginalNodeId(node)];
previousOnEmitNode(node, emit);
exportFunctionForFile = undefined;
}

View File

@ -1034,7 +1034,7 @@ namespace ts {
function transformInitializedProperty(node: ClassExpression | ClassDeclaration, property: PropertyDeclaration, receiver: LeftHandSideExpression) {
const propertyName = visitPropertyNameOfClassElement(property);
const initializer = visitNode(property.initializer, visitor, isExpression);
const memberAccess = createMemberAccessForPropertyName(receiver, propertyName, /*location*/ propertyName);
const memberAccess = createMemberAccessForPropertyName(receiver, propertyName, setNodeEmitFlags, /*location*/ propertyName);
if (!isComputedPropertyName(propertyName)) {
setNodeEmitFlags(memberAccess, NodeEmitFlags.NoNestedSourceMaps);
}
@ -1753,7 +1753,10 @@ namespace ts {
function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): Expression {
switch (node.kind) {
case SyntaxKind.Identifier:
// Create a clone of the name with a new parent, and treat it as if it were
// a source tree node for the purposes of the checker.
const name = getMutableClone(<Identifier>node);
name.flags &= ~NodeFlags.Synthesized;
name.original = undefined;
name.parent = currentScope;
if (useFallback) {
@ -2714,6 +2717,11 @@ namespace ts {
&& resolver.isTopLevelValueImportEqualsWithEntityName(node));
}
function disableCommentsRecursive(node: Node) {
setNodeEmitFlags(node, NodeEmitFlags.NoComments | NodeEmitFlags.Merge);
forEachChild(node, disableCommentsRecursive);
}
/**
* Visits an import equals declaration.
*
@ -2728,7 +2736,8 @@ namespace ts {
return undefined;
}
const moduleReference = createExpressionFromEntityName(<EntityName>node.moduleReference, { flags: NodeEmitFlags.NoComments });
const moduleReference = createExpressionFromEntityName(<EntityName>node.moduleReference);
disableCommentsRecursive(moduleReference);
if (isNamedExternalModuleExport(node) || !isNamespaceExport(node)) {
// export var ${name} = ${moduleReference};
// var ${name} = ${moduleReference};
@ -2913,7 +2922,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;
@ -3150,9 +3159,12 @@ namespace ts {
// behavior of class names in ES6.
const declaration = resolver.getReferencedValueDeclaration(node);
if (declaration) {
const classAlias = currentDecoratedClassAliases[getNodeId(declaration)];
const classAlias = currentDecoratedClassAliases[getOriginalNodeId(declaration)];
if (classAlias) {
return getSynthesizedClone(classAlias, { sourceMapRange: node, commentRange: node });
const clone = getSynthesizedClone(classAlias);
setSourceMapRange(clone, node);
setCommentRange(clone, node);
return clone;
}
}
}

View File

@ -238,7 +238,7 @@ namespace ts {
}
function reportStatisticalValue(name: string, value: string) {
sys.write(padRight(name + ":", 12) + padLeft(value.toString(), 10) + sys.newLine);
sys.write(padRight(name + ":", 20) + padLeft(value.toString(), 10) + sys.newLine);
}
function reportCountStatistic(name: string, count: number) {
@ -544,12 +544,9 @@ namespace ts {
}
function compile(fileNames: string[], compilerOptions: CompilerOptions, compilerHost: CompilerHost) {
ioReadTime = 0;
ioWriteTime = 0;
programTime = 0;
bindTime = 0;
checkTime = 0;
emitTime = 0;
if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) {
performance.enable();
}
const program = createProgram(fileNames, compilerOptions, compilerHost);
const exitStatus = compileProgram();
@ -560,7 +557,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));
@ -573,17 +570,29 @@ 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
// emit time includes I/O write time. We preserve this behavior so we can accurately compare times.
reportTimeStatistic("I/O read", ioReadTime);
reportTimeStatistic("I/O write", ioWriteTime);
reportTimeStatistic("I/O read", performance.getDuration("ioReadTime"));
reportTimeStatistic("I/O write", performance.getDuration("ioWriteTime"));
const programTime = performance.getDuration("programTime");
const bindTime = performance.getDuration("bindTime");
const checkTime = performance.getDuration("checkTime");
const emitTime = performance.getDuration("emitTime");
reportTimeStatistic("Parse time", programTime);
reportTimeStatistic("Bind time", bindTime);
reportTimeStatistic("Check time", checkTime);
reportTimeStatistic("Emit time", emitTime);
reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime);
performance.disable();
}
return { program, exitStatus };

View File

@ -383,6 +383,7 @@ namespace ts {
Let = 1 << 0, // Variable declaration
Const = 1 << 1, // Variable declaration
NestedNamespace = 1 << 2, // Namespace declaration
Synthesized = 1 << 3, // Node was synthesized during transformation
Namespace = 1 << 12, // Namespace declaration
ExportContext = 1 << 13, // Export context (initialized by binding)
ContainsThis = 1 << 14, // Interface contains references to "this"
@ -469,7 +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 */ emitOptions?: NodeEmitOptions; // Options used to control node emit (used by transforms, should never be set directly on a source tree node)
/* @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 {
@ -502,6 +506,7 @@ namespace ts {
text: string; // Text of identifier (with escapes converted to characters)
originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later
/*@internal*/ autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier.
/*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name.
}
// Transient identifier node (marked by id === -1)
@ -2475,6 +2480,7 @@ namespace ts {
declaration?: boolean;
declarationDir?: string;
diagnostics?: boolean;
/*@internal*/ extendedDiagnostics?: boolean;
emitBOM?: boolean;
help?: boolean;
init?: boolean;
@ -2955,26 +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.
}
/* @internal */
export interface NodeEmitOptions {
/**
* Specifies a custom range to use when emitting source maps.
*/
sourceMapRange?: TextRange;
/**
* Specifies a custom range to use when emitting tokens of a node.
*/
tokenSourceMapRange?: Map<TextRange>;
/**
* Specifies a custom range to use when emitting comments.
*/
commentRange?: TextRange;
/**
* Specifies flags to use to customize emit.
*/
flags?: NodeEmitFlags;
HasNodeEmitFlags = 1 << 31, // Indicates the node has emit flags set.
}
/** Additional context provided to `visitEachChild` */
@ -2987,99 +2975,6 @@ namespace ts {
endLexicalEnvironment(): Statement[];
}
/* @internal */
export interface TransformationContext extends LexicalEnvironment {
getCompilerOptions(): CompilerOptions;
getEmitResolver(): EmitResolver;
getEmitHost(): EmitHost;
/**
* Gets flags used to customize later transformations or emit.
*/
getNodeEmitFlags(node: Node): NodeEmitFlags;
/**
* Sets flags used to customize later transformations or emit.
*/
setNodeEmitFlags<T extends Node>(node: T, flags: NodeEmitFlags): T;
/**
* Gets the TextRange to use for source maps for the node.
*/
getSourceMapRange(node: Node): TextRange;
/**
* Sets the TextRange to use for source maps for the node.
*/
setSourceMapRange<T extends Node>(node: T, range: TextRange): T;
/**
* Gets the TextRange to use for source maps for a token of a node.
*/
getTokenSourceMapRange(node: Node, token: SyntaxKind): TextRange;
/**
* Sets the TextRange to use for source maps for a token of a node.
*/
setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: TextRange): T;
/**
* Gets the TextRange to use for comments for the node.
*/
getCommentRange(node: Node): TextRange;
/**
* Sets the TextRange to use for comments for the node.
*/
setCommentRange<T extends Node>(node: T, range: TextRange): T;
/**
* Hoists a function declaration to the containing scope.
*/
hoistFunctionDeclaration(node: FunctionDeclaration): void;
/**
* Hoists a variable declaration to the containing scope.
*/
hoistVariableDeclaration(node: Identifier): void;
/**
* Enables expression substitutions in the pretty printer for the provided SyntaxKind.
*/
enableSubstitution(kind: SyntaxKind): void;
/**
* Determines whether expression substitutions are enabled for the provided node.
*/
isSubstitutionEnabled(node: Node): boolean;
/**
* Hook used by transformers to substitute expressions just before they
* are emitted by the pretty printer.
*/
onSubstituteNode?: (node: Node, isExpression: boolean) => Node;
/**
* Enables before/after emit notifications in the pretty printer for the provided
* SyntaxKind.
*/
enableEmitNotification(kind: SyntaxKind): void;
/**
* Determines whether before/after emit notifications should be raised in the pretty
* printer when it emits a node.
*/
isEmitNotificationEnabled(node: Node): boolean;
/**
* Hook used to allow transformers to capture state before or after
* the printer emits a node.
*/
onEmitNode?: (node: Node, emit: (node: Node) => void) => void;
}
/* @internal */
export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile;
export interface TextSpan {
start: number;

View File

@ -207,18 +207,6 @@ namespace ts {
return `${ file.fileName }(${ loc.line + 1 },${ loc.character + 1 })`;
}
export function getEnvironmentVariable(name: string, host?: CompilerHost) {
if (host && host.getEnvironmentVariable) {
return host.getEnvironmentVariable(name);
}
if (sys && sys.getEnvironmentVariable) {
return sys.getEnvironmentVariable(name);
}
return "";
}
export function getStartPosOfNode(node: Node): number {
return node.pos;
}
@ -1831,14 +1819,17 @@ namespace ts {
}
export function isSourceTreeNode(node: Node): boolean {
return node.original === undefined
&& (node.parent !== undefined || node.kind === SyntaxKind.SourceFile);
return (node.flags & NodeFlags.Synthesized) === 0;
}
export function getSourceTreeNode(node: Node): Node {
if (isSourceTreeNode(node)) {
return node;
}
node = getOriginalNode(node);
if (node && (node.parent !== undefined || node.kind === SyntaxKind.SourceFile)) {
if (isSourceTreeNode(node)) {
return node;
}

View File

@ -113,7 +113,7 @@ false = value;
}
value;
// array literals
"" = value[0], "" = value[1];
'' = value[0], '' = value[1];
// super
var Derived = (function (_super) {
__extends(Derived, _super);

View File

@ -134,7 +134,7 @@ false = Math.pow(false, value);
}
value;
// array literals
_a = Math.pow(['', ''], value), "" = _a[0], "" = _a[1];
_a = Math.pow(['', ''], value), '' = _a[0], '' = _a[1];
// super
var Derived = (function (_super) {
__extends(Derived, _super);