mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 657013 - Remove the old JSON parser. r=njn
--HG-- extra : rebase_source : 5c11e9179d2291d498667c17c05ff7e5f8ace3f2
This commit is contained in:
parent
ac8938a32d
commit
4cf43e2ca4
@ -1182,20 +1182,7 @@ EvalKernel(JSContext *cx, const CallArgs &call, EvalType evalType, StackFrame *c
|
||||
* will be lost.
|
||||
*/
|
||||
if (length > 2 && chars[0] == '(' && chars[length - 1] == ')') {
|
||||
#if USE_OLD_AND_BUSTED_JSON_PARSER
|
||||
Value tmp;
|
||||
JSONParser *jp = js_BeginJSONParse(cx, &tmp, /* suppressErrors = */true);
|
||||
if (jp != NULL) {
|
||||
/* Run JSON-parser on string inside ( and ). */
|
||||
bool ok = js_ConsumeJSONText(cx, jp, chars + 1, length - 2);
|
||||
ok &= js_FinishJSONParse(cx, jp, NullValue());
|
||||
if (ok) {
|
||||
call.rval() = tmp;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
JSONSourceParser parser(cx, chars + 1, length - 2, JSONSourceParser::StrictJSON,
|
||||
JSONSourceParser::NoError);
|
||||
JSONParser parser(cx, chars + 1, length - 2, JSONParser::StrictJSON, JSONParser::NoError);
|
||||
Value tmp;
|
||||
if (!parser.parse(&tmp))
|
||||
return false;
|
||||
@ -1203,7 +1190,6 @@ EvalKernel(JSContext *cx, const CallArgs &call, EvalType evalType, StackFrame *c
|
||||
call.rval() = tmp;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
618
js/src/json.cpp
618
js/src/json.cpp
@ -72,35 +72,6 @@
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4351)
|
||||
#endif
|
||||
|
||||
struct JSONParser
|
||||
{
|
||||
JSONParser(JSContext *cx)
|
||||
: hexChar(), numHex(), statep(), stateStack(), rootVal(), objectStack(),
|
||||
objectKey(cx), buffer(cx), suppressErrors(false)
|
||||
{}
|
||||
|
||||
/* Used while handling \uNNNN in strings */
|
||||
jschar hexChar;
|
||||
uint8 numHex;
|
||||
|
||||
JSONParserState *statep;
|
||||
JSONParserState stateStack[JSON_MAX_DEPTH];
|
||||
Value *rootVal;
|
||||
JSObject *objectStack;
|
||||
js::Vector<jschar, 8> objectKey;
|
||||
js::Vector<jschar, 8> buffer;
|
||||
bool suppressErrors;
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
Class js_JSONClass = {
|
||||
js_JSON_str,
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_JSON),
|
||||
@ -796,18 +767,6 @@ js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, Value space, StringBu
|
||||
return Str(cx, *vp, &scx);
|
||||
}
|
||||
|
||||
// helper to determine whether a character could be part of a number
|
||||
static JSBool IsNumChar(jschar c)
|
||||
{
|
||||
return ((c <= '9' && c >= '0') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E');
|
||||
}
|
||||
|
||||
static JSBool HandleDataString(JSContext *cx, JSONParser *jp);
|
||||
static JSBool HandleDataKeyString(JSContext *cx, JSONParser *jp);
|
||||
static JSBool HandleDataNumber(JSContext *cx, JSONParser *jp);
|
||||
static JSBool HandleDataKeyword(JSContext *cx, JSONParser *jp);
|
||||
static JSBool PopState(JSContext *cx, JSONParser *jp);
|
||||
|
||||
/* ES5 15.12.2 Walk. */
|
||||
static bool
|
||||
Walk(JSContext *cx, JSObject *holder, jsid name, const Value &reviver, Value *vp)
|
||||
@ -910,14 +869,6 @@ Walk(JSContext *cx, JSObject *holder, jsid name, const Value &reviver, Value *vp
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
JSONParseError(JSONParser *jp, JSContext *cx)
|
||||
{
|
||||
if (!jp->suppressErrors)
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE, "syntax error");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static bool
|
||||
Revive(JSContext *cx, const Value &reviver, Value *vp)
|
||||
{
|
||||
@ -935,96 +886,15 @@ Revive(JSContext *cx, const Value &reviver, Value *vp)
|
||||
return Walk(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), reviver, vp);
|
||||
}
|
||||
|
||||
JSONParser *
|
||||
js_BeginJSONParse(JSContext *cx, Value *rootVal, bool suppressErrors /*= false*/)
|
||||
{
|
||||
if (!cx)
|
||||
return NULL;
|
||||
|
||||
JSObject *arr = NewDenseEmptyArray(cx);
|
||||
if (!arr)
|
||||
return NULL;
|
||||
|
||||
JSONParser *jp = cx->new_<JSONParser>(cx);
|
||||
if (!jp)
|
||||
return NULL;
|
||||
|
||||
jp->objectStack = arr;
|
||||
if (!JS_AddNamedObjectRoot(cx, &jp->objectStack, "JSON parse stack"))
|
||||
goto bad;
|
||||
|
||||
jp->statep = jp->stateStack;
|
||||
*jp->statep = JSON_PARSE_STATE_INIT;
|
||||
jp->rootVal = rootVal;
|
||||
jp->suppressErrors = suppressErrors;
|
||||
|
||||
return jp;
|
||||
|
||||
bad:
|
||||
js_FinishJSONParse(cx, jp, NullValue());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
js_FinishJSONParse(JSContext *cx, JSONParser *jp, const Value &reviver)
|
||||
{
|
||||
if (!jp)
|
||||
return true;
|
||||
|
||||
JSBool early_ok = JS_TRUE;
|
||||
|
||||
// Check for unprocessed primitives at the root. This doesn't happen for
|
||||
// strings because a closing quote triggers value processing.
|
||||
if ((jp->statep - jp->stateStack) == 1) {
|
||||
if (*jp->statep == JSON_PARSE_STATE_KEYWORD) {
|
||||
early_ok = HandleDataKeyword(cx, jp);
|
||||
if (early_ok)
|
||||
PopState(cx, jp);
|
||||
} else if (*jp->statep == JSON_PARSE_STATE_NUMBER) {
|
||||
early_ok = HandleDataNumber(cx, jp);
|
||||
if (early_ok)
|
||||
PopState(cx, jp);
|
||||
}
|
||||
}
|
||||
|
||||
// This internal API is infallible, in spite of its JSBool return type.
|
||||
js_RemoveRoot(cx->runtime, &jp->objectStack);
|
||||
|
||||
bool ok = *jp->statep == JSON_PARSE_STATE_FINISHED;
|
||||
Value *vp = jp->rootVal;
|
||||
|
||||
if (!early_ok) {
|
||||
ok = false;
|
||||
} else if (!ok) {
|
||||
JSONParseError(jp, cx);
|
||||
} else if (js_IsCallable(reviver)) {
|
||||
ok = Revive(cx, reviver, vp);
|
||||
}
|
||||
|
||||
cx->delete_(jp);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
JSBool
|
||||
ParseJSONWithReviver(JSContext *cx, const jschar *chars, size_t length, const Value &reviver,
|
||||
Value *vp, DecodingMode decodingMode /* = STRICT */)
|
||||
{
|
||||
#if USE_OLD_AND_BUSTED_JSON_PARSER
|
||||
JSONParser *jp = js_BeginJSONParse(cx, vp);
|
||||
if (!jp)
|
||||
return false;
|
||||
JSBool ok = js_ConsumeJSONText(cx, jp, chars, length, decodingMode);
|
||||
ok &= !!js_FinishJSONParse(cx, jp, reviver);
|
||||
return ok;
|
||||
#else
|
||||
/* 15.12.2 steps 2-3. */
|
||||
JSONSourceParser parser(cx, chars, length,
|
||||
decodingMode == STRICT
|
||||
? JSONSourceParser::StrictJSON
|
||||
: JSONSourceParser::LegacyJSON);
|
||||
JSONParser parser(cx, chars, length,
|
||||
decodingMode == STRICT ? JSONParser::StrictJSON : JSONParser::LegacyJSON);
|
||||
if (!parser.parse(vp))
|
||||
return false;
|
||||
|
||||
@ -1032,494 +902,10 @@ ParseJSONWithReviver(JSContext *cx, const jschar *chars, size_t length, const Va
|
||||
if (js_IsCallable(reviver))
|
||||
return Revive(cx, reviver, vp);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
static JSBool
|
||||
PushState(JSContext *cx, JSONParser *jp, JSONParserState state)
|
||||
{
|
||||
if (*jp->statep == JSON_PARSE_STATE_FINISHED) {
|
||||
// extra input
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
|
||||
jp->statep++;
|
||||
if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack)) {
|
||||
// too deep
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
|
||||
*jp->statep = state;
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PopState(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
jp->statep--;
|
||||
if (jp->statep < jp->stateStack) {
|
||||
jp->statep = jp->stateStack;
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
|
||||
if (*jp->statep == JSON_PARSE_STATE_INIT)
|
||||
*jp->statep = JSON_PARSE_STATE_FINISHED;
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, const Value &value)
|
||||
{
|
||||
JSBool ok;
|
||||
if (parent->isArray()) {
|
||||
jsuint len;
|
||||
ok = js_GetLengthProperty(cx, parent, &len);
|
||||
if (ok) {
|
||||
jsid index;
|
||||
if (!IndexToId(cx, len, &index))
|
||||
return JS_FALSE;
|
||||
ok = parent->defineProperty(cx, index, value, NULL, NULL, JSPROP_ENUMERATE);
|
||||
}
|
||||
} else {
|
||||
ok = JS_DefineUCProperty(cx, parent, jp->objectKey.begin(),
|
||||
jp->objectKey.length(), Jsvalify(value),
|
||||
NULL, NULL, JSPROP_ENUMERATE);
|
||||
jp->objectKey.clear();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
|
||||
{
|
||||
jsuint len;
|
||||
if (!js_GetLengthProperty(cx, jp->objectStack, &len))
|
||||
return JS_FALSE;
|
||||
if (len >= JSON_MAX_DEPTH)
|
||||
return JSONParseError(jp, cx);
|
||||
|
||||
AutoObjectRooter tvr(cx, obj);
|
||||
Value v = ObjectOrNullValue(obj);
|
||||
|
||||
// Check if this is the root object
|
||||
if (len == 0) {
|
||||
*jp->rootVal = v;
|
||||
// This property must be enumerable to keep the array dense
|
||||
if (!jp->objectStack->defineProperty(cx, INT_TO_JSID(0), *jp->rootVal,
|
||||
NULL, NULL, JSPROP_ENUMERATE)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
Value p;
|
||||
if (!jp->objectStack->getProperty(cx, INT_TO_JSID(len - 1), &p))
|
||||
return JS_FALSE;
|
||||
|
||||
JSObject *parent = &p.toObject();
|
||||
if (!PushValue(cx, jp, parent, v))
|
||||
return JS_FALSE;
|
||||
|
||||
// This property must be enumerable to keep the array dense
|
||||
if (!jp->objectStack->defineProperty(cx, INT_TO_JSID(len), v,
|
||||
NULL, NULL, JSPROP_ENUMERATE)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
OpenObject(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
|
||||
return PushObject(cx, jp, obj);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
OpenArray(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
// Add an array to an existing array or object
|
||||
JSObject *arr = NewDenseEmptyArray(cx);
|
||||
if (!arr)
|
||||
return JS_FALSE;
|
||||
|
||||
return PushObject(cx, jp, arr);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
CloseObject(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
jsuint len;
|
||||
if (!js_GetLengthProperty(cx, jp->objectStack, &len))
|
||||
return JS_FALSE;
|
||||
if (!js_SetLengthProperty(cx, jp->objectStack, len - 1))
|
||||
return JS_FALSE;
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
CloseArray(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
return CloseObject(cx, jp);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PushPrimitive(JSContext *cx, JSONParser *jp, const Value &value)
|
||||
{
|
||||
AutoValueRooter tvr(cx, value);
|
||||
|
||||
jsuint len;
|
||||
if (!js_GetLengthProperty(cx, jp->objectStack, &len))
|
||||
return JS_FALSE;
|
||||
|
||||
if (len > 0) {
|
||||
Value o;
|
||||
if (!jp->objectStack->getProperty(cx, INT_TO_JSID(len - 1), &o))
|
||||
return JS_FALSE;
|
||||
|
||||
return PushValue(cx, jp, &o.toObject(), value);
|
||||
}
|
||||
|
||||
// root value must be primitive
|
||||
*jp->rootVal = value;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
|
||||
{
|
||||
const jschar *ep;
|
||||
double val;
|
||||
if (!js_strtod(cx, buf, buf + len, &ep, &val))
|
||||
return JS_FALSE;
|
||||
if (ep != buf + len) {
|
||||
// bad number input
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
|
||||
return PushPrimitive(cx, jp, NumberValue(val));
|
||||
}
|
||||
|
||||
static JSBool
|
||||
HandleString(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
|
||||
{
|
||||
JSString *str = js_NewStringCopyN(cx, buf, len);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
|
||||
return PushPrimitive(cx, jp, StringValue(str));
|
||||
}
|
||||
|
||||
static JSBool
|
||||
HandleKeyword(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
|
||||
{
|
||||
const KeywordInfo *ki = FindKeyword(buf, len);
|
||||
if (!ki || ki->tokentype != TOK_PRIMARY) {
|
||||
// bad keyword
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
|
||||
Value keyword;
|
||||
if (buf[0] == 'n') {
|
||||
keyword.setNull();
|
||||
} else if (buf[0] == 't') {
|
||||
keyword.setBoolean(true);
|
||||
} else if (buf[0] == 'f') {
|
||||
keyword.setBoolean(false);
|
||||
} else {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
|
||||
return PushPrimitive(cx, jp, keyword);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
HandleDataString(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
JSBool ok = HandleString(cx, jp, jp->buffer.begin(), jp->buffer.length());
|
||||
if (ok)
|
||||
jp->buffer.clear();
|
||||
return ok;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
HandleDataKeyString(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
JSBool ok = jp->objectKey.append(jp->buffer.begin(), jp->buffer.end());
|
||||
if (ok)
|
||||
jp->buffer.clear();
|
||||
return ok;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
HandleDataNumber(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
JSBool ok = HandleNumber(cx, jp, jp->buffer.begin(), jp->buffer.length());
|
||||
if (ok)
|
||||
jp->buffer.clear();
|
||||
return ok;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
HandleDataKeyword(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
JSBool ok = HandleKeyword(cx, jp, jp->buffer.begin(), jp->buffer.length());
|
||||
if (ok)
|
||||
jp->buffer.clear();
|
||||
return ok;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len,
|
||||
DecodingMode decodingMode)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
if (*jp->statep == JSON_PARSE_STATE_INIT) {
|
||||
PushState(cx, jp, JSON_PARSE_STATE_VALUE);
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < len; i++) {
|
||||
jschar c = data[i];
|
||||
switch (*jp->statep) {
|
||||
case JSON_PARSE_STATE_ARRAY_INITIAL_VALUE:
|
||||
if (c == ']') {
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
JS_ASSERT(*jp->statep == JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT);
|
||||
if (!CloseArray(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
// fall through if non-empty array or whitespace
|
||||
|
||||
case JSON_PARSE_STATE_VALUE:
|
||||
if (c == '"') {
|
||||
*jp->statep = JSON_PARSE_STATE_STRING;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsNumChar(c)) {
|
||||
*jp->statep = JSON_PARSE_STATE_NUMBER;
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (JS7_ISLET(c)) {
|
||||
*jp->statep = JSON_PARSE_STATE_KEYWORD;
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '{') {
|
||||
*jp->statep = JSON_PARSE_STATE_OBJECT_AFTER_PAIR;
|
||||
if (!OpenObject(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_OBJECT_INITIAL_PAIR))
|
||||
return JS_FALSE;
|
||||
} else if (c == '[') {
|
||||
*jp->statep = JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT;
|
||||
if (!OpenArray(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_ARRAY_INITIAL_VALUE))
|
||||
return JS_FALSE;
|
||||
} else if (JS_ISXMLSPACE(c)) {
|
||||
// nothing to do
|
||||
} else if (decodingMode == LEGACY && c == ']') {
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
JS_ASSERT(*jp->statep == JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT);
|
||||
if (!CloseArray(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT:
|
||||
if (c == ',') {
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_VALUE))
|
||||
return JS_FALSE;
|
||||
} else if (c == ']') {
|
||||
if (!CloseArray(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_AFTER_PAIR:
|
||||
if (c == ',') {
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR))
|
||||
return JS_FALSE;
|
||||
} else if (c == '}') {
|
||||
if (!CloseObject(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_INITIAL_PAIR:
|
||||
if (c == '}') {
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
JS_ASSERT(*jp->statep == JSON_PARSE_STATE_OBJECT_AFTER_PAIR);
|
||||
if (!CloseObject(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
// fall through if non-empty object or whitespace
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_PAIR:
|
||||
if (c == '"') {
|
||||
// we want to be waiting for a : when the string has been read
|
||||
*jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR;
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_STRING))
|
||||
return JS_FALSE;
|
||||
} else if (JS_ISXMLSPACE(c)) {
|
||||
// nothing to do
|
||||
} else if (decodingMode == LEGACY && c == '}') {
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
JS_ASSERT(*jp->statep == JSON_PARSE_STATE_OBJECT_AFTER_PAIR);
|
||||
if (!CloseObject(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_IN_PAIR:
|
||||
if (c == ':') {
|
||||
*jp->statep = JSON_PARSE_STATE_VALUE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_STRING:
|
||||
if (c == '"') {
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) {
|
||||
if (!HandleDataKeyString(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
if (!HandleDataString(cx, jp))
|
||||
return JS_FALSE;
|
||||
}
|
||||
} else if (c == '\\') {
|
||||
*jp->statep = JSON_PARSE_STATE_STRING_ESCAPE;
|
||||
} else if (c <= 0x1F) {
|
||||
// The JSON lexical grammer does not allow a JSONStringCharacter to be
|
||||
// any of the Unicode characters U+0000 thru U+001F (control characters).
|
||||
return JSONParseError(jp, cx);
|
||||
} else {
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_STRING_ESCAPE:
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
break;
|
||||
case 'b' : c = '\b'; break;
|
||||
case 'f' : c = '\f'; break;
|
||||
case 'n' : c = '\n'; break;
|
||||
case 'r' : c = '\r'; break;
|
||||
case 't' : c = '\t'; break;
|
||||
default :
|
||||
if (c == 'u') {
|
||||
jp->numHex = 0;
|
||||
jp->hexChar = 0;
|
||||
*jp->statep = JSON_PARSE_STATE_STRING_HEX;
|
||||
continue;
|
||||
} else {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
}
|
||||
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
*jp->statep = JSON_PARSE_STATE_STRING;
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_STRING_HEX:
|
||||
if (('0' <= c) && (c <= '9')) {
|
||||
jp->hexChar = (jp->hexChar << 4) | (c - '0');
|
||||
} else if (('a' <= c) && (c <= 'f')) {
|
||||
jp->hexChar = (jp->hexChar << 4) | (c - 'a' + 0x0a);
|
||||
} else if (('A' <= c) && (c <= 'F')) {
|
||||
jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a);
|
||||
} else {
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
|
||||
if (++(jp->numHex) == 4) {
|
||||
if (!jp->buffer.append(jp->hexChar))
|
||||
return JS_FALSE;
|
||||
jp->hexChar = 0;
|
||||
jp->numHex = 0;
|
||||
*jp->statep = JSON_PARSE_STATE_STRING;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_KEYWORD:
|
||||
if (JS7_ISLET(c)) {
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
// this character isn't part of the keyword, process it again
|
||||
i--;
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!HandleDataKeyword(cx, jp))
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_NUMBER:
|
||||
if (IsNumChar(c)) {
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
// this character isn't part of the number, process it again
|
||||
i--;
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
if (!HandleDataNumber(cx, jp))
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_FINISHED:
|
||||
if (!JS_ISXMLSPACE(c)) {
|
||||
// extra input
|
||||
return JSONParseError(jp, cx);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
JS_NOT_REACHED("Invalid JSON parser state");
|
||||
}
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
#if JS_HAS_TOSOURCE
|
||||
static JSBool
|
||||
json_toSource(JSContext *cx, uintN argc, Value *vp)
|
||||
|
@ -57,59 +57,6 @@ js_Stringify(JSContext *cx, js::Value *vp, JSObject *replacer, js::Value space,
|
||||
|
||||
extern JSBool js_TryJSON(JSContext *cx, js::Value *vp);
|
||||
|
||||
/* JSON parsing states; most permit leading whitespace. */
|
||||
enum JSONParserState {
|
||||
/* Start of string. */
|
||||
JSON_PARSE_STATE_INIT,
|
||||
|
||||
/* JSON fully processed, expecting only trailing whitespace. */
|
||||
JSON_PARSE_STATE_FINISHED,
|
||||
|
||||
/* Start of JSON value. */
|
||||
JSON_PARSE_STATE_VALUE,
|
||||
|
||||
/* Start of first key/value pair in object, or at }. */
|
||||
JSON_PARSE_STATE_OBJECT_INITIAL_PAIR,
|
||||
|
||||
/* Start of subsequent key/value pair in object, after delimiting comma. */
|
||||
JSON_PARSE_STATE_OBJECT_PAIR,
|
||||
|
||||
/* At : in key/value pair in object. */
|
||||
JSON_PARSE_STATE_OBJECT_IN_PAIR,
|
||||
|
||||
/* Immediately after key/value pair in object: at , or }. */
|
||||
JSON_PARSE_STATE_OBJECT_AFTER_PAIR,
|
||||
|
||||
/* Start of first element of array or at ]. */
|
||||
JSON_PARSE_STATE_ARRAY_INITIAL_VALUE,
|
||||
|
||||
/* Immediately after element in array: at , or ]. */
|
||||
JSON_PARSE_STATE_ARRAY_AFTER_ELEMENT,
|
||||
|
||||
|
||||
/* The following states allow no leading whitespace. */
|
||||
|
||||
/* Within string literal. */
|
||||
JSON_PARSE_STATE_STRING,
|
||||
|
||||
/* At first character after \ in string literal. */
|
||||
JSON_PARSE_STATE_STRING_ESCAPE,
|
||||
|
||||
/* Within numbers in \uXXXX in string literal. */
|
||||
JSON_PARSE_STATE_STRING_HEX,
|
||||
|
||||
/* Within numeric literal. */
|
||||
JSON_PARSE_STATE_NUMBER,
|
||||
|
||||
/* Handling keywords (only null/true/false pass validity post-check). */
|
||||
JSON_PARSE_STATE_KEYWORD
|
||||
};
|
||||
|
||||
struct JSONParser;
|
||||
|
||||
extern JSONParser *
|
||||
js_BeginJSONParse(JSContext *cx, js::Value *rootVal, bool suppressErrors = false);
|
||||
|
||||
/* Aargh, Windows. */
|
||||
#ifdef STRICT
|
||||
#undef STRICT
|
||||
@ -127,13 +74,6 @@ js_BeginJSONParse(JSContext *cx, js::Value *rootVal, bool suppressErrors = false
|
||||
*/
|
||||
enum DecodingMode { STRICT, LEGACY };
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len,
|
||||
DecodingMode decodingMode = STRICT);
|
||||
|
||||
extern bool
|
||||
js_FinishJSONParse(JSContext *cx, JSONParser *jp, const js::Value &reviver);
|
||||
|
||||
namespace js {
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
|
@ -48,21 +48,21 @@
|
||||
using namespace js;
|
||||
|
||||
void
|
||||
JSONSourceParser::error(const char *msg)
|
||||
JSONParser::error(const char *msg)
|
||||
{
|
||||
if (errorHandling == RaiseError)
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE, msg);
|
||||
}
|
||||
|
||||
bool
|
||||
JSONSourceParser::errorReturn()
|
||||
JSONParser::errorReturn()
|
||||
{
|
||||
return errorHandling == NoError;
|
||||
}
|
||||
|
||||
template<JSONSourceParser::StringType ST>
|
||||
JSONSourceParser::Token
|
||||
JSONSourceParser::readString()
|
||||
template<JSONParser::StringType ST>
|
||||
JSONParser::Token
|
||||
JSONParser::readString()
|
||||
{
|
||||
JS_ASSERT(current < end);
|
||||
JS_ASSERT(*current == '"');
|
||||
@ -86,7 +86,7 @@ JSONSourceParser::readString()
|
||||
if (*current == '"') {
|
||||
size_t length = current - start;
|
||||
current++;
|
||||
JSFlatString *str = (ST == JSONSourceParser::PropertyName)
|
||||
JSFlatString *str = (ST == JSONParser::PropertyName)
|
||||
? js_AtomizeChars(cx, start, length)
|
||||
: js_NewStringCopyN(cx, start, length);
|
||||
if (!str)
|
||||
@ -118,7 +118,7 @@ JSONSourceParser::readString()
|
||||
|
||||
jschar c = *current++;
|
||||
if (c == '"') {
|
||||
JSFlatString *str = (ST == JSONSourceParser::PropertyName)
|
||||
JSFlatString *str = (ST == JSONParser::PropertyName)
|
||||
? buffer.finishAtom()
|
||||
: buffer.finishString();
|
||||
if (!str)
|
||||
@ -181,8 +181,8 @@ JSONSourceParser::readString()
|
||||
return token(Error);
|
||||
}
|
||||
|
||||
JSONSourceParser::Token
|
||||
JSONSourceParser::readNumber()
|
||||
JSONParser::Token
|
||||
JSONParser::readNumber()
|
||||
{
|
||||
JS_ASSERT(current < end);
|
||||
JS_ASSERT(JS7_ISDEC(*current) || *current == '-');
|
||||
@ -276,8 +276,8 @@ IsJSONWhitespace(jschar c)
|
||||
return c == '\t' || c == '\r' || c == '\n' || c == ' ';
|
||||
}
|
||||
|
||||
JSONSourceParser::Token
|
||||
JSONSourceParser::advance()
|
||||
JSONParser::Token
|
||||
JSONParser::advance()
|
||||
{
|
||||
while (current < end && IsJSONWhitespace(*current))
|
||||
current++;
|
||||
@ -357,8 +357,8 @@ JSONSourceParser::advance()
|
||||
}
|
||||
}
|
||||
|
||||
JSONSourceParser::Token
|
||||
JSONSourceParser::advanceAfterObjectOpen()
|
||||
JSONParser::Token
|
||||
JSONParser::advanceAfterObjectOpen()
|
||||
{
|
||||
JS_ASSERT(current[-1] == '{');
|
||||
|
||||
@ -408,8 +408,8 @@ AssertPastValue(const jschar *current)
|
||||
JS7_ISDEC(current[-1]));
|
||||
}
|
||||
|
||||
JSONSourceParser::Token
|
||||
JSONSourceParser::advanceAfterArrayElement()
|
||||
JSONParser::Token
|
||||
JSONParser::advanceAfterArrayElement()
|
||||
{
|
||||
AssertPastValue(current);
|
||||
|
||||
@ -434,8 +434,8 @@ JSONSourceParser::advanceAfterArrayElement()
|
||||
return token(Error);
|
||||
}
|
||||
|
||||
JSONSourceParser::Token
|
||||
JSONSourceParser::advancePropertyName()
|
||||
JSONParser::Token
|
||||
JSONParser::advancePropertyName()
|
||||
{
|
||||
JS_ASSERT(current[-1] == ',');
|
||||
|
||||
@ -466,8 +466,8 @@ JSONSourceParser::advancePropertyName()
|
||||
return token(Error);
|
||||
}
|
||||
|
||||
JSONSourceParser::Token
|
||||
JSONSourceParser::advancePropertyColon()
|
||||
JSONParser::Token
|
||||
JSONParser::advancePropertyColon()
|
||||
{
|
||||
JS_ASSERT(current[-1] == '"');
|
||||
|
||||
@ -487,8 +487,8 @@ JSONSourceParser::advancePropertyColon()
|
||||
return token(Error);
|
||||
}
|
||||
|
||||
JSONSourceParser::Token
|
||||
JSONSourceParser::advanceAfterProperty()
|
||||
JSONParser::Token
|
||||
JSONParser::advanceAfterProperty()
|
||||
{
|
||||
AssertPastValue(current);
|
||||
|
||||
@ -514,13 +514,13 @@ JSONSourceParser::advanceAfterProperty()
|
||||
}
|
||||
|
||||
/*
|
||||
* This enum is local to JSONSourceParser::parse, below, but ISO C++98 doesn't
|
||||
* allow templates to depend on local types. Boo-urns!
|
||||
* This enum is local to JSONParser::parse, below, but ISO C++98 doesn't allow
|
||||
* templates to depend on local types. Boo-urns!
|
||||
*/
|
||||
enum ParserState { FinishArrayElement, FinishObjectMember, JSONValue };
|
||||
|
||||
bool
|
||||
JSONSourceParser::parse(Value *vp)
|
||||
JSONParser::parse(Value *vp)
|
||||
{
|
||||
Vector<ParserState> stateStack(cx);
|
||||
AutoValueVector valueStack(cx);
|
||||
|
@ -47,12 +47,9 @@
|
||||
#include "jsvalue.h"
|
||||
|
||||
/*
|
||||
* This class should be JSONParser, but the old JSON parser uses that name, so
|
||||
* until we remove the old parser the class name will be overlong.
|
||||
*
|
||||
* NB: This class must only be used on the stack as it contains a js::Value.
|
||||
*/
|
||||
class JSONSourceParser
|
||||
class JSONParser
|
||||
{
|
||||
public:
|
||||
enum ErrorHandling { RaiseError, NoError };
|
||||
@ -88,9 +85,9 @@ class JSONSourceParser
|
||||
* Description of this syntax is deliberately omitted: new code should only
|
||||
* use strict JSON parsing.
|
||||
*/
|
||||
JSONSourceParser(JSContext *cx, const jschar *data, size_t length,
|
||||
ParsingMode parsingMode = StrictJSON,
|
||||
ErrorHandling errorHandling = RaiseError)
|
||||
JSONParser(JSContext *cx, const jschar *data, size_t length,
|
||||
ParsingMode parsingMode = StrictJSON,
|
||||
ErrorHandling errorHandling = RaiseError)
|
||||
: cx(cx),
|
||||
current(data, data, length),
|
||||
end(data + length, data, length),
|
||||
|
@ -161,7 +161,6 @@ typedef struct JSXDRState JSXDRState;
|
||||
typedef struct JSExceptionState JSExceptionState;
|
||||
typedef struct JSLocaleCallbacks JSLocaleCallbacks;
|
||||
typedef struct JSSecurityCallbacks JSSecurityCallbacks;
|
||||
typedef struct JSONParser JSONParser;
|
||||
typedef struct JSCompartment JSCompartment;
|
||||
typedef struct JSCrossCompartmentCall JSCrossCompartmentCall;
|
||||
typedef struct JSStructuredCloneWriter JSStructuredCloneWriter;
|
||||
|
@ -219,13 +219,3 @@
|
||||
* support likely to be made opt-in at some future time.
|
||||
*/
|
||||
#define OLD_GETTER_SETTER_METHODS 1
|
||||
|
||||
/*
|
||||
* Embedders: don't change this: it's a bake-until-ready hack only!
|
||||
*
|
||||
* NB: Changing this value requires adjusting the pass/fail state of a handful
|
||||
* of tests in ecma_5/JSON/ which the old parser implemented incorrectly.
|
||||
* Also make sure to rename JSONSourceParser to just JSONParser when the
|
||||
* old parser is removed completely.
|
||||
*/
|
||||
#define USE_OLD_AND_BUSTED_JSON_PARSER 0
|
||||
|
@ -4,11 +4,14 @@ script small-codepoints.js
|
||||
script parse.js
|
||||
script parse-arguments.js
|
||||
script parse-crockford-01.js
|
||||
script parse-number-syntax.js
|
||||
script parse-octal-syntax-error.js
|
||||
script parse-primitives.js
|
||||
script parse-reviver.js
|
||||
script parse-reviver-array-delete.js
|
||||
script parse-syntax-errors-01.js
|
||||
script parse-syntax-errors-02.js
|
||||
script parse-syntax-errors-03.js
|
||||
script stringify.js
|
||||
script stringify-boxed-primitives.js
|
||||
script stringify-call-replacer-once.js
|
||||
@ -28,11 +31,3 @@ script stringify-replacer-with-array-indexes.js
|
||||
script stringify-special-escapes.js
|
||||
script stringify-toJSON-arguments.js
|
||||
script trailing-comma.js
|
||||
|
||||
# These tests pass with the new parser but fail with the old one. If you're
|
||||
# changing which parser is used, you'll need to change these test declarations
|
||||
# accordingly. (And if you're removing the old parser, re-alphabetize these
|
||||
# tests into the list above.)
|
||||
script parse-number-syntax.js
|
||||
script parse-octal-syntax-error.js
|
||||
script parse-syntax-errors-03.js
|
||||
|
Loading…
Reference in New Issue
Block a user