# HG changeset patch # User Tooru Fujisawa # Date 1428853172 -32400 # Mon Apr 13 00:39:32 2015 +0900 # Node ID 518bd4d322a19de77899c492fc9ffcfba07f2f48 # Parent 0a46652bd992eb3fdd3ef48f6b066650c33210a5 Bug 320500 - Add \u{xxxxxx} string literals. diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -1675,16 +1675,45 @@ TokenStream::getTokenInternal(TokenKind* // immediately. userbuf.poison(); #endif MOZ_MAKE_MEM_UNDEFINED(ttp, sizeof(*ttp)); return false; } bool +TokenStream::getBracedUnicode(uint32_t* cp) +{ + skipChars(1); + + bool first = true; + int32_t c; + uint32_t code = 0; + while (true) { + c = getCharIgnoreEOL(); + if (c == '}') { + if (first) + return false; + break; + } + + if (!JS7_ISHEX(c)) + return false; + + code = (code << 4) | JS7_UNHEX(c); + if (code > 0x10FFFF) + return false; + first = false; + } + + *cp = code; + return true; +} + +bool TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) { int c; int nc = -1; bool parsingTemplate = (untilChar == '`'); *tp = newToken(-1); @@ -1711,16 +1740,33 @@ TokenStream::getStringOrTemplateToken(in case '\n': // ES5 7.8.4: an escaped line terminator represents // no character. continue; // Unicode character specification. case 'u': { + if (peekChar() == '{') { + uint32_t code; + if (!getBracedUnicode(&code)) { + reportError(JSMSG_MALFORMED_ESCAPE, "Unicode"); + return false; + } + + if (code >= 0x10000) { + if (!tokenbuf.append((code - 0x10000) / 1024 + 0xD800)) + return false; + c = (code - 0x10000) % 1024 + 0xDC00; + } else { + c = code; + } + break; + } + char16_t cp[4]; if (peekChars(4, cp) && JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) { c = JS7_UNHEX(cp[0]); c = (c << 4) + JS7_UNHEX(cp[1]); c = (c << 4) + JS7_UNHEX(cp[2]); c = (c << 4) + JS7_UNHEX(cp[3]); diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -769,16 +769,17 @@ class MOZ_STACK_CLASS TokenStream const char16_t* base_; // base of buffer uint32_t startOffset_; // offset of base_[0] const char16_t* limit_; // limit for quick bounds check const char16_t* ptr; // next char to get }; bool getTokenInternal(TokenKind* ttp, Modifier modifier); + bool getBracedUnicode(uint32_t* code); bool getStringOrTemplateToken(int untilChar, Token** tp); int32_t getChar(); int32_t getCharIgnoreEOL(); void ungetChar(int32_t c); void ungetCharIgnoreEOL(int32_t c); Token* newToken(ptrdiff_t adjust); bool peekUnicodeEscape(int32_t* c); diff --git a/js/src/tests/ecma_6/String/unicode-braced.js b/js/src/tests/ecma_6/String/unicode-braced.js new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/String/unicode-braced.js @@ -0,0 +1,59 @@ +var BUGNUMBER = 320500; +var summary = 'Add \\u{xxxxxx} string literals'; + +print(BUGNUMBER + ": " + summary); + +assertEq("\u{0}", String.fromCodePoint(0x0)); +assertEq("\u{1}", String.fromCodePoint(0x1)); +assertEq("\u{10}", String.fromCodePoint(0x10)); +assertEq("\u{100}", String.fromCodePoint(0x100)); +assertEq("\u{1000}", String.fromCodePoint(0x1000)); +assertEq("\u{D7FF}", String.fromCodePoint(0xD7FF)); +assertEq("\u{D800}", String.fromCodePoint(0xD800)); +assertEq("\u{DBFF}", String.fromCodePoint(0xDBFF)); +assertEq("\u{DC00}", String.fromCodePoint(0xDC00)); +assertEq("\u{DFFF}", String.fromCodePoint(0xDFFF)); +assertEq("\u{E000}", String.fromCodePoint(0xE000)); +assertEq("\u{10000}", String.fromCodePoint(0x10000)); +assertEq("\u{100000}", String.fromCodePoint(0x100000)); +assertEq("\u{10FFFF}", String.fromCodePoint(0x10FFFF)); +assertEq("\u{10ffff}", String.fromCodePoint(0x10FFFF)); + +assertEq("A\u{1}\u{10}B\u{100}\u{1000}\u{10000}C\u{100000}", + "A" + + String.fromCodePoint(0x1) + + String.fromCodePoint(0x10) + + "B" + + String.fromCodePoint(0x100) + + String.fromCodePoint(0x1000) + + String.fromCodePoint(0x10000) + + "C" + + String.fromCodePoint(0x100000)); + +assertEq('\u{10ffff}', String.fromCodePoint(0x10FFFF)); +assertEq(`\u{10ffff}`, String.fromCodePoint(0x10FFFF)); +assertEq(`\u{10ffff}${""}`, String.fromCodePoint(0x10FFFF)); +assertEq(`${""}\u{10ffff}`, String.fromCodePoint(0x10FFFF)); +assertEq(`${""}\u{10ffff}${""}`, String.fromCodePoint(0x10FFFF)); + +assertEq("\u{00}", String.fromCodePoint(0x0)); +assertEq("\u{00000000000000000}", String.fromCodePoint(0x0)); +assertEq("\u{00000000000001000}", String.fromCodePoint(0x1000)); + +assertEq(eval(`"\\u{${"0".repeat(Math.pow(2, 28) - 20) + "1234"}}"`), String.fromCodePoint(0x1234)); + +assertEq("\U{0}", "U{0}"); + +assertThrowsInstanceOf(() => eval(`"\\u{-1}"`), SyntaxError); +assertThrowsInstanceOf(() => eval(`"\\u{0.0}"`), SyntaxError); +assertThrowsInstanceOf(() => eval(`"\\u{G}"`), SyntaxError); +assertThrowsInstanceOf(() => eval(`"\\u{}"`), SyntaxError); +assertThrowsInstanceOf(() => eval(`"\\u{{"`), SyntaxError); +assertThrowsInstanceOf(() => eval(`"\\u{"`), SyntaxError); +assertThrowsInstanceOf(() => eval(`"\\u{110000}"`), SyntaxError); +assertThrowsInstanceOf(() => eval(`"\\u{00110000}"`), SyntaxError); +assertThrowsInstanceOf(() => eval(`"\\u{100000000000000000000000000000}"`), SyntaxError); +assertThrowsInstanceOf(() => eval(`"\\u{FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF}"`), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true);