Bug 476374 - JSON.parse does not support reviver argument as defined in spec. r=jorendorff

This commit is contained in:
Robert Sayre 2009-03-03 12:55:11 -05:00
parent 57a234ccb4
commit bc53f36141
12 changed files with 163 additions and 21 deletions

View File

@ -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;
}

View File

@ -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;

View 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);
}

View File

@ -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;

View File

@ -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);
}
/*

View File

@ -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);
/************************************************************************/

View File

@ -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;
}

View File

@ -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);

View File

@ -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 {

View File

@ -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

View File

@ -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"

View File

@ -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 */