Skip to content

Commit 41fb0aa

Browse files
authored
Merge pull request #14995 from Microsoft/cleanupLiteralEmit
Clean up literal emit
2 parents 9d2b57a + d32231e commit 41fb0aa

24 files changed

+128
-123
lines changed

src/compiler/binder.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1855,7 +1855,7 @@ namespace ts {
18551855
}
18561856

18571857
function checkStrictModeNumericLiteral(node: NumericLiteral) {
1858-
if (inStrictMode && node.isOctalLiteral) {
1858+
if (inStrictMode && node.numericLiteralFlags & NumericLiteralFlags.Octal) {
18591859
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode));
18601860
}
18611861
}
@@ -3330,6 +3330,18 @@ namespace ts {
33303330
transformFlags |= TransformFlags.AssertES2015;
33313331
break;
33323332

3333+
case SyntaxKind.StringLiteral:
3334+
if ((<StringLiteral>node).hasExtendedUnicodeEscape) {
3335+
transformFlags |= TransformFlags.AssertES2015;
3336+
}
3337+
break;
3338+
3339+
case SyntaxKind.NumericLiteral:
3340+
if ((<NumericLiteral>node).numericLiteralFlags & NumericLiteralFlags.BinaryOrOctalSpecifier) {
3341+
transformFlags |= TransformFlags.AssertES2015;
3342+
}
3343+
break;
3344+
33333345
case SyntaxKind.ForOfStatement:
33343346
// This node is either ES2015 syntax or ES2017 syntax (if it is a for-await-of).
33353347
if ((<ForOfStatement>node).awaitModifier) {

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24017,7 +24017,7 @@ namespace ts {
2401724017

2401824018
function checkGrammarNumericLiteral(node: NumericLiteral): boolean {
2401924019
// Grammar checking
24020-
if (node.isOctalLiteral) {
24020+
if (node.numericLiteralFlags & NumericLiteralFlags.Octal) {
2402124021
let diagnosticMessage: DiagnosticMessage | undefined;
2402224022
if (languageVersion >= ScriptTarget.ES5) {
2402324023
diagnosticMessage = Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher_Use_the_syntax_0;

src/compiler/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1455,7 +1455,7 @@ namespace ts {
14551455
return /^\.\.?($|[\\/])/.test(moduleName);
14561456
}
14571457

1458-
export function getEmitScriptTarget(compilerOptions: CompilerOptions | PrinterOptions) {
1458+
export function getEmitScriptTarget(compilerOptions: CompilerOptions) {
14591459
return compilerOptions.target || ScriptTarget.ES3;
14601460
}
14611461

src/compiler/emitter.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ namespace ts {
204204
} = handlers;
205205

206206
const newLine = getNewLineCharacter(printerOptions);
207-
const languageVersion = getEmitScriptTarget(printerOptions);
208207
const comments = createCommentWriter(printerOptions, onEmitSourceMapOfPosition);
209208
const {
210209
emitNodeWithComments,
@@ -1084,7 +1083,7 @@ namespace ts {
10841083
}
10851084

10861085
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
1087-
const allowTrailingComma = languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None;
1086+
const allowTrailingComma = currentSourceFile.languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None;
10881087
emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine);
10891088

10901089
if (indentedFlag) {
@@ -1118,11 +1117,11 @@ namespace ts {
11181117
// 1..toString is a valid property access, emit a dot after the literal
11191118
// Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal
11201119
function needsDotDotForPropertyAccess(expression: Expression) {
1121-
if (expression.kind === SyntaxKind.NumericLiteral) {
1120+
expression = skipPartiallyEmittedExpressions(expression);
1121+
if (isNumericLiteral(expression)) {
11221122
// check if numeric literal is a decimal literal that was originally written with a dot
11231123
const text = getLiteralTextOfNode(<LiteralExpression>expression);
1124-
return getNumericLiteralFlags(text, /*hint*/ NumericLiteralFlags.All) === NumericLiteralFlags.None
1125-
&& !(<LiteralExpression>expression).isOctalLiteral
1124+
return !expression.numericLiteralFlags
11261125
&& text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0;
11271126
}
11281127
else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) {
@@ -2638,7 +2637,7 @@ namespace ts {
26382637
}
26392638
}
26402639

2641-
return getLiteralText(node, currentSourceFile, languageVersion);
2640+
return getLiteralText(node, currentSourceFile);
26422641
}
26432642

26442643
/**

src/compiler/factory.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ namespace ts {
8888
export function createNumericLiteral(value: string): NumericLiteral {
8989
const node = <NumericLiteral>createSynthesizedNode(SyntaxKind.NumericLiteral);
9090
node.text = value;
91+
node.numericLiteralFlags = 0;
9192
return node;
9293
}
9394

@@ -3365,17 +3366,14 @@ namespace ts {
33653366
*/
33663367
export function parenthesizeForAccess(expression: Expression): LeftHandSideExpression {
33673368
// isLeftHandSideExpression is almost the correct criterion for when it is not necessary
3368-
// to parenthesize the expression before a dot. The known exceptions are:
3369+
// to parenthesize the expression before a dot. The known exception is:
33693370
//
33703371
// NewExpression:
33713372
// new C.x -> not the same as (new C).x
3372-
// NumericLiteral
3373-
// 1.x -> not the same as (1).x
33743373
//
33753374
const emittedExpression = skipPartiallyEmittedExpressions(expression);
33763375
if (isLeftHandSideExpression(emittedExpression)
3377-
&& (emittedExpression.kind !== SyntaxKind.NewExpression || (<NewExpression>emittedExpression).arguments)
3378-
&& emittedExpression.kind !== SyntaxKind.NumericLiteral) {
3376+
&& (emittedExpression.kind !== SyntaxKind.NewExpression || (<NewExpression>emittedExpression).arguments)) {
33793377
return <LeftHandSideExpression>expression;
33803378
}
33813379

src/compiler/parser.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2031,23 +2031,19 @@ namespace ts {
20312031
node.isUnterminated = true;
20322032
}
20332033

2034-
const tokenPos = scanner.getTokenPos();
2035-
nextToken();
2036-
finishNode(node);
2037-
20382034
// Octal literals are not allowed in strict mode or ES5
20392035
// Note that theoretically the following condition would hold true literals like 009,
20402036
// which is not octal.But because of how the scanner separates the tokens, we would
20412037
// never get a token like this. Instead, we would get 00 and 9 as two separate tokens.
20422038
// We also do not need to check for negatives because any prefix operator would be part of a
20432039
// parent unary expression.
2044-
if (node.kind === SyntaxKind.NumericLiteral
2045-
&& sourceText.charCodeAt(tokenPos) === CharacterCodes._0
2046-
&& isOctalDigit(sourceText.charCodeAt(tokenPos + 1))) {
2047-
2048-
node.isOctalLiteral = true;
2040+
if (node.kind === SyntaxKind.NumericLiteral) {
2041+
(<NumericLiteral>node).numericLiteralFlags = scanner.getNumericLiteralFlags();
20492042
}
20502043

2044+
nextToken();
2045+
finishNode(node);
2046+
20512047
return node;
20522048
}
20532049

src/compiler/scanner.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ namespace ts {
2323
isIdentifier(): boolean;
2424
isReservedWord(): boolean;
2525
isUnterminated(): boolean;
26+
/* @internal */
27+
getNumericLiteralFlags(): NumericLiteralFlags;
2628
reScanGreaterToken(): SyntaxKind;
2729
reScanSlashToken(): SyntaxKind;
2830
reScanTemplateToken(): SyntaxKind;
@@ -799,6 +801,7 @@ namespace ts {
799801
let precedingLineBreak: boolean;
800802
let hasExtendedUnicodeEscape: boolean;
801803
let tokenIsUnterminated: boolean;
804+
let numericLiteralFlags: NumericLiteralFlags;
802805

803806
setText(text, start, length);
804807

@@ -814,6 +817,7 @@ namespace ts {
814817
isIdentifier: () => token === SyntaxKind.Identifier || token > SyntaxKind.LastReservedWord,
815818
isReservedWord: () => token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord,
816819
isUnterminated: () => tokenIsUnterminated,
820+
getNumericLiteralFlags: () => numericLiteralFlags,
817821
reScanGreaterToken,
818822
reScanSlashToken,
819823
reScanTemplateToken,
@@ -850,6 +854,7 @@ namespace ts {
850854
let end = pos;
851855
if (text.charCodeAt(pos) === CharacterCodes.E || text.charCodeAt(pos) === CharacterCodes.e) {
852856
pos++;
857+
numericLiteralFlags = NumericLiteralFlags.Scientific;
853858
if (text.charCodeAt(pos) === CharacterCodes.plus || text.charCodeAt(pos) === CharacterCodes.minus) pos++;
854859
if (isDigit(text.charCodeAt(pos))) {
855860
pos++;
@@ -1221,6 +1226,7 @@ namespace ts {
12211226
hasExtendedUnicodeEscape = false;
12221227
precedingLineBreak = false;
12231228
tokenIsUnterminated = false;
1229+
numericLiteralFlags = 0;
12241230
while (true) {
12251231
tokenPos = pos;
12261232
if (pos >= end) {
@@ -1419,6 +1425,7 @@ namespace ts {
14191425
value = 0;
14201426
}
14211427
tokenValue = "" + value;
1428+
numericLiteralFlags = NumericLiteralFlags.HexSpecifier;
14221429
return token = SyntaxKind.NumericLiteral;
14231430
}
14241431
else if (pos + 2 < end && (text.charCodeAt(pos + 1) === CharacterCodes.B || text.charCodeAt(pos + 1) === CharacterCodes.b)) {
@@ -1429,6 +1436,7 @@ namespace ts {
14291436
value = 0;
14301437
}
14311438
tokenValue = "" + value;
1439+
numericLiteralFlags = NumericLiteralFlags.BinarySpecifier;
14321440
return token = SyntaxKind.NumericLiteral;
14331441
}
14341442
else if (pos + 2 < end && (text.charCodeAt(pos + 1) === CharacterCodes.O || text.charCodeAt(pos + 1) === CharacterCodes.o)) {
@@ -1439,11 +1447,13 @@ namespace ts {
14391447
value = 0;
14401448
}
14411449
tokenValue = "" + value;
1450+
numericLiteralFlags = NumericLiteralFlags.OctalSpecifier;
14421451
return token = SyntaxKind.NumericLiteral;
14431452
}
14441453
// Try to parse as an octal
14451454
if (pos + 1 < end && isOctalDigit(text.charCodeAt(pos + 1))) {
14461455
tokenValue = "" + scanOctalDigits();
1456+
numericLiteralFlags = NumericLiteralFlags.Octal;
14471457
return token = SyntaxKind.NumericLiteral;
14481458
}
14491459
// This fall-through is a deviation from the EcmaScript grammar. The grammar says that a leading zero

src/compiler/transformers/es2015.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,12 @@ namespace ts {
466466
case SyntaxKind.TemplateTail:
467467
return visitTemplateLiteral(<LiteralExpression>node);
468468

469+
case SyntaxKind.StringLiteral:
470+
return visitStringLiteral(<StringLiteral>node);
471+
472+
case SyntaxKind.NumericLiteral:
473+
return visitNumericLiteral(<NumericLiteral>node);
474+
469475
case SyntaxKind.TaggedTemplateExpression:
470476
return visitTaggedTemplateExpression(<TaggedTemplateExpression>node);
471477

@@ -3414,6 +3420,30 @@ namespace ts {
34143420
return setTextRange(createLiteral(node.text), node);
34153421
}
34163422

3423+
/**
3424+
* Visits a string literal with an extended unicode escape.
3425+
*
3426+
* @param node A string literal.
3427+
*/
3428+
function visitStringLiteral(node: StringLiteral) {
3429+
if (node.hasExtendedUnicodeEscape) {
3430+
return setTextRange(createLiteral(node.text), node);
3431+
}
3432+
return node;
3433+
}
3434+
3435+
/**
3436+
* Visits a binary or octal (ES6) numeric literal.
3437+
*
3438+
* @param node A string literal.
3439+
*/
3440+
function visitNumericLiteral(node: NumericLiteral) {
3441+
if (node.numericLiteralFlags & NumericLiteralFlags.BinaryOrOctalSpecifier) {
3442+
return setTextRange(createNumericLiteral(node.text), node);
3443+
}
3444+
return node;
3445+
}
3446+
34173447
/**
34183448
* Visits a TaggedTemplateExpression node.
34193449
*

src/compiler/types.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,8 +1313,6 @@ namespace ts {
13131313
text: string;
13141314
isUnterminated?: boolean;
13151315
hasExtendedUnicodeEscape?: boolean;
1316-
/* @internal */
1317-
isOctalLiteral?: boolean;
13181316
}
13191317

13201318
// 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 {
13321330
kind: SyntaxKind.NoSubstitutionTemplateLiteral;
13331331
}
13341332

1333+
/* @internal */
1334+
export const enum NumericLiteralFlags {
1335+
None = 0,
1336+
Scientific = 1 << 1, // e.g. `10e2`
1337+
Octal = 1 << 2, // e.g. `0777`
1338+
HexSpecifier = 1 << 3, // e.g. `0x00000000`
1339+
BinarySpecifier = 1 << 4, // e.g. `0b0110010000000000`
1340+
OctalSpecifier = 1 << 5, // e.g. `0o777`
1341+
BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier,
1342+
}
1343+
13351344
export interface NumericLiteral extends LiteralExpression {
13361345
kind: SyntaxKind.NumericLiteral;
1346+
/* @internal */
1347+
numericLiteralFlags?: NumericLiteralFlags;
13371348
}
13381349

13391350
export interface TemplateHead extends LiteralLikeNode {
@@ -4189,7 +4200,6 @@ namespace ts {
41894200
}
41904201

41914202
export interface PrinterOptions {
4192-
target?: ScriptTarget;
41934203
removeComments?: boolean;
41944204
newLine?: NewLineKind;
41954205
/*@internal*/ sourceMap?: boolean;

src/compiler/utilities.ts

Lines changed: 6 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -322,21 +322,11 @@ namespace ts {
322322
return getSourceTextOfNodeFromSourceFile(getSourceFileOfNode(node), node, includeTrivia);
323323
}
324324

325-
export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile, languageVersion: ScriptTarget) {
326-
// Any template literal or string literal with an extended escape
327-
// (e.g. "\u{0067}") will need to be downleveled as a escaped string literal.
328-
if (languageVersion < ScriptTarget.ES2015 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) {
329-
return getQuotedEscapedLiteralText('"', node.text, '"');
330-
}
331-
325+
export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile) {
332326
// If we don't need to downlevel and we can reach the original source text using
333327
// the node's parent reference, then simply get the text as it was originally written.
334328
if (!nodeIsSynthesized(node) && node.parent) {
335-
const text = getSourceTextOfNodeFromSourceFile(sourceFile, node);
336-
if (languageVersion < ScriptTarget.ES2015 && isBinaryOrOctalIntegerLiteral(node, text)) {
337-
return node.text;
338-
}
339-
return text;
329+
return getSourceTextOfNodeFromSourceFile(sourceFile, node);
340330
}
341331

342332
// If we can't reach the original source text, use the canonical form if it's a number,
@@ -359,55 +349,6 @@ namespace ts {
359349
Debug.fail(`Literal kind '${node.kind}' not accounted for.`);
360350
}
361351

362-
export function isBinaryOrOctalIntegerLiteral(node: LiteralLikeNode, text: string) {
363-
return node.kind === SyntaxKind.NumericLiteral
364-
&& (getNumericLiteralFlags(text, /*hint*/ NumericLiteralFlags.BinaryOrOctal) & NumericLiteralFlags.BinaryOrOctal) !== 0;
365-
}
366-
367-
export const enum NumericLiteralFlags {
368-
None = 0,
369-
Hexadecimal = 1 << 0,
370-
Binary = 1 << 1,
371-
Octal = 1 << 2,
372-
Scientific = 1 << 3,
373-
374-
BinaryOrOctal = Binary | Octal,
375-
BinaryOrOctalOrHexadecimal = BinaryOrOctal | Hexadecimal,
376-
All = Hexadecimal | Binary | Octal | Scientific,
377-
}
378-
379-
/**
380-
* Scans a numeric literal string to determine the form of the number.
381-
* @param text Numeric literal text
382-
* @param hint If `Scientific` or `All` is specified, performs a more expensive check to scan for scientific notation.
383-
*/
384-
export function getNumericLiteralFlags(text: string, hint?: NumericLiteralFlags) {
385-
if (text.length > 1) {
386-
switch (text.charCodeAt(1)) {
387-
case CharacterCodes.b:
388-
case CharacterCodes.B:
389-
return NumericLiteralFlags.Binary;
390-
case CharacterCodes.o:
391-
case CharacterCodes.O:
392-
return NumericLiteralFlags.Octal;
393-
case CharacterCodes.x:
394-
case CharacterCodes.X:
395-
return NumericLiteralFlags.Hexadecimal;
396-
}
397-
398-
if (hint & NumericLiteralFlags.Scientific) {
399-
for (let i = text.length - 1; i >= 0; i--) {
400-
switch (text.charCodeAt(i)) {
401-
case CharacterCodes.e:
402-
case CharacterCodes.E:
403-
return NumericLiteralFlags.Scientific;
404-
}
405-
}
406-
}
407-
}
408-
return NumericLiteralFlags.None;
409-
}
410-
411352
function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) {
412353
return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote;
413354
}
@@ -2027,6 +1968,10 @@ namespace ts {
20271968
return false;
20281969
}
20291970

1971+
export function isNumericLiteral(node: Node): node is NumericLiteral {
1972+
return node.kind === SyntaxKind.NumericLiteral;
1973+
}
1974+
20301975
export function isStringOrNumericLiteral(node: Node): node is StringLiteral | NumericLiteral {
20311976
const kind = node.kind;
20321977
return kind === SyntaxKind.StringLiteral

0 commit comments

Comments
 (0)