diff --git a/JavaScriptCore/ChangeLog b/JavaScriptCore/ChangeLog index 21f30b6d6c48bd09e2088ec76c86a2925d6a7c17..d207e1ebcc6eda1522eb30604983837e06f6ed83 100644 --- a/JavaScriptCore/ChangeLog +++ b/JavaScriptCore/ChangeLog @@ -1,3 +1,32 @@ +2009-06-21 Oliver Hunt + + Reviewed by Darin Adler and Cameron Zwarich. + + Bug 26587: Support JSON.parse + + + Extend the LiteralParser to support the full strict JSON + grammar, fix a few places where the grammar was incorrectly + lenient. Doesn't yet support the JSON.parse reviver function + but that does not block the JSON.parse functionality itself. + + * interpreter/Interpreter.cpp: + (JSC::Interpreter::callEval): + * runtime/JSGlobalObjectFunctions.cpp: + (JSC::globalFuncEval): + * runtime/JSONObject.cpp: + (JSC::JSONProtoFuncParse): + * runtime/LiteralParser.cpp: + (JSC::LiteralParser::Lexer::lex): + (JSC::isSafeStringCharacter): + (JSC::LiteralParser::Lexer::lexString): + (JSC::LiteralParser::parse): + * runtime/LiteralParser.h: + (JSC::LiteralParser::LiteralParser): + (JSC::LiteralParser::tryJSONParse): + (JSC::LiteralParser::): + (JSC::LiteralParser::Lexer::Lexer): + 2009-06-21 David Levin Reviewed by NOBODY (speculative build fix for windows). diff --git a/JavaScriptCore/interpreter/Interpreter.cpp b/JavaScriptCore/interpreter/Interpreter.cpp index b3b681a1ee0ae27cb0342741beb633ec20c04095..1c800a9f0e50aa4d693efdde1cb943946a8d38fc 100644 --- a/JavaScriptCore/interpreter/Interpreter.cpp +++ b/JavaScriptCore/interpreter/Interpreter.cpp @@ -350,7 +350,7 @@ NEVER_INLINE JSValue Interpreter::callEval(CallFrame* callFrame, RegisterFile* r UString programSource = asString(program)->value(); - LiteralParser preparser(callFrame, programSource); + LiteralParser preparser(callFrame, programSource, LiteralParser::NonStrictJSON); if (JSValue parsedObject = preparser.tryLiteralParse()) return parsedObject; diff --git a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp index b013957eaee0d72c984b34d3e98c60434644a26a..85f92f2973d93b4b3c53a2ec3ddbb66f5b6ae9d1 100644 --- a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp +++ b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -282,7 +282,7 @@ JSValue JSC_HOST_CALL globalFuncEval(ExecState* exec, JSObject* function, JSValu UString s = x.toString(exec); - LiteralParser preparser(exec, s); + LiteralParser preparser(exec, s, LiteralParser::NonStrictJSON); if (JSValue parsedObject = preparser.tryLiteralParse()) return parsedObject; diff --git a/JavaScriptCore/runtime/JSONObject.cpp b/JavaScriptCore/runtime/JSONObject.cpp index f7e708e55db2c44e3d4a2556c98c82c0d0673eb4..c1b284c79b72f564392aebefde162791ac7b0194 100644 --- a/JavaScriptCore/runtime/JSONObject.cpp +++ b/JavaScriptCore/runtime/JSONObject.cpp @@ -29,6 +29,7 @@ #include "Error.h" #include "ExceptionHelpers.h" #include "JSArray.h" +#include "LiteralParser.h" #include "PropertyNameArray.h" #include @@ -36,6 +37,7 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(JSONObject); +static JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*, JSObject*, JSValue, const ArgList&); static JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*, JSObject*, JSValue, const ArgList&); } @@ -562,6 +564,7 @@ const ClassInfo JSONObject::info = { "JSON", 0, 0, ExecState::jsonTable }; /* Source for JSONObject.lut.h @begin jsonTable + parse JSONProtoFuncParse DontEnum|Function 1 stringify JSONProtoFuncStringify DontEnum|Function 1 @end */ @@ -584,6 +587,24 @@ void JSONObject::markStringifiers(Stringifier* stringifier) stringifier->mark(); } +// ECMA-262 v5 15.12.3 +JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec, JSObject*, JSValue, const ArgList& args) +{ + if (args.isEmpty()) + return throwError(exec, GeneralError, "JSON.parse requires at least one parameter"); + JSValue value = args.at(0); + UString source = value.toString(exec); + if (exec->hadException()) + return jsNull(); + + LiteralParser jsonParser(exec, source, LiteralParser::StrictJSON); + JSValue parsedObject = jsonParser.tryLiteralParse(); + if (!parsedObject) + return throwError(exec, SyntaxError, "Unable to parse JSON string"); + + return parsedObject; +} + // ECMA-262 v5 15.12.3 JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState* exec, JSObject*, JSValue, const ArgList& args) { diff --git a/JavaScriptCore/runtime/LiteralParser.cpp b/JavaScriptCore/runtime/LiteralParser.cpp index 20b3b787fc16f9483d759ba84f7989c550461e98..72563fe6cd24036b3bfadab2f48c436bdad813ea 100644 --- a/JavaScriptCore/runtime/LiteralParser.cpp +++ b/JavaScriptCore/runtime/LiteralParser.cpp @@ -28,15 +28,11 @@ #include "JSArray.h" #include "JSString.h" +#include "Lexer.h" #include namespace JSC { -static bool isSafeStringCharacter(UChar c) -{ - return (c >= ' ' && c <= 0xff && c != '\\') || c == '\t'; -} - LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token) { while (m_ptr < m_end && isASCIISpace(*m_ptr)) @@ -84,8 +80,33 @@ LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token) token.end = ++m_ptr; return TokColon; case '"': - return lexString(token); - + if (m_mode == StrictJSON) + return lexString(token); + return lexString(token); + case 't': + if (m_end - m_ptr >= 4 && m_ptr[1] == 'r' && m_ptr[2] == 'u' && m_ptr[3] == 'e') { + m_ptr += 4; + token.type = TokTrue; + token.end = m_ptr; + return TokTrue; + } + break; + case 'f': + if (m_end - m_ptr >= 5 && m_ptr[1] == 'a' && m_ptr[2] == 'l' && m_ptr[3] == 's' && m_ptr[4] == 'e') { + m_ptr += 5; + token.type = TokFalse; + token.end = m_ptr; + return TokFalse; + } + break; + case 'n': + if (m_end - m_ptr >= 4 && m_ptr[1] == 'u' && m_ptr[2] == 'l' && m_ptr[3] == 'l') { + m_ptr += 4; + token.type = TokNull; + token.end = m_ptr; + return TokNull; + } + break; case '-': case '0': case '1': @@ -102,16 +123,80 @@ LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token) return TokError; } -LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token) +static inline bool isSafeStringCharacter(UChar c) +{ + return (c >= ' ' && c <= 0xff && c != '\\' && c != '"') || c == '\t'; +} + +template LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token) { ++m_ptr; - while (m_ptr < m_end && isSafeStringCharacter(*m_ptr) && *m_ptr != '"') - ++m_ptr; - if (m_ptr >= m_end || *m_ptr != '"') { - token.type = TokError; - token.end = ++m_ptr; + const UChar* runStart; + token.stringToken = UString(); + do { + runStart = m_ptr; + while (m_ptr < m_end && isSafeStringCharacter(*m_ptr)) + ++m_ptr; + if (runStart < m_ptr) + token.stringToken.append(runStart, m_ptr - runStart); + if ((mode == StrictJSON) && m_ptr < m_end && *m_ptr == '\\') { + ++m_ptr; + if (m_ptr >= m_end) + return TokError; + switch (*m_ptr) { + case '"': + token.stringToken.append('"'); + m_ptr++; + break; + case '\\': + token.stringToken.append('\\'); + m_ptr++; + break; + case '/': + token.stringToken.append('/'); + m_ptr++; + break; + case 'b': + token.stringToken.append('\b'); + m_ptr++; + break; + case 'f': + token.stringToken.append('\f'); + m_ptr++; + break; + case 'n': + token.stringToken.append('\n'); + m_ptr++; + break; + case 'r': + token.stringToken.append('\r'); + m_ptr++; + break; + case 't': + token.stringToken.append('\t'); + m_ptr++; + break; + + case 'u': + if ((m_end - m_ptr) < 5) // uNNNN == 5 characters + return TokError; + for (int i = 1; i < 5; i++) { + if (!isASCIIHexDigit(m_ptr[i])) + return TokError; + } + token.stringToken.append(JSC::Lexer::convertUnicode(m_ptr[1], m_ptr[2], m_ptr[3], m_ptr[4])); + m_ptr += 5; + break; + + default: + return TokError; + } + } + } while ((mode == StrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != '"'); + + if (m_ptr >= m_end || *m_ptr != '"') return TokError; - } + token.type = TokString; token.end = ++m_ptr; return TokString; @@ -151,7 +236,7 @@ LiteralParser::TokenType LiteralParser::Lexer::lexNumber(LiteralParserToken& tok if (m_ptr < m_end && *m_ptr == '.') { ++m_ptr; // [0-9]+ - if (m_ptr >= m_end && !isASCIIDigit(*m_ptr)) + if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) return TokError; ++m_ptr; @@ -168,7 +253,7 @@ LiteralParser::TokenType LiteralParser::Lexer::lexNumber(LiteralParserToken& tok ++m_ptr; // [0-9]+ - if (m_ptr >= m_end && !isASCIIDigit(*m_ptr)) + if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) return TokError; ++m_ptr; @@ -226,7 +311,25 @@ JSValue LiteralParser::parse(ParserState initialState) case StartParseObject: { JSObject* object = constructEmptyObject(m_exec); objectStack.append(object); - // fallthrough + + TokenType type = m_lexer.next(); + if (type == TokString) { + Lexer::LiteralParserToken identifierToken = m_lexer.currentToken(); + + // Check for colon + if (m_lexer.next() != TokColon) + return JSValue(); + + m_lexer.next(); + identifierStack.append(Identifier(m_exec, identifierToken.stringToken)); + stateStack.append(DoParseObjectEndExpression); + goto startParseExpression; + } else if (type != TokRBrace) + return JSValue(); + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; } doParseObjectStartExpression: case DoParseObjectStartExpression: { @@ -239,10 +342,10 @@ JSValue LiteralParser::parse(ParserState initialState) return JSValue(); m_lexer.next(); - identifierStack.append(Identifier(m_exec, identifierToken.start + 1, identifierToken.end - identifierToken.start - 2)); + identifierStack.append(Identifier(m_exec, identifierToken.stringToken)); stateStack.append(DoParseObjectEndExpression); goto startParseExpression; - } else if (type != TokRBrace) + } else return JSValue(); m_lexer.next(); lastValue = objectStack.last(); @@ -272,7 +375,7 @@ JSValue LiteralParser::parse(ParserState initialState) case TokString: { Lexer::LiteralParserToken stringToken = m_lexer.currentToken(); m_lexer.next(); - lastValue = jsString(m_exec, UString(stringToken.start + 1, stringToken.end - stringToken.start - 2)); + lastValue = jsString(m_exec, stringToken.stringToken); break; } case TokNumber: { @@ -281,6 +384,21 @@ JSValue LiteralParser::parse(ParserState initialState) lastValue = jsNumber(m_exec, UString(numberToken.start, numberToken.end - numberToken.start).toDouble()); break; } + case TokNull: + m_lexer.next(); + lastValue = jsNull(); + break; + + case TokTrue: + m_lexer.next(); + lastValue = jsBoolean(true); + break; + + case TokFalse: + m_lexer.next(); + lastValue = jsBoolean(false); + break; + default: // Error return JSValue(); diff --git a/JavaScriptCore/runtime/LiteralParser.h b/JavaScriptCore/runtime/LiteralParser.h index 7d028f71fa2a305c1114ec2136a09cade0844972..1fe33af337863b345ceb6a468921bfceb0641b49 100644 --- a/JavaScriptCore/runtime/LiteralParser.h +++ b/JavaScriptCore/runtime/LiteralParser.h @@ -34,16 +34,18 @@ namespace JSC { class LiteralParser { public: - LiteralParser(ExecState* exec, const UString& s) + typedef enum { StrictJSON, NonStrictJSON } ParserMode; + LiteralParser(ExecState* exec, const UString& s, ParserMode mode) : m_exec(exec) - , m_lexer(s) + , m_lexer(s, mode) + , m_mode(mode) { } JSValue tryLiteralParse() { m_lexer.next(); - JSValue result = parse(StartParseStatement); + JSValue result = parse(m_mode == StrictJSON ? StartParseExpression : StartParseStatement); if (m_lexer.currentToken().type != TokEnd) return JSValue(); return result; @@ -55,7 +57,8 @@ namespace JSC { DoParseArrayStartExpression, DoParseArrayEndExpression }; enum TokenType { TokLBracket, TokRBracket, TokLBrace, TokRBrace, TokString, TokIdentifier, TokNumber, TokColon, - TokLParen, TokRParen, TokComma, TokEnd, TokError }; + TokLParen, TokRParen, TokComma, TokTrue, TokFalse, + TokNull, TokEnd, TokError }; class Lexer { public: @@ -63,9 +66,11 @@ namespace JSC { TokenType type; const UChar* start; const UChar* end; + UString stringToken; }; - Lexer(const UString& s) + Lexer(const UString& s, ParserMode mode) : m_string(s) + , m_mode(mode) , m_ptr(s.data()) , m_end(s.data() + s.size()) { @@ -83,10 +88,11 @@ namespace JSC { private: TokenType lex(LiteralParserToken&); - TokenType lexString(LiteralParserToken&); + template TokenType lexString(LiteralParserToken&); TokenType lexNumber(LiteralParserToken&); LiteralParserToken m_currentToken; UString m_string; + ParserMode m_mode; const UChar* m_ptr; const UChar* m_end; }; @@ -96,6 +102,7 @@ namespace JSC { ExecState* m_exec; LiteralParser::Lexer m_lexer; + ParserMode m_mode; }; } diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog index 08e6a8c49484eb679a5bb98130ec0ac83b86ba52..80c8d10fb34e92c2d625e39d63c47cf7b7eaf1bc 100644 --- a/LayoutTests/ChangeLog +++ b/LayoutTests/ChangeLog @@ -1,3 +1,17 @@ +2009-06-21 Oliver Hunt + + Reviewed by Darin Adler and Cameron Zwarich. + + Bug 26587: Support JSON.parse + + Add tests to cover basic usage of JSON.parse + + * fast/js/JSON-parse.html: Added. + * fast/js/JSON-parse-expected.txt: Added. + * fast/js/resources/JSON-parse.js: Added. + (createTests.result): + (createTests): + 2009-06-21 Drew Wilson Reviewed by David Levin. diff --git a/LayoutTests/fast/js/JSON-parse-expected.txt b/LayoutTests/fast/js/JSON-parse-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..92b6bf431d2cf23bdb3939493f44b1633b9b3ccd --- /dev/null +++ b/LayoutTests/fast/js/JSON-parse-expected.txt @@ -0,0 +1,396 @@ +function (jsonObject) { + return jsonObject.parse(); + } +PASS tests[i](nativeJSON) threw exception Error: JSON.parse requires at least one parameter. +function (jsonObject) { + return jsonObject.parse(''); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('1'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('-1'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('Infinity'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('NaN'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('null'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('undefined'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('{}'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('({})'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('{a}'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('{a:}'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('{a:5}'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('{a:5,}'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('{"a"}'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('{"a":}'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('{"a":5}'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('{"a":5,}'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +json2.js did not throw for a test we expect to throw. +function (jsonObject) { + return jsonObject.parse('{"a":5,,}'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('{"a":5,"a",}'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('{"a":(5,"a"),}'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('[]'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('[1]'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('[1,]'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('[1,2]'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('[1,2,,]'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +json2.js did not throw for a test we expect to throw. +function (jsonObject) { + return jsonObject.parse('[1,2,,4]'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +json2.js did not throw for a test we expect to throw. +function (jsonObject) { + return jsonObject.parse('""'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"\'"'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\"'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\"'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\z"'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\\z"'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\\\z"'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\tz"'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\tz"'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\nz"'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\nz"'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\rz"'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\rz"'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\/z"'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\/z"'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\bz"'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +json2.js did not throw for a test we expect to throw. +function (jsonObject) { + return jsonObject.parse('"a\\bz"'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\rz"'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\rz"'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\uz" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\u0z" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\u00z" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\u000z" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\u0000z" '); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\u000Az" '); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\u000az" '); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\u000Gz" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\u000gz" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\u00A0z" '); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\u00a0z" '); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\u00G0z" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\u00g0z" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\u0A00z" '); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\u0a00z" '); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\u0G00z" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\u0g00z" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\uA000z" '); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\ua000z" '); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('"a\\uG000z" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('"a\\ug000z" '); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('00'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +json2.js did not throw for a test we expect to throw. +function (jsonObject) { + return jsonObject.parse('01'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +json2.js did not throw for a test we expect to throw. +function (jsonObject) { + return jsonObject.parse('0.a'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('0x0'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('2e1.3'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('2e-+10'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('2e+-10'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('2e3e4'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('-01.0'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('-01'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +json2.js did not throw for a test we expect to throw. +function (jsonObject) { + return jsonObject.parse('-01.a'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +function (jsonObject) { + return jsonObject.parse('1.e1'); + } +PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string. +json2.js did not throw for a test we expect to throw. +function (jsonObject) { + return jsonObject.parse('true'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse('false'); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse(JSON.stringify(simpleObject)); + } +PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected +function (jsonObject) { + return jsonObject.parse(JSON.stringify(complexObject)); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse(JSON.stringify(complexObject)); + } +PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected +function (jsonObject) { + return jsonObject.parse(JSON.stringify(simpleObject,null,100)); + } +PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected +function (jsonObject) { + return jsonObject.parse(JSON.stringify(complexObject,null,100)); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse(JSON.stringify(complexObject,null,100)); + } +PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected +function (jsonObject) { + return jsonObject.parse(JSON.stringify(simpleObject,null," ")); + } +PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected +function (jsonObject) { + return jsonObject.parse(JSON.stringify(complexObject,null," ")); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse(JSON.stringify(complexObject,null," ")); + } +PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected +function (jsonObject) { + return jsonObject.parse(JSON.stringify(simpleObject,null,"\t")); + } +PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected +function (jsonObject) { + return jsonObject.parse(JSON.stringify(complexObject,null,"\t")); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse(JSON.stringify(complexObject,null,"\t")); + } +PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected +function (jsonObject) { + return jsonObject.parse(JSON.stringify(simpleObject,null,"\n")); + } +PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected +function (jsonObject) { + return jsonObject.parse(JSON.stringify(complexObject,null,"\n")); + } +PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON)) +function (jsonObject) { + return jsonObject.parse(JSON.stringify(complexObject,null,"\n")); + } +PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected +PASS successfullyParsed is true + +TEST COMPLETE + diff --git a/LayoutTests/fast/js/JSON-parse.html b/LayoutTests/fast/js/JSON-parse.html new file mode 100644 index 0000000000000000000000000000000000000000..d659bee06730064f33fc77339579d610a090c5c4 --- /dev/null +++ b/LayoutTests/fast/js/JSON-parse.html @@ -0,0 +1,18 @@ + + + + + + + +

+
+ + + + + + diff --git a/LayoutTests/fast/js/resources/JSON-parse.js b/LayoutTests/fast/js/resources/JSON-parse.js new file mode 100644 index 0000000000000000000000000000000000000000..e9080e99845363bad9ab30280ec61282f06093a2 --- /dev/null +++ b/LayoutTests/fast/js/resources/JSON-parse.js @@ -0,0 +1,383 @@ + +function createTests() { + var result = []; + result.push(function(jsonObject){ + return jsonObject.parse(); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse(''); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('1'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('-1'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('Infinity'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('NaN'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('null'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('undefined'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('{}'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('({})'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('{a}'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('{a:}'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('{a:5}'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('{a:5,}'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('{"a"}'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('{"a":}'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('{"a":5}'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('{"a":5,}'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('{"a":5,,}'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('{"a":5,"a",}'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('{"a":(5,"a"),}'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('[]'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('[1]'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('[1,]'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('[1,2]'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('[1,2,,]'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('[1,2,,4]'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('""'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"\'"'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\"'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\"'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\z"'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\\z"'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\\\z"'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\tz"'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\tz"'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\nz"'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\nz"'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\rz"'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\rz"'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\/z"'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\/z"'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\bz"'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\bz"'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\rz"'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\rz"'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\uz" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u0z" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u00z" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u000z" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u0000z" '); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u000Az" '); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u000az" '); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u000Gz" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u000gz" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u00A0z" '); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u00a0z" '); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u00G0z" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u00g0z" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u0A00z" '); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u0a00z" '); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u0G00z" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\u0g00z" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\uA000z" '); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\ua000z" '); + }); + result.push(function(jsonObject){ + return jsonObject.parse('"a\\uG000z" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('"a\\ug000z" '); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('00'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('01'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('0.a'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('0x0'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('2e1.3'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('2e-+10'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('2e+-10'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('2e3e4'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('-01.0'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('-01'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('-01.a'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('1.e1'); + }); + result[result.length - 1].throws = true; + result.push(function(jsonObject){ + return jsonObject.parse('true'); + }); + result.push(function(jsonObject){ + return jsonObject.parse('false'); + }); + var simpleArray = ['a', 'b', 'c']; + var simpleObject = {a:"1", b:"2", c:"3"}; + var complexArray = ['a', 'b', 'c',,,simpleObject, simpleArray, [simpleObject,simpleArray]]; + var complexObject = {a:"1", b:"2", c:"3", d:4.5e10, g: 0.45e-5, h: 0.0, i: 0, j:.5, k:0., l:-0, m:-0.0, n:-0., o:-.5, p:-0.45e-10, q:-4.5e10, e:null, "":12, f: simpleArray, array: complexArray}; + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(simpleObject)); + }); + result[result.length - 1].expected = JSON.stringify(simpleObject); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(complexObject)); + }); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(complexObject)); + }); + result[result.length - 1].expected = JSON.stringify(complexObject); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(simpleObject,null,100)); + }); + result[result.length - 1].expected = JSON.stringify(simpleObject); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(complexObject,null,100)); + }); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(complexObject,null,100)); + }); + result[result.length - 1].expected = JSON.stringify(complexObject); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(simpleObject,null," ")); + }); + result[result.length - 1].expected = JSON.stringify(simpleObject); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(complexObject,null," ")); + }); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(complexObject,null," ")); + }); + result[result.length - 1].expected = JSON.stringify(complexObject); + + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(simpleObject,null,"\t")); + }); + result[result.length - 1].expected = JSON.stringify(simpleObject); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(complexObject,null,"\t")); + }); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(complexObject,null,"\t")); + }); + result[result.length - 1].expected = JSON.stringify(complexObject); + + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(simpleObject,null,"\n")); + }); + result[result.length - 1].expected = JSON.stringify(simpleObject); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(complexObject,null,"\n")); + }); + result.push(function(jsonObject){ + return jsonObject.parse(JSON.stringify(complexObject,null,"\n")); + }); + result[result.length - 1].expected = JSON.stringify(complexObject); + + return result; +} +var tests = createTests(); +for (var i = 0; i < tests.length; i++) { + try { + debug(tests[i]); + if (tests[i].throws) { + shouldThrow('tests[i](nativeJSON)'); + try { + var threw = false; + tests[i](JSON); + } catch(e) { + var threw = true; + } + if (!threw) + debug("json2.js did not throw for a test we expect to throw."); + } else if (tests[i].expected) + try { shouldBe('JSON.stringify(tests[i](nativeJSON))', "tests[i].expected") } catch(e) { debug("threw - " + e)} + else + try { shouldBe('JSON.stringify(tests[i](nativeJSON))', 'JSON.stringify(tests[i](JSON))') } catch(e) { debug("threw - " + e) }; + }catch(e){ + debug(e); + } +} +successfullyParsed = true; +