diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 03f785dc7de..6ad91069033 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1143,10 +1143,6 @@ const _super = (function (geti, seti) { } function emitPropertyAccessExpression(node: PropertyAccessExpression) { - if (tryEmitConstantValue(node)) { - return; - } - let indentBeforeDot = false; let indentAfterDot = false; if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { @@ -1156,11 +1152,13 @@ const _super = (function (geti, seti) { indentBeforeDot = needsIndentation(node, node.expression, dotToken); indentAfterDot = needsIndentation(node, dotToken, node.name); } - const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); emitExpression(node.expression); increaseIndentIf(indentBeforeDot); + + const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); write(shouldEmitDotDot ? ".." : "."); + increaseIndentIf(indentAfterDot); emit(node.name); decreaseIndentIf(indentBeforeDot, indentAfterDot); @@ -1176,17 +1174,15 @@ const _super = (function (geti, seti) { } else { // check if constant enum value is integer - const constantValue = tryGetConstEnumValue(expression); + const constantValue = getConstantValue(expression); // isFinite handles cases when constantValue is undefined - return isFinite(constantValue) && Math.floor(constantValue) === constantValue; + return isFinite(constantValue) + && Math.floor(constantValue) === constantValue + && compilerOptions.removeComments; } } function emitElementAccessExpression(node: ElementAccessExpression) { - if (tryEmitConstantValue(node)) { - return; - } - emitExpression(node.expression); write("["); emitExpression(node.argumentExpression); @@ -2260,24 +2256,6 @@ const _super = (function (geti, seti) { } } - // TODO(rbuckton): Move this into a transformer - function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean { - const constantValue = tryGetConstEnumValue(node); - if (constantValue !== undefined) { - write(String(constantValue)); - if (!compilerOptions.removeComments) { - const propertyName = isPropertyAccessExpression(node) - ? declarationNameToString(node.name) - : getTextOfNode(node.argumentExpression); - write(` /* ${propertyName} */`); - } - - return true; - } - - return false; - } - function emitEmbeddedStatement(node: Statement) { if (isBlock(node)) { write(" "); @@ -2628,16 +2606,6 @@ const _super = (function (geti, seti) { return getLiteralText(node, currentSourceFile, languageVersion); } - function tryGetConstEnumValue(node: Node): number { - if (compilerOptions.isolatedModules) { - return undefined; - } - - return isPropertyAccessExpression(node) || isElementAccessExpression(node) - ? resolver.getConstantValue(node) - : undefined; - } - function isSingleLineEmptyBlock(block: Block) { return !block.multiLine && block.statements.length === 0 diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index b6fc2093288..75655d8cff8 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2798,6 +2798,20 @@ namespace ts { return tokenSourceMapRanges && tokenSourceMapRanges[token]; } + export function getConstantValue(node: Node) { + const emitNode = node.emitNode; + if (emitNode && emitNode.flags & EmitFlags.ConstantValue) { + return emitNode.constantValue; + } + } + + export function setConstantValue(node: Node, value: number) { + const emitNode = getOrCreateEmitNode(node); + emitNode.constantValue = value; + emitNode.flags |= EmitFlags.ConstantValue; + return node; + } + export function setTextRange(node: T, location: TextRange): T { if (location) { node.pos = location.pos; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 225ab130d62..2a33ecc0e37 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -42,6 +42,10 @@ namespace ts { context.onEmitNode = onEmitNode; context.onSubstituteNode = onSubstituteNode; + // Enable substitution for property/element access to emit const enum values. + context.enableSubstitution(SyntaxKind.PropertyAccessExpression); + context.enableSubstitution(SyntaxKind.ElementAccessExpression); + // These variables contain state that changes as we descend into the tree. let currentSourceFile: SourceFile; let currentNamespace: ModuleDeclaration; @@ -3294,17 +3298,15 @@ namespace ts { switch (node.kind) { case SyntaxKind.Identifier: return substituteExpressionIdentifier(node); - } - - if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper) { - switch (node.kind) { - case SyntaxKind.CallExpression: + case SyntaxKind.PropertyAccessExpression: + return substitutePropertyAccessExpression(node); + case SyntaxKind.ElementAccessExpression: + return substituteElementAccessExpression(node); + case SyntaxKind.CallExpression: + if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper) { return substituteCallExpression(node); - case SyntaxKind.PropertyAccessExpression: - return substitutePropertyAccessExpression(node); - case SyntaxKind.ElementAccessExpression: - return substituteElementAccessExpression(node); - } + } + break; } return node; @@ -3381,7 +3383,7 @@ namespace ts { } function substitutePropertyAccessExpression(node: PropertyAccessExpression) { - if (node.expression.kind === SyntaxKind.SuperKeyword) { + if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper && node.expression.kind === SyntaxKind.SuperKeyword) { const flags = getSuperContainerAsyncMethodFlags(); if (flags) { return createSuperAccessInAsyncMethod( @@ -3392,11 +3394,11 @@ namespace ts { } } - return node; + return substituteConstantValue(node); } function substituteElementAccessExpression(node: ElementAccessExpression) { - if (node.expression.kind === SyntaxKind.SuperKeyword) { + if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper && node.expression.kind === SyntaxKind.SuperKeyword) { const flags = getSuperContainerAsyncMethodFlags(); if (flags) { return createSuperAccessInAsyncMethod( @@ -3407,9 +3409,39 @@ namespace ts { } } + return substituteConstantValue(node); + } + + function substituteConstantValue(node: PropertyAccessExpression | ElementAccessExpression): LeftHandSideExpression { + const constantValue = tryGetConstEnumValue(node); + if (constantValue !== undefined) { + const substitute = createLiteral(constantValue); + setSourceMapRange(substitute, node); + setCommentRange(substitute, node); + if (!compilerOptions.removeComments) { + const propertyName = isPropertyAccessExpression(node) + ? declarationNameToString(node.name) + : getTextOfNode(node.argumentExpression); + substitute.trailingComment = ` ${propertyName} `; + } + + setConstantValue(node, constantValue); + return substitute; + } + return node; } + function tryGetConstEnumValue(node: Node): number { + if (compilerOptions.isolatedModules) { + return undefined; + } + + return isPropertyAccessExpression(node) || isElementAccessExpression(node) + ? resolver.getConstantValue(node) + : undefined; + } + function createSuperAccessInAsyncMethod(argumentExpression: Expression, flags: NodeCheckFlags, location: TextRange): LeftHandSideExpression { if (flags & NodeCheckFlags.AsyncMethodWithSuperBinding) { return createPropertyAccess( diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 74e7e741be8..e8a521ef3ff 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3156,6 +3156,7 @@ namespace ts { sourceMapRange?: TextRange; tokenSourceMapRanges?: Map; annotatedNodes?: Node[]; // Tracks Parse-tree nodes with EmitNodes for eventual cleanup. + constantValue?: number; } /* @internal */ @@ -3187,6 +3188,7 @@ namespace ts { AsyncFunctionBody = 1 << 21, ReuseTempVariableScope = 1 << 22, // Reuse the existing temp variable scope during emit. CustomPrologue = 1 << 23, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). + ConstantValue = 1 << 24, // The node was replaced with a constant value during substitution. } /* @internal */ diff --git a/tests/baselines/reference/constEnumToStringWithComments.js b/tests/baselines/reference/constEnumToStringWithComments.js index c01e75cb923..f0fbc866385 100644 --- a/tests/baselines/reference/constEnumToStringWithComments.js +++ b/tests/baselines/reference/constEnumToStringWithComments.js @@ -23,15 +23,15 @@ let c1 = Foo["C"].toString(); //// [constEnumToStringWithComments.js] -var x0 = 100 /* X */..toString(); -var x1 = 100 /* "X" */..toString(); +var x0 = 100 /* X */.toString(); +var x1 = 100 /* "X" */.toString(); var y0 = 0.5 /* Y */.toString(); var y1 = 0.5 /* "Y" */.toString(); -var z0 = 2 /* Z */..toString(); -var z1 = 2 /* "Z" */..toString(); -var a0 = -1 /* A */..toString(); -var a1 = -1 /* "A" */..toString(); +var z0 = 2 /* Z */.toString(); +var z1 = 2 /* "Z" */.toString(); +var a0 = -1 /* A */.toString(); +var a1 = -1 /* "A" */.toString(); var b0 = -1.5 /* B */.toString(); var b1 = -1.5 /* "B" */.toString(); -var c0 = -1 /* C */..toString(); -var c1 = -1 /* "C" */..toString(); +var c0 = -1 /* C */.toString(); +var c1 = -1 /* "C" */.toString();