mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-29 02:27:30 +09:00
Issue #1732 - Implement JSON superset proposal.
This change makes unicode line and paragraph separators allowed in JS strings, removing the only quirk of the JS string definition that made JSON not a valid subset of JS. Resolves #1732
This commit is contained in:
parent
4ff9d87cb4
commit
34dc51690f
4 changed files with 44 additions and 89 deletions
|
|
@ -141,35 +141,29 @@ template <typename CharT>
|
||||||
static bool
|
static bool
|
||||||
EvalStringMightBeJSON(const mozilla::Range<const CharT> chars)
|
EvalStringMightBeJSON(const mozilla::Range<const CharT> chars)
|
||||||
{
|
{
|
||||||
// If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
|
// If the eval string starts with '(' or '[' and ends with ')' or ']', it
|
||||||
// Try the JSON parser first because it's much faster. If the eval string
|
// may be JSON. Try the JSON parser first because it's much faster. If
|
||||||
// isn't JSON, JSON parsing will probably fail quickly, so little time
|
// the eval string isn't JSON, JSON parsing will probably fail quickly, so
|
||||||
// will be lost.
|
// little time will be lost.
|
||||||
size_t length = chars.length();
|
size_t length = chars.length();
|
||||||
if (length > 2 &&
|
if (length < 2)
|
||||||
((chars[0] == '[' && chars[length - 1] == ']') ||
|
return false;
|
||||||
(chars[0] == '(' && chars[length - 1] == ')')))
|
|
||||||
{
|
|
||||||
// Remarkably, JavaScript syntax is not a superset of JSON syntax:
|
|
||||||
// strings in JavaScript cannot contain the Unicode line and paragraph
|
|
||||||
// terminator characters U+2028 and U+2029, but strings in JSON can.
|
|
||||||
// Rather than force the JSON parser to handle this quirk when used by
|
|
||||||
// eval, we simply don't use the JSON parser when either character
|
|
||||||
// appears in the provided string. See bug 657367.
|
|
||||||
if (sizeof(CharT) > 1) {
|
|
||||||
for (RangedPtr<const CharT> cp = chars.begin() + 1, end = chars.end() - 1;
|
|
||||||
cp < end;
|
|
||||||
cp++)
|
|
||||||
{
|
|
||||||
char16_t c = *cp;
|
|
||||||
if (c == 0x2028 || c == 0x2029)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
// It used to be that strings in JavaScript forbid U+2028 LINE SEPARATOR
|
||||||
}
|
// and U+2029 PARAGRAPH SEPARATOR, so something like
|
||||||
return false;
|
//
|
||||||
|
// eval("['" + "\u2028" + "']");
|
||||||
|
//
|
||||||
|
// i.e. an array containing a string with a line separator in it, *would*
|
||||||
|
// be JSON but *would not* be valid JavaScript. Handing such a string to
|
||||||
|
// the JSON parser would then fail to recognize a syntax error. As of
|
||||||
|
// <https://tc39.github.io/proposal-json-superset/> JavaScript strings may
|
||||||
|
// contain these two code points, so it's safe to JSON-parse eval strings
|
||||||
|
// that contain them.
|
||||||
|
|
||||||
|
CharT first = chars[0], last = chars[length - 1];
|
||||||
|
return (first == '[' && last == ']') ||
|
||||||
|
(first == '(' && last == ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CharT>
|
template <typename CharT>
|
||||||
|
|
|
||||||
|
|
@ -603,6 +603,10 @@ TokenStream::TokenBuf::findEOLMax(size_t start, size_t max)
|
||||||
if (n >= max)
|
if (n >= max)
|
||||||
break;
|
break;
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
|
// This stops at U+2028 LINE SEPARATOR or U+2029 PARAGRAPH SEPARATOR in
|
||||||
|
// string and template literals. These code points do affect line and
|
||||||
|
// column coordinates, even as they encode their literal values.
|
||||||
if (TokenBuf::isRawEOLChar(*p++))
|
if (TokenBuf::isRawEOLChar(*p++))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -2129,8 +2133,9 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (TokenBuf::isRawEOLChar(c)) {
|
} else if (c == '\r' || c == '\n') {
|
||||||
if (!parsingTemplate) {
|
if (!parsingTemplate) {
|
||||||
|
// String literals don't allow ASCII line breaks.
|
||||||
ungetCharIgnoreEOL(c);
|
ungetCharIgnoreEOL(c);
|
||||||
error(JSMSG_UNTERMINATED_STRING);
|
error(JSMSG_UNTERMINATED_STRING);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2138,10 +2143,18 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
|
||||||
if (c == '\r') {
|
if (c == '\r') {
|
||||||
c = '\n';
|
c = '\n';
|
||||||
if (userbuf.peekRawChar() == '\n')
|
if (userbuf.peekRawChar() == '\n')
|
||||||
|
// Treat CRLF as a single line break.
|
||||||
skipCharsIgnoreEOL(1);
|
skipCharsIgnoreEOL(1);
|
||||||
}
|
}
|
||||||
updateLineInfoForEOL();
|
updateLineInfoForEOL();
|
||||||
updateFlagsForEOL();
|
updateFlagsForEOL();
|
||||||
|
} else if (c == LINE_SEPARATOR || c == PARA_SEPARATOR) {
|
||||||
|
// U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR encode
|
||||||
|
// their literal values in template literals and (as of the
|
||||||
|
// JSON superset proposal) string literals, but they still count
|
||||||
|
// as line terminators when computing line/column coordinates.
|
||||||
|
updateLineInfoForEOL();
|
||||||
|
updateFlagsForEOL();
|
||||||
} else if (parsingTemplate && c == '$') {
|
} else if (parsingTemplate && c == '$') {
|
||||||
if ((nc = getCharIgnoreEOL()) == '{')
|
if ((nc = getCharIgnoreEOL()) == '{')
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -55,13 +55,10 @@ function testEvalHackNotJSON() {
|
||||||
arr = eval("[]; var z; [1, 2, 3, \"abc\u1200\"]");
|
arr = eval("[]; var z; [1, 2, 3, \"abc\u1200\"]");
|
||||||
assertEq(JSON.stringify(arr), '[1,2,3,"abc\u1200"]');
|
assertEq(JSON.stringify(arr), '[1,2,3,"abc\u1200"]');
|
||||||
|
|
||||||
try {
|
// JSON superset
|
||||||
eval("[1, 2, 3, \"abc\u2028\"]");
|
var arr = eval("[1, 2, 3, \"abc\u2028\"]");
|
||||||
throw new Error("U+2028 shouldn't eval");
|
assertEq(arr.length, 4);
|
||||||
} catch (e) {
|
assertEq(arr[3], "abc\u2028");
|
||||||
assertEq(e instanceof SyntaxError, true,
|
|
||||||
"should have thrown a SyntaxError, instead got " + e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
testEvalHackNotJSON();
|
testEvalHackNotJSON();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
var BUGNUMBER = 657367;
|
var BUGNUMBER = 657367;
|
||||||
var summary = "eval must not parse strings containing U+2028 or U+2029";
|
var summary =
|
||||||
|
"eval via the JSON parser should parse strings containing U+2028/U+2029 " +
|
||||||
|
"(as of <https://tc39.github.io/proposal-json-superset/>, that is)";
|
||||||
|
|
||||||
print(BUGNUMBER + ": " + summary);
|
print(BUGNUMBER + ": " + summary);
|
||||||
|
|
||||||
|
|
@ -11,59 +13,8 @@ print(BUGNUMBER + ": " + summary);
|
||||||
* BEGIN TEST *
|
* BEGIN TEST *
|
||||||
**************/
|
**************/
|
||||||
|
|
||||||
function esc(s)
|
assertEq(eval('("\u2028")'), "\u2028");
|
||||||
{
|
assertEq(eval('("\u2029")'), "\u2029");
|
||||||
return s.split("").map(function(v)
|
|
||||||
{
|
|
||||||
var code =
|
|
||||||
("000" + v.charCodeAt(0).toString(16)).slice(-4);
|
|
||||||
return "\\u" + code;
|
|
||||||
}).join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var r = eval('"\u2028"');
|
|
||||||
throw new Error("\"\\u2028\" didn't throw, returned " + esc(r));
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
assertEq(e instanceof SyntaxError, true,
|
|
||||||
"U+2028 is not a valid string character");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var r = eval('("\u2028")');
|
|
||||||
throw new Error("(\"\\u2028\") didn't throw, returned " + esc(r));
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
assertEq(e instanceof SyntaxError, true,
|
|
||||||
"U+2028 is not a valid string character");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var r = eval('"\u2029"');
|
|
||||||
throw new Error("\"\\u2029\" didn't throw, returned " + esc(r));
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
assertEq(e instanceof SyntaxError, true,
|
|
||||||
"U+2029 is not a valid string character");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var r = eval('("\u2029")');
|
|
||||||
throw new Error("(\"\\u2029\") didn't throw, returned " + esc(r));
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
assertEq(e instanceof SyntaxError, true,
|
|
||||||
"U+2029 is not a valid string character");
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue