Skip to content

Commit a81bf8c

Browse files
Emit non-ASCII characters with unicode escape sequences.
1 parent bbf9579 commit a81bf8c

13 files changed

+65
-36
lines changed

src/compiler/core.ts

-23
Original file line numberDiff line numberDiff line change
@@ -623,29 +623,6 @@ module ts {
623623
"\u0085": "\\u0085" // nextLine
624624
};
625625

626-
/**
627-
* Based heavily on the abstract 'Quote'/ 'QuoteJSONString' operation from ECMA-262 (24.3.2.2),
628-
* but augmented for a few select characters.
629-
* Note that this doesn't actually wrap the input in double quotes.
630-
*/
631-
export function escapeString(s: string): string {
632-
// Prioritize '"' and '\'
633-
s = backslashOrDoubleQuote.test(s) ? s.replace(backslashOrDoubleQuote, getReplacement) : s;
634-
s = escapedCharsRegExp.test(s) ? s.replace(escapedCharsRegExp, getReplacement) : s;
635-
636-
return s;
637-
638-
function getReplacement(c: string) {
639-
return escapedCharsMap[c] || unicodeEscape(c);
640-
}
641-
642-
function unicodeEscape(c: string): string {
643-
var hexCharCode = c.charCodeAt(0).toString(16);
644-
var paddedHexCode = ("0000" + hexCharCode).slice(-4);
645-
return "\\u" + paddedHexCode;
646-
}
647-
}
648-
649626
export function getDefaultLibFileName(options: CompilerOptions): string {
650627
return options.target === ScriptTarget.ES6 ? "lib.es6.d.ts" : "lib.d.ts";
651628
}

src/compiler/emitter.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -2201,9 +2201,12 @@ module ts {
22012201
write(text);
22022202
}
22032203
}
2204-
2204+
22052205
function getTemplateLiteralAsStringLiteral(node: LiteralExpression): string {
2206-
return '"' + escapeString(node.text) + '"';
2206+
var result = escapeString(node.text);
2207+
result = replaceNonAsciiCharacters(result);
2208+
2209+
return '"' + result + '"';
22072210
}
22082211

22092212
function emitDownlevelRawTemplateLiteral(node: LiteralExpression) {

src/compiler/utilities.ts

+50-1
Original file line numberDiff line numberDiff line change
@@ -1120,7 +1120,7 @@ module ts {
11201120
newEndN = Math.max(newEnd2, newEnd2 + (newEnd1 - oldEnd2));
11211121
}
11221122

1123-
return createTextChangeRange(createTextSpanFromBounds(oldStartN, oldEndN), /*newLength: */newEndN - oldStartN);
1123+
return createTextChangeRange(createTextSpanFromBounds(oldStartN, oldEndN), /*newLength: */ newEndN - oldStartN);
11241124
}
11251125

11261126
// @internal
@@ -1202,4 +1202,53 @@ module ts {
12021202
}
12031203
}
12041204
}
1205+
1206+
var backslashOrDoubleQuote = /[\"\\]/g;
1207+
var escapedCharsRegExp = /[\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
1208+
var escapedCharsMap: Map<string> = {
1209+
"\0": "\\0",
1210+
"\t": "\\t",
1211+
"\v": "\\v",
1212+
"\f": "\\f",
1213+
"\b": "\\b",
1214+
"\r": "\\r",
1215+
"\n": "\\n",
1216+
"\\": "\\\\",
1217+
"\"": "\\\"",
1218+
"\u2028": "\\u2028", // lineSeparator
1219+
"\u2029": "\\u2029", // paragraphSeparator
1220+
"\u0085": "\\u0085" // nextLine
1221+
};
1222+
1223+
/**
1224+
* Based heavily on the abstract 'Quote'/ 'QuoteJSONString' operation from ECMA-262 (24.3.2.2),
1225+
* but augmented for a few select characters.
1226+
* Note that this doesn't actually wrap the input in double quotes.
1227+
*/
1228+
export function escapeString(s: string): string {
1229+
// Prioritize '"' and '\'
1230+
s = backslashOrDoubleQuote.test(s) ? s.replace(backslashOrDoubleQuote, getReplacement) : s;
1231+
s = escapedCharsRegExp.test(s) ? s.replace(escapedCharsRegExp, getReplacement) : s;
1232+
1233+
return s;
1234+
1235+
function getReplacement(c: string) {
1236+
return escapedCharsMap[c] || get16BitUnicodeEscapeSequence(c.charCodeAt(0));
1237+
}
1238+
}
1239+
1240+
function get16BitUnicodeEscapeSequence(charCode: number): string {
1241+
var hexCharCode = charCode.toString(16);
1242+
var paddedHexCode = ("0000" + hexCharCode).slice(-4);
1243+
return "\\u" + paddedHexCode;
1244+
}
1245+
1246+
var nonAsciiCharacters = /[^\u0000-\u007F]/g;
1247+
export function replaceNonAsciiCharacters(s: string): string {
1248+
// Replace non-ASCII characters with '\uNNNN' escapes if any exist.
1249+
// Otherwise just return the original string.
1250+
return nonAsciiCharacters.test(s) ?
1251+
s.replace(nonAsciiCharacters, c => get16BitUnicodeEscapeSequence(c.charCodeAt(0))) :
1252+
s;
1253+
}
12051254
}

tests/baselines/reference/taggedTemplateStringsWithUnicodeEscapes.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ function f() {
1111
args[_i - 0] = arguments[_i];
1212
}
1313
}
14-
(_a = ["'💩'", "'💩'"], _a.raw = ["'\\u{1f4a9}'", "'\\uD83D\\uDCA9'"], f(_a, " should be converted to "));
14+
(_a = ["'\ud83d\udca9'", "'\ud83d\udca9'"], _a.raw = ["'\\u{1f4a9}'", "'\\uD83D\\uDCA9'"], f(_a, " should be converted to "));
1515
var _a;

tests/baselines/reference/templateStringWhitespaceEscapes2.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66

77
//// [templateStringWhitespaceEscapes2.js]
88
// <TAB>, <VT>, <FF>, <SP>, <NBSP>, <BOM>
9-
"\t\v\f  ";
9+
"\t\v\f \u00a0\ufeff";

tests/baselines/reference/unicodeExtendedEscapesInTemplates06_ES5.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ var x = `\u{10FFFF}`;
88
//// [unicodeExtendedEscapesInTemplates06_ES5.js]
99
// ES6 Spec - 10.1.1 Static Semantics: UTF16Encoding (cp)
1010
// 1. Assert: 0 ≤ cp ≤ 0x10FFFF.
11-
var x = "􏿿";
11+
var x = "\udbff\udfff";

tests/baselines/reference/unicodeExtendedEscapesInTemplates08_ES5.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ var x = `\u{FFFF}`;
1010
// ES6 Spec - 10.1.1 Static Semantics: UTF16Encoding (cp)
1111
// 2. If cp ≤ 65535, return cp.
1212
// (FFFF == 65535)
13-
var x = "";
13+
var x = "\uffff";

tests/baselines/reference/unicodeExtendedEscapesInTemplates09_ES5.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ var x = `\u{10000}`;
1010
// ES6 Spec - 10.1.1 Static Semantics: UTF16Encoding (cp)
1111
// 2. If cp ≤ 65535, return cp.
1212
// (10000 == 65536)
13-
var x = "𐀀";
13+
var x = "\ud800\udc00";

tests/baselines/reference/unicodeExtendedEscapesInTemplates10_ES5.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ var x = `\u{D800}`;
1212
// 2. Let cu1 be floor((cp – 65536) / 1024) + 0xD800.
1313
// Although we should just get back a single code point value of 0xD800,
1414
// this is a useful edge-case test.
15-
var x = "";
15+
var x = "\ud800";

tests/baselines/reference/unicodeExtendedEscapesInTemplates11_ES5.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ var x = `\u{DC00}`;
1212
// 2. Let cu2 be ((cp – 65536) modulo 1024) + 0xDC00.
1313
// Although we should just get back a single code point value of 0xDC00,
1414
// this is a useful edge-case test.
15-
var x = "";
15+
var x = "\udc00";

tests/baselines/reference/unicodeExtendedEscapesInTemplates13_ES5.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ var x = `\u{DDDDD}`;
44

55

66
//// [unicodeExtendedEscapesInTemplates13_ES5.js]
7-
var x = "󝷝";
7+
var x = "\udb37\udddd";

tests/baselines/reference/unicodeExtendedEscapesInTemplates15_ES5.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ var x = `\u{abcd}\u{ef12}\u{3456}\u{7890}`;
44

55

66
//// [unicodeExtendedEscapesInTemplates15_ES5.js]
7-
var x = "ꯍ㑖碐";
7+
var x = "\uabcd\uef12\u3456\u7890";

tests/baselines/reference/unicodeExtendedEscapesInTemplates16_ES5.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ var x = `\u{ABCD}\u{EF12}\u{3456}\u{7890}`;
44

55

66
//// [unicodeExtendedEscapesInTemplates16_ES5.js]
7-
var x = "ꯍ㑖碐";
7+
var x = "\uabcd\uef12\u3456\u7890";

0 commit comments

Comments
 (0)