Clean up literal emit

This commit is contained in:
Ron Buckton 2017-04-03 14:02:14 -07:00
parent 9c9b659f44
commit fd081f40a8
24 changed files with 127 additions and 123 deletions

View file

@ -1854,7 +1854,7 @@ namespace ts {
}
function checkStrictModeNumericLiteral(node: NumericLiteral) {
if (inStrictMode && node.isOctalLiteral) {
if (inStrictMode && node.numericLiteralFlags & NumericLiteralFlags.Octal) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode));
}
}
@ -3327,6 +3327,18 @@ namespace ts {
transformFlags |= TransformFlags.AssertES2015;
break;
case SyntaxKind.StringLiteral:
if ((<StringLiteral>node).hasExtendedUnicodeEscape) {
transformFlags |= TransformFlags.AssertES2015;
}
break;
case SyntaxKind.NumericLiteral:
if ((<NumericLiteral>node).numericLiteralFlags & NumericLiteralFlags.BinaryOrOctalSpecifier) {
transformFlags |= TransformFlags.AssertES2015;
}
break;
case SyntaxKind.ForOfStatement:
// This node is either ES2015 syntax or ES2017 syntax (if it is a for-await-of).
if ((<ForOfStatement>node).awaitModifier) {

View file

@ -23986,7 +23986,7 @@ namespace ts {
function checkGrammarNumericLiteral(node: NumericLiteral): boolean {
// Grammar checking
if (node.isOctalLiteral) {
if (node.numericLiteralFlags & NumericLiteralFlags.Octal) {
let diagnosticMessage: DiagnosticMessage | undefined;
if (languageVersion >= ScriptTarget.ES5) {
diagnosticMessage = Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher_Use_the_syntax_0;

View file

@ -1455,7 +1455,7 @@ namespace ts {
return /^\.\.?($|[\\/])/.test(moduleName);
}
export function getEmitScriptTarget(compilerOptions: CompilerOptions | PrinterOptions) {
export function getEmitScriptTarget(compilerOptions: CompilerOptions) {
return compilerOptions.target || ScriptTarget.ES3;
}

View file

@ -204,7 +204,6 @@ namespace ts {
} = handlers;
const newLine = getNewLineCharacter(printerOptions);
const languageVersion = getEmitScriptTarget(printerOptions);
const comments = createCommentWriter(printerOptions, onEmitSourceMapOfPosition);
const {
emitNodeWithComments,
@ -1084,7 +1083,7 @@ namespace ts {
}
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
const allowTrailingComma = languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None;
const allowTrailingComma = currentSourceFile.languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None;
emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine);
if (indentedFlag) {
@ -1118,11 +1117,11 @@ namespace ts {
// 1..toString is a valid property access, emit a dot after the literal
// Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal
function needsDotDotForPropertyAccess(expression: Expression) {
if (expression.kind === SyntaxKind.NumericLiteral) {
expression = skipPartiallyEmittedExpressions(expression);
if (isNumericLiteral(expression)) {
// check if numeric literal is a decimal literal that was originally written with a dot
const text = getLiteralTextOfNode(<LiteralExpression>expression);
return getNumericLiteralFlags(text, /*hint*/ NumericLiteralFlags.All) === NumericLiteralFlags.None
&& !(<LiteralExpression>expression).isOctalLiteral
return !expression.numericLiteralFlags
&& text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0;
}
else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) {
@ -2638,7 +2637,7 @@ namespace ts {
}
}
return getLiteralText(node, currentSourceFile, languageVersion);
return getLiteralText(node, currentSourceFile);
}
/**

View file

@ -3365,17 +3365,14 @@ namespace ts {
*/
export function parenthesizeForAccess(expression: Expression): LeftHandSideExpression {
// isLeftHandSideExpression is almost the correct criterion for when it is not necessary
// to parenthesize the expression before a dot. The known exceptions are:
// to parenthesize the expression before a dot. The known exception is:
//
// NewExpression:
// new C.x -> not the same as (new C).x
// NumericLiteral
// 1.x -> not the same as (1).x
//
const emittedExpression = skipPartiallyEmittedExpressions(expression);
if (isLeftHandSideExpression(emittedExpression)
&& (emittedExpression.kind !== SyntaxKind.NewExpression || (<NewExpression>emittedExpression).arguments)
&& emittedExpression.kind !== SyntaxKind.NumericLiteral) {
&& (emittedExpression.kind !== SyntaxKind.NewExpression || (<NewExpression>emittedExpression).arguments)) {
return <LeftHandSideExpression>expression;
}

View file

@ -2031,23 +2031,19 @@ namespace ts {
node.isUnterminated = true;
}
const tokenPos = scanner.getTokenPos();
nextToken();
finishNode(node);
// Octal literals are not allowed in strict mode or ES5
// Note that theoretically the following condition would hold true literals like 009,
// which is not octal.But because of how the scanner separates the tokens, we would
// never get a token like this. Instead, we would get 00 and 9 as two separate tokens.
// We also do not need to check for negatives because any prefix operator would be part of a
// parent unary expression.
if (node.kind === SyntaxKind.NumericLiteral
&& sourceText.charCodeAt(tokenPos) === CharacterCodes._0
&& isOctalDigit(sourceText.charCodeAt(tokenPos + 1))) {
node.isOctalLiteral = true;
if (node.kind === SyntaxKind.NumericLiteral) {
(<NumericLiteral>node).numericLiteralFlags = scanner.getNumericLiteralFlags();
}
nextToken();
finishNode(node);
return node;
}

View file

@ -23,6 +23,8 @@ namespace ts {
isIdentifier(): boolean;
isReservedWord(): boolean;
isUnterminated(): boolean;
/* @internal */
getNumericLiteralFlags(): NumericLiteralFlags;
reScanGreaterToken(): SyntaxKind;
reScanSlashToken(): SyntaxKind;
reScanTemplateToken(): SyntaxKind;
@ -799,6 +801,7 @@ namespace ts {
let precedingLineBreak: boolean;
let hasExtendedUnicodeEscape: boolean;
let tokenIsUnterminated: boolean;
let numericLiteralFlags: NumericLiteralFlags;
setText(text, start, length);
@ -814,6 +817,7 @@ namespace ts {
isIdentifier: () => token === SyntaxKind.Identifier || token > SyntaxKind.LastReservedWord,
isReservedWord: () => token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord,
isUnterminated: () => tokenIsUnterminated,
getNumericLiteralFlags: () => numericLiteralFlags,
reScanGreaterToken,
reScanSlashToken,
reScanTemplateToken,
@ -850,6 +854,7 @@ namespace ts {
let end = pos;
if (text.charCodeAt(pos) === CharacterCodes.E || text.charCodeAt(pos) === CharacterCodes.e) {
pos++;
numericLiteralFlags = NumericLiteralFlags.Scientific;
if (text.charCodeAt(pos) === CharacterCodes.plus || text.charCodeAt(pos) === CharacterCodes.minus) pos++;
if (isDigit(text.charCodeAt(pos))) {
pos++;
@ -1221,6 +1226,7 @@ namespace ts {
hasExtendedUnicodeEscape = false;
precedingLineBreak = false;
tokenIsUnterminated = false;
numericLiteralFlags = 0;
while (true) {
tokenPos = pos;
if (pos >= end) {
@ -1419,6 +1425,7 @@ namespace ts {
value = 0;
}
tokenValue = "" + value;
numericLiteralFlags = NumericLiteralFlags.HexSpecifier;
return token = SyntaxKind.NumericLiteral;
}
else if (pos + 2 < end && (text.charCodeAt(pos + 1) === CharacterCodes.B || text.charCodeAt(pos + 1) === CharacterCodes.b)) {
@ -1429,6 +1436,7 @@ namespace ts {
value = 0;
}
tokenValue = "" + value;
numericLiteralFlags = NumericLiteralFlags.BinarySpecifier;
return token = SyntaxKind.NumericLiteral;
}
else if (pos + 2 < end && (text.charCodeAt(pos + 1) === CharacterCodes.O || text.charCodeAt(pos + 1) === CharacterCodes.o)) {
@ -1439,11 +1447,13 @@ namespace ts {
value = 0;
}
tokenValue = "" + value;
numericLiteralFlags = NumericLiteralFlags.OctalSpecifier;
return token = SyntaxKind.NumericLiteral;
}
// Try to parse as an octal
if (pos + 1 < end && isOctalDigit(text.charCodeAt(pos + 1))) {
tokenValue = "" + scanOctalDigits();
numericLiteralFlags = NumericLiteralFlags.Octal;
return token = SyntaxKind.NumericLiteral;
}
// This fall-through is a deviation from the EcmaScript grammar. The grammar says that a leading zero

View file

@ -466,6 +466,12 @@ namespace ts {
case SyntaxKind.TemplateTail:
return visitTemplateLiteral(<LiteralExpression>node);
case SyntaxKind.StringLiteral:
return visitStringLiteral(<StringLiteral>node);
case SyntaxKind.NumericLiteral:
return visitNumericLiteral(<NumericLiteral>node);
case SyntaxKind.TaggedTemplateExpression:
return visitTaggedTemplateExpression(<TaggedTemplateExpression>node);
@ -3414,6 +3420,30 @@ namespace ts {
return setTextRange(createLiteral(node.text), node);
}
/**
* Visits a string literal with an extended unicode escape.
*
* @param node A string literal.
*/
function visitStringLiteral(node: StringLiteral) {
if (node.hasExtendedUnicodeEscape) {
return setTextRange(createLiteral(node.text), node);
}
return node;
}
/**
* Visits a binary or octal (ES6) numeric literal.
*
* @param node A string literal.
*/
function visitNumericLiteral(node: NumericLiteral) {
if (node.numericLiteralFlags & NumericLiteralFlags.BinaryOrOctalSpecifier) {
return setTextRange(createNumericLiteral(node.text), node);
}
return node;
}
/**
* Visits a TaggedTemplateExpression node.
*

View file

@ -1313,8 +1313,6 @@ namespace ts {
text: string;
isUnterminated?: boolean;
hasExtendedUnicodeEscape?: boolean;
/* @internal */
isOctalLiteral?: boolean;
}
// The text property of a LiteralExpression stores the interpreted value of the literal in text form. For a StringLiteral,
@ -1332,8 +1330,21 @@ namespace ts {
kind: SyntaxKind.NoSubstitutionTemplateLiteral;
}
/* @internal */
export const enum NumericLiteralFlags {
None = 0,
Scientific = 1 << 1, // e.g. `10e2`
Octal = 1 << 2, // e.g. `0777`
HexSpecifier = 1 << 3, // e.g. `0x00000000`
BinarySpecifier = 1 << 4, // e.g. `0b0110010000000000`
OctalSpecifier = 1 << 5, // e.g. `0o777`
BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier,
}
export interface NumericLiteral extends LiteralExpression {
kind: SyntaxKind.NumericLiteral;
/* @internal */
numericLiteralFlags?: NumericLiteralFlags;
}
export interface TemplateHead extends LiteralLikeNode {
@ -4189,7 +4200,6 @@ namespace ts {
}
export interface PrinterOptions {
target?: ScriptTarget;
removeComments?: boolean;
newLine?: NewLineKind;
/*@internal*/ sourceMap?: boolean;

View file

@ -322,21 +322,11 @@ namespace ts {
return getSourceTextOfNodeFromSourceFile(getSourceFileOfNode(node), node, includeTrivia);
}
export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile, languageVersion: ScriptTarget) {
// Any template literal or string literal with an extended escape
// (e.g. "\u{0067}") will need to be downleveled as a escaped string literal.
if (languageVersion < ScriptTarget.ES2015 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) {
return getQuotedEscapedLiteralText('"', node.text, '"');
}
export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile) {
// If we don't need to downlevel and we can reach the original source text using
// the node's parent reference, then simply get the text as it was originally written.
if (!nodeIsSynthesized(node) && node.parent) {
const text = getSourceTextOfNodeFromSourceFile(sourceFile, node);
if (languageVersion < ScriptTarget.ES2015 && isBinaryOrOctalIntegerLiteral(node, text)) {
return node.text;
}
return text;
return getSourceTextOfNodeFromSourceFile(sourceFile, node);
}
// If we can't reach the original source text, use the canonical form if it's a number,
@ -359,55 +349,6 @@ namespace ts {
Debug.fail(`Literal kind '${node.kind}' not accounted for.`);
}
export function isBinaryOrOctalIntegerLiteral(node: LiteralLikeNode, text: string) {
return node.kind === SyntaxKind.NumericLiteral
&& (getNumericLiteralFlags(text, /*hint*/ NumericLiteralFlags.BinaryOrOctal) & NumericLiteralFlags.BinaryOrOctal) !== 0;
}
export const enum NumericLiteralFlags {
None = 0,
Hexadecimal = 1 << 0,
Binary = 1 << 1,
Octal = 1 << 2,
Scientific = 1 << 3,
BinaryOrOctal = Binary | Octal,
BinaryOrOctalOrHexadecimal = BinaryOrOctal | Hexadecimal,
All = Hexadecimal | Binary | Octal | Scientific,
}
/**
* Scans a numeric literal string to determine the form of the number.
* @param text Numeric literal text
* @param hint If `Scientific` or `All` is specified, performs a more expensive check to scan for scientific notation.
*/
export function getNumericLiteralFlags(text: string, hint?: NumericLiteralFlags) {
if (text.length > 1) {
switch (text.charCodeAt(1)) {
case CharacterCodes.b:
case CharacterCodes.B:
return NumericLiteralFlags.Binary;
case CharacterCodes.o:
case CharacterCodes.O:
return NumericLiteralFlags.Octal;
case CharacterCodes.x:
case CharacterCodes.X:
return NumericLiteralFlags.Hexadecimal;
}
if (hint & NumericLiteralFlags.Scientific) {
for (let i = text.length - 1; i >= 0; i--) {
switch (text.charCodeAt(i)) {
case CharacterCodes.e:
case CharacterCodes.E:
return NumericLiteralFlags.Scientific;
}
}
}
}
return NumericLiteralFlags.None;
}
function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) {
return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote;
}
@ -2027,6 +1968,10 @@ namespace ts {
return false;
}
export function isNumericLiteral(node: Node): node is NumericLiteral {
return node.kind === SyntaxKind.NumericLiteral;
}
export function isStringOrNumericLiteral(node: Node): node is StringLiteral | NumericLiteral {
const kind = node.kind;
return kind === SyntaxKind.StringLiteral

View file

@ -52,6 +52,9 @@ namespace ts {
printsCorrectly("default", {}, printer => printer.printFile(sourceFile));
printsCorrectly("removeComments", { removeComments: true }, printer => printer.printFile(sourceFile));
// github #14948
printsCorrectly("templateLiteral", {}, printer => printer.printFile(createSourceFile("source.ts", "let greeting = `Hi ${name}, how are you?`;", ScriptTarget.ES2017)));
});
describe("printBundle", () => {

View file

@ -24,15 +24,15 @@ declare var a;
(<any>a[0]);
(<any>a.b["0"]);
(<any>a()).x;
declare var A;
// should keep the parentheses in emit
(<any>1).foo;
(<any>1.).foo;
(<any>1.0).foo;
(<any>12e+34).foo;
(<any>0xff).foo;
declare var A;
// should keep the parentheses in emit
(<any>(1.0));
(<any>new A).foo;
(<any>typeof A).x;
@ -74,12 +74,12 @@ a;
a[0];
a.b["0"];
a().x;
1..foo;
1..foo;
1.0.foo;
12e+34.foo;
0xff.foo;
// should keep the parentheses in emit
(1).foo;
(1.).foo;
(1.0).foo;
(12e+34).foo;
(0xff).foo;
(1.0);
(new A).foo;
(typeof A).x;

View file

@ -37,27 +37,28 @@ declare var a;
(<any>a()).x;
>a : Symbol(a, Decl(castExpressionParentheses.ts, 0, 11))
declare var A;
>A : Symbol(A, Decl(castExpressionParentheses.ts, 26, 11))
// should keep the parentheses in emit
(<any>1).foo;
(<any>1.).foo;
(<any>1.0).foo;
(<any>12e+34).foo;
(<any>0xff).foo;
declare var A;
>A : Symbol(A, Decl(castExpressionParentheses.ts, 31, 11))
// should keep the parentheses in emit
(<any>(1.0));
(<any>new A).foo;
>A : Symbol(A, Decl(castExpressionParentheses.ts, 26, 11))
>A : Symbol(A, Decl(castExpressionParentheses.ts, 31, 11))
(<any>typeof A).x;
>A : Symbol(A, Decl(castExpressionParentheses.ts, 26, 11))
>A : Symbol(A, Decl(castExpressionParentheses.ts, 31, 11))
(<any>-A).x;
>A : Symbol(A, Decl(castExpressionParentheses.ts, 26, 11))
>A : Symbol(A, Decl(castExpressionParentheses.ts, 31, 11))
new (<any>A());
>A : Symbol(A, Decl(castExpressionParentheses.ts, 26, 11))
>A : Symbol(A, Decl(castExpressionParentheses.ts, 31, 11))
(<Tany>()=> {})();
>Tany : Symbol(Tany, Decl(castExpressionParentheses.ts, 39, 2))
@ -66,14 +67,14 @@ new (<any>A());
>foo : Symbol(foo, Decl(castExpressionParentheses.ts, 40, 6))
(<any><number><any>-A).x;
>A : Symbol(A, Decl(castExpressionParentheses.ts, 26, 11))
>A : Symbol(A, Decl(castExpressionParentheses.ts, 31, 11))
// nested cast, should keep one pair of parenthese
(<any><number>(<any>-A)).x;
>A : Symbol(A, Decl(castExpressionParentheses.ts, 26, 11))
>A : Symbol(A, Decl(castExpressionParentheses.ts, 31, 11))
// nested parenthesized expression, should keep one pair of parenthese
(<any>(A))
>A : Symbol(A, Decl(castExpressionParentheses.ts, 26, 11))
>A : Symbol(A, Decl(castExpressionParentheses.ts, 31, 11))

View file

@ -125,10 +125,6 @@ declare var a;
>a : any
>x : any
declare var A;
>A : any
// should keep the parentheses in emit
(<any>1).foo;
>(<any>1).foo : any
>(<any>1) : any
@ -164,6 +160,10 @@ declare var A;
>0xff : 255
>foo : any
declare var A;
>A : any
// should keep the parentheses in emit
(<any>(1.0));
>(<any>(1.0)) : any
><any>(1.0) : any

View file

@ -8,7 +8,7 @@ export let { toString } = 1;
//// [destructuringInVariableDeclarations1.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toString = (1).toString;
exports.toString = 1..toString;
{
let { toFixed } = 1;
}

View file

@ -9,7 +9,7 @@ export let { toString } = 1;
define(["require", "exports"], function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toString = (1).toString;
exports.toString = 1..toString;
{
let { toFixed } = 1;
}

View file

@ -17,7 +17,7 @@ export let { toString } = 1;
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toString = (1).toString;
exports.toString = 1..toString;
{
let { toFixed } = 1;
}

View file

@ -13,7 +13,7 @@ System.register([], function (exports_1, context_1) {
return {
setters: [],
execute: function () {
exports_1("toString", toString = (1).toString);
exports_1("toString", toString = 1..toString);
{
let { toFixed } = 1;
}

View file

@ -14,7 +14,7 @@ System.register([], function (exports_1, context_1) {
return {
setters: [],
execute: function () {
toString = (1).toString;
toString = 1..toString;
{
let { toFixed } = 1;
}

View file

@ -2,4 +2,4 @@
var { x } = <any>0;
//// [destructuringTypeAssertionsES5_5.js]
var x = (0).x;
var x = 0..x;

View file

@ -2,4 +2,4 @@
var { toExponential } = 0;
//// [destructuringWithNumberLiteral.js]
var toExponential = (0).toExponential;
var toExponential = 0..toExponential;

View file

@ -10,7 +10,7 @@ var X = { 0b11: '', 3: '' };
//// [duplicateIdentifierDifferentSpelling.js]
var A = (function () {
function A() {
this[3] = '';
this[0b11] = '';
this[3] = '';
}
return A;

View file

@ -0,0 +1 @@
let greeting = `Hi ${name}, how are you?`;

View file

@ -23,15 +23,15 @@ declare var a;
(<any>a[0]);
(<any>a.b["0"]);
(<any>a()).x;
declare var A;
// should keep the parentheses in emit
(<any>1).foo;
(<any>1.).foo;
(<any>1.0).foo;
(<any>12e+34).foo;
(<any>0xff).foo;
declare var A;
// should keep the parentheses in emit
(<any>(1.0));
(<any>new A).foo;
(<any>typeof A).x;