Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
// Simple cases, not using eval.
{
let \u{61} = 123;
assertEq(a, 123);
let \u{6A} = 123;
assertEq(j, 123);
let a\u{62} = 456;
assertEq(ab, 456);
let \u{63}\u{6b} = 789;
assertEq(ck, 789);
}
const leadingZeros = [0, 1, 2, 3, 4, 100].map(c => "0".repeat(c));
// From DerivedCoreProperties.txt (Unicode 9):
// Derived Property: ID_Start
// Characters that can start an identifier.
// Generated from:
// Lu + Ll + Lt + Lm + Lo + Nl
// + Other_ID_Start
// - Pattern_Syntax
// - Pattern_White_Space
const idStart = [
0x0041, // LATIN CAPITAL LETTER A, Gc=Lu
0x006A, // LATIN SMALL LETTER J, Gc=Ll
0x00C9, // LATIN CAPITAL LETTER E WITH ACUTE, Gc=Lu
0x00FF, // LATIN SMALL LETTER Y WITH DIAERESIS, Gc=Ll
0x01C5, // LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON, Gc=Lt
0x0294, // LATIN LETTER GLOTTAL STOP, Gc=Lo
0x037A, // GREEK YPOGEGRAMMENI, Gc=Lm
0x16EE, // RUNIC ARLAUG SYMBOL, Gc=Nl
0xFF70, // HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK, Gc=Lm
];
const idStartSupplemental = [
0x10140, // GREEK ACROPHONIC ATTIC ONE QUARTER, Gc=Nl
0x10300, // OLD ITALIC LETTER A, Gc=Lo
0x10400, // DESERET CAPITAL LETTER LONG I, Gc=Lu
0x10430, // DESERET SMALL LETTER SHORT A, Gc=Ll
0x16B40, // PAHAWH HMONG SIGN VOS SEEV, Gc=Lm
];
// From PropList.txt (Unicode 9):
const otherIdStart = [
0x1885, // MONGOLIAN LETTER ALI GALI BALUDA, Gc=Mn
0x1886, // MONGOLIAN LETTER ALI GALI THREE BALUDA, Gc=Mn
0x2118, // SCRIPT CAPITAL P, Gc=Sm
0x212E, // ESTIMATED SYMBOL, Gc=So
0x309B, // KATAKANA-HIRAGANA VOICED SOUND MARK, Gc=Sk
0x309C, // KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK, Gc=Sk
];
// From DerivedCoreProperties.txt (Unicode 9):
// Derived Property: ID_Continue
// Characters that can continue an identifier.
// Generated from:
// ID_Start
// + Mn + Mc + Nd + Pc
// + Other_ID_Continue
// - Pattern_Syntax
// - Pattern_White_Space
const idContinue = [
0x0030, // DIGIT ZERO, Gc=Nd
0x0300, // COMBINING GRAVE ACCENT, Gc=Mn
0x0660, // ARABIC-INDIC DIGIT ZERO, Gc=Nd
0x0903, // DEVANAGARI SIGN VISARGA, Gc=Mc
0xFF10, // FULLWIDTH DIGIT ZERO, Gc=Nd
0xFF3F, // FULLWIDTH LOW LINE, Gc=Pc
];
const idContinueSupplemental = [
0x101FD, // PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE, Gc=Mn
0x104A0, // OSMANYA DIGIT ZERO, Gc=Nd
0x11000, // BRAHMI SIGN CANDRABINDU, Gc=Mc
];
// From PropList.txt (Unicode 9):
const otherIdContinue = [
0x00B7, // MIDDLE DOT, Gc=Po
0x0387, // GREEK ANO TELEIA, Gc=Po
0x1369, // ETHIOPIC DIGIT ONE, Gc=No
0x136A, // ETHIOPIC DIGIT TWO, Gc=No
0x136B, // ETHIOPIC DIGIT THREE, Gc=No
0x136C, // ETHIOPIC DIGIT FOUR, Gc=No
0x136D, // ETHIOPIC DIGIT FIVE, Gc=No
0x136E, // ETHIOPIC DIGIT SIX, Gc=No
0x136F, // ETHIOPIC DIGIT SEVEN, Gc=No
0x1370, // ETHIOPIC DIGIT EIGHT, Gc=No
0x1371, // ETHIOPIC DIGIT NINE, Gc=No
0x19DA, // NEW TAI LUE THAM DIGIT ONE, Gc=No
];
for (let ident of [...idStart, ...otherIdStart, ...idStartSupplemental]) {
for (let count of leadingZeros) {
let zeros = "0".repeat(count);
eval(`
let \\u{${zeros}${ident.toString(16)}} = 123;
assertEq(${String.fromCodePoint(ident)}, 123);
`);
}
}
for (let ident of [...idContinue, ...idContinueSupplemental, ...otherIdContinue]) {
for (let zeros of leadingZeros) {
assertThrowsInstanceOf(() => eval(`\\u{${zeros}${ident.toString(16)}}`), SyntaxError);
}
}
for (let ident of [...idStart, ...otherIdStart, ...idContinue, ...otherIdContinue, ...idStartSupplemental, ...idContinueSupplemental]) {
for (let zeros of leadingZeros) {
eval(`
let A\\u{${zeros}${ident.toString(16)}} = 123;
assertEq(${String.fromCodePoint(0x41, ident)}, 123);
`);
}
}
const notIdentifiers = [
0x0000, // NULL, Gc=Cc
0x000A, // LINE FEED (LF), Gc=Cc
0x005E, // CIRCUMFLEX ACCENT, Gc=Sk
0x00B1, // PLUS-MINUS SIGN, Gc=Sm
0xFF61, // HALFWIDTH IDEOGRAPHIC FULL STOP, Gc=Po
0x10061, // Not assigned.
0x10100, // AEGEAN WORD SEPARATOR LINE, Gc=Po
0x100061, // <Plane 16 Private Use>, Gc=Co
];
for (let ident of notIdentifiers) {
for (let zeros of leadingZeros) {
assertThrowsInstanceOf(() => eval(`\\u{${zeros}${ident.toString(16)}}`), SyntaxError);
}
}
const incompleteEscapes = [
"\\u{",
"\\u{6",
"\\u{61",
"\\u{061",
"\\u{0061",
"\\u{00061",
"\\u{000061",
"\\u{0000061",
"\\u}",
];
for (let invalid of incompleteEscapes) {
// Ends with EOF.
assertThrowsInstanceOf(() => eval(invalid), SyntaxError);
// Ends with EOL.
assertThrowsInstanceOf(() => eval(invalid + "\n"), SyntaxError);
// Ends with space.
assertThrowsInstanceOf(() => eval(invalid + " "), SyntaxError);
}
const invalidEscapes = [
// Empty escape.
"",
// Not hexadecimal characters.
"\0",
"G",
"Z",
"\uFFFF",
"\uDBFF\uDFFF",
// Has space characters.
" 61",
"61 ",
// Has newline characters.
"\n61",
"61\n",
// Exceeds 0x10FFFF, six characters.
"110000",
"110001",
"fffffe",
"ffffff",
// Exceeds 0x10FFFF, more than six characters.
"10ffff0",
"10ffffabcdef",
];
for (let invalid of invalidEscapes) {
for (let zeros of leadingZeros) {
assertThrowsInstanceOf(() => eval(`\\u{${zeros}${invalid}}`), SyntaxError);
assertThrowsInstanceOf(() => eval(`var \\u{${zeros}${invalid}}`), SyntaxError);
}
}
if (typeof reportCompare === "function")
reportCompare(0, 0, "ok");