mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 08:12:05 +00:00
Bug 476374 - JSON.parse does not support reviver argument as defined in spec. r=jorendorff
This commit is contained in:
parent
57a234ccb4
commit
bc53f36141
@ -529,7 +529,7 @@ nsJSONListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
JSBool ok = JS_FinishJSONParse(mCx, mJSONParser);
|
||||
JSBool ok = JS_FinishJSONParse(mCx, mJSONParser, JSVAL_NULL);
|
||||
mJSONParser = nsnull;
|
||||
|
||||
if (!ok)
|
||||
@ -651,7 +651,7 @@ nsJSONListener::ConsumeConverted(const char* aBuffer, PRUint32 aByteLength)
|
||||
void nsJSONListener::Cleanup()
|
||||
{
|
||||
if (mJSONParser)
|
||||
JS_FinishJSONParse(mCx, mJSONParser);
|
||||
JS_FinishJSONParse(mCx, mJSONParser, JSVAL_NULL);
|
||||
mJSONParser = nsnull;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ function tooDeep() {
|
||||
var arr = [];
|
||||
var root = [arr];
|
||||
var tail;
|
||||
for (var i = 0; i < 100000; i++) {
|
||||
for (var i = 0; i < 5000; i++) {
|
||||
tail = [];
|
||||
arr.push(tail);
|
||||
arr = tail;
|
||||
|
21
dom/src/json/test/unit/test_reviver.js
Normal file
21
dom/src/json/test/unit/test_reviver.js
Normal file
@ -0,0 +1,21 @@
|
||||
function doubler(k, v) {
|
||||
do_check_true("string" == typeof k);
|
||||
|
||||
if ((typeof v) == "number")
|
||||
return 2 * v;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
var x = JSON.parse('{"a":5,"b":6}', doubler);
|
||||
do_check_true(x.hasOwnProperty('a'));
|
||||
do_check_true(x.hasOwnProperty('b'));
|
||||
do_check_eq(x.a, 10);
|
||||
do_check_eq(x.b, 12);
|
||||
|
||||
x = JSON.parse('[3, 4, 5]', doubler);
|
||||
do_check_eq(x[0], 6);
|
||||
do_check_eq(x[1], 8);
|
||||
do_check_eq(x[2], 10);
|
||||
}
|
@ -307,7 +307,7 @@ nsDOMWorkerMessageEvent::GetData(nsAString& aData)
|
||||
(uint32)mData.Length());
|
||||
|
||||
// Note the '&& ok' after the call here!
|
||||
ok = JS_FinishJSONParse(cx, parser) && ok;
|
||||
ok = JS_FinishJSONParse(cx, parser, JSVAL_NULL) && ok;
|
||||
if (!ok) {
|
||||
mCachedJSVal = JSVAL_NULL;
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
@ -5688,10 +5688,10 @@ JS_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_FinishJSONParse(JSContext *cx, JSONParser *jp)
|
||||
JS_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
return js_FinishJSONParse(cx, jp);
|
||||
return js_FinishJSONParse(cx, jp, reviver);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2513,7 +2513,7 @@ JS_PUBLIC_API(JSBool)
|
||||
JS_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len);
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_FinishJSONParse(JSContext *cx, JSONParser *jp);
|
||||
JS_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver);
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
|
@ -2235,17 +2235,14 @@ js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags)
|
||||
JSObject *
|
||||
js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags)
|
||||
{
|
||||
JSObject *callable;
|
||||
JSObject *callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp);
|
||||
|
||||
callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp);
|
||||
if (callable &&
|
||||
((callable->map->ops == &js_ObjectOps)
|
||||
? OBJ_GET_CLASS(cx, callable)->call
|
||||
: callable->map->ops->call)) {
|
||||
if (js_IsCallable(cx, callable)) {
|
||||
*vp = OBJECT_TO_JSVAL(callable);
|
||||
} else {
|
||||
callable = js_ValueToFunctionObject(cx, vp, flags);
|
||||
}
|
||||
|
||||
return callable;
|
||||
}
|
||||
|
||||
|
@ -755,6 +755,14 @@ extern const char *
|
||||
js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
|
||||
JSPrincipals *principals, uintN *linenop);
|
||||
|
||||
/* TODO: bug 481218. This returns false for functions */
|
||||
static JS_INLINE JSBool
|
||||
js_IsCallable(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
return (obj && ((obj->map->ops == &js_ObjectOps) ? OBJ_GET_CLASS(cx, obj)->call
|
||||
: obj->map->ops->call));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_FRIEND_API(void) js_DumpChars(const jschar *s, size_t n);
|
||||
JS_FRIEND_API(void) js_DumpString(JSString *str);
|
||||
|
126
js/src/json.cpp
126
js/src/json.cpp
@ -68,15 +68,17 @@ js_json_parse(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
JSString *s = NULL;
|
||||
jsval *argv = vp + 2;
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "S", &s))
|
||||
jsval reviver = JSVAL_VOID;
|
||||
JSAutoTempValueRooter(cx, 1, &reviver);
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "S / v", &s, &reviver))
|
||||
return JS_FALSE;
|
||||
|
||||
JSONParser *jp = js_BeginJSONParse(cx, vp);
|
||||
JSBool ok = jp != NULL;
|
||||
if (ok) {
|
||||
ok = js_ConsumeJSONText(cx, jp, JS_GetStringChars(s), JS_GetStringLength(s));
|
||||
ok &= js_FinishJSONParse(cx, jp);
|
||||
ok &= js_FinishJSONParse(cx, jp, reviver);
|
||||
}
|
||||
|
||||
return ok;
|
||||
@ -279,7 +281,10 @@ stringify(JSContext *cx, jsval *vp, JSObject *replacer,
|
||||
if (isArray) {
|
||||
if ((jsuint)i >= length)
|
||||
break;
|
||||
ok = OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(i), &outputValue);
|
||||
jsid index;
|
||||
if (!js_IndexToId(cx, i, &index))
|
||||
return JS_FALSE;
|
||||
ok = OBJ_GET_PROPERTY(cx, obj, index, &outputValue);
|
||||
if (!ok)
|
||||
break;
|
||||
i++;
|
||||
@ -413,6 +418,107 @@ static JSBool IsNumChar(jschar c)
|
||||
static JSBool HandleData(JSContext *cx, JSONParser *jp, JSONDataType type);
|
||||
static JSBool PopState(JSContext *cx, JSONParser *jp);
|
||||
|
||||
static JSBool
|
||||
DestroyIdArrayOnError(JSContext *cx, JSIdArray *ida) {
|
||||
JS_DestroyIdArray(cx, ida);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Walk(JSContext *cx, jsid id, JSObject *holder, jsval reviver, jsval *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return JS_FALSE);
|
||||
|
||||
if (!OBJ_GET_PROPERTY(cx, holder, id, vp))
|
||||
return JS_FALSE;
|
||||
|
||||
JSObject *obj;
|
||||
|
||||
if (!JSVAL_IS_PRIMITIVE(*vp) && !JS_ObjectIsFunction(cx, obj = JSVAL_TO_OBJECT(*vp)) &&
|
||||
!js_IsCallable(cx, obj)) {
|
||||
jsval propValue = JSVAL_VOID;
|
||||
JSAutoTempValueRooter tvr(cx, 1, &propValue);
|
||||
|
||||
if(OBJ_IS_ARRAY(cx, obj)) {
|
||||
jsuint length = 0;
|
||||
if (!js_GetLengthProperty(cx, obj, &length))
|
||||
return JS_FALSE;
|
||||
|
||||
for (jsuint i = 0; i < length; i++) {
|
||||
jsid index;
|
||||
if (!js_IndexToId(cx, i, &index))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!Walk(cx, index, obj, reviver, &propValue))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!OBJ_DEFINE_PROPERTY(cx, obj, index, propValue,
|
||||
NULL, NULL, JSPROP_ENUMERATE, NULL)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
JSIdArray *ida = JS_Enumerate(cx, obj);
|
||||
if (!ida)
|
||||
return JS_FALSE;
|
||||
|
||||
JSAutoTempValueRooter idaroot(cx, JS_ARRAY_LENGTH(ida), (jsval*)ida);
|
||||
|
||||
for(jsint i = 0; i < ida->length; i++) {
|
||||
jsid idName = ida->vector[i];
|
||||
if (!Walk(cx, idName, obj, reviver, &propValue))
|
||||
return DestroyIdArrayOnError(cx, ida);
|
||||
if (propValue == JSVAL_VOID) {
|
||||
if (!js_DeleteProperty(cx, obj, idName, &propValue))
|
||||
return DestroyIdArrayOnError(cx, ida);
|
||||
} else {
|
||||
if (!OBJ_DEFINE_PROPERTY(cx, obj, idName, propValue,
|
||||
NULL, NULL, JSPROP_ENUMERATE, NULL)) {
|
||||
return DestroyIdArrayOnError(cx, ida);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JS_DestroyIdArray(cx, ida);
|
||||
}
|
||||
}
|
||||
|
||||
// return reviver.call(holder, key, value);
|
||||
jsval value = *vp;
|
||||
JSString *key = js_ValueToString(cx, ID_TO_VALUE(id));
|
||||
if (!key)
|
||||
return JS_FALSE;
|
||||
|
||||
jsval vec[2] = {STRING_TO_JSVAL(key), value};
|
||||
jsval reviverResult;
|
||||
if (!JS_CallFunctionValue(cx, holder, reviver, 2, vec, &reviverResult))
|
||||
return JS_FALSE;
|
||||
|
||||
*vp = reviverResult;
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Revive(JSContext *cx, jsval reviver, jsval *vp)
|
||||
{
|
||||
|
||||
JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
|
||||
jsval v = OBJECT_TO_JSVAL(obj);
|
||||
JSAutoTempValueRooter tvr(cx, 1, &v);
|
||||
if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom),
|
||||
*vp, NULL, NULL, JSPROP_ENUMERATE, NULL)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return Walk(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), obj, reviver, vp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
JSONParser *
|
||||
js_BeginJSONParse(JSContext *cx, jsval *rootVal)
|
||||
{
|
||||
@ -456,7 +562,7 @@ bad:
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_FinishJSONParse(JSContext *cx, JSONParser *jp)
|
||||
js_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver)
|
||||
{
|
||||
if (!jp)
|
||||
return JS_TRUE;
|
||||
@ -491,6 +597,11 @@ js_FinishJSONParse(JSContext *cx, JSONParser *jp)
|
||||
if (!ok)
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
|
||||
if (reviver && !JSVAL_IS_PRIMITIVE(reviver) &&
|
||||
(JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(reviver)) || js_IsCallable(cx, JSVAL_TO_OBJECT(reviver)))) {
|
||||
ok = Revive(cx, reviver, jp->rootVal);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -539,7 +650,10 @@ PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value)
|
||||
jsuint len;
|
||||
ok = js_GetLengthProperty(cx, parent, &len);
|
||||
if (ok) {
|
||||
ok = OBJ_DEFINE_PROPERTY(cx, parent, INT_TO_JSID(len), value,
|
||||
jsid index;
|
||||
if (!js_IndexToId(cx, len, &index))
|
||||
return JS_FALSE;
|
||||
ok = OBJ_DEFINE_PROPERTY(cx, parent, index, value,
|
||||
NULL, NULL, JSPROP_ENUMERATE, NULL);
|
||||
}
|
||||
} else {
|
||||
|
@ -100,7 +100,7 @@ extern JSBool
|
||||
js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len);
|
||||
|
||||
extern JSBool
|
||||
js_FinishJSONParse(JSContext *cx, JSONParser *jp);
|
||||
js_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver);
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
|
@ -52,7 +52,8 @@
|
||||
#include <limits.h>
|
||||
|
||||
#include "nanojit/nanojit.h"
|
||||
#include "jsarray.h" // higher-level library and API headers
|
||||
#include "jsapi.h" // higher-level library and API headers
|
||||
#include "jsarray.h"
|
||||
#include "jsbool.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsdbgapi.h"
|
||||
|
@ -57,6 +57,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsj_private.h" /* LiveConnect internals */
|
||||
#include "jsj_hash.h" /* Hash table with Java object as key */
|
||||
|
Loading…
Reference in New Issue
Block a user