diff --git a/dom/src/json/nsJSON.cpp b/dom/src/json/nsJSON.cpp index d060602a45b3..5302e2346429 100644 --- a/dom/src/json/nsJSON.cpp +++ b/dom/src/json/nsJSON.cpp @@ -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; } diff --git a/dom/src/json/test/unit/test_encoding_errors.js b/dom/src/json/test/unit/test_encoding_errors.js index 0cd05cb16b82..0e1b4b471de2 100644 --- a/dom/src/json/test/unit/test_encoding_errors.js +++ b/dom/src/json/test/unit/test_encoding_errors.js @@ -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; diff --git a/dom/src/json/test/unit/test_reviver.js b/dom/src/json/test/unit/test_reviver.js new file mode 100644 index 000000000000..cbdbe29e5cba --- /dev/null +++ b/dom/src/json/test/unit/test_reviver.js @@ -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); +} \ No newline at end of file diff --git a/dom/src/threads/nsDOMWorkerEvents.cpp b/dom/src/threads/nsDOMWorkerEvents.cpp index ceef7eefe0d4..00298f95becd 100644 --- a/dom/src/threads/nsDOMWorkerEvents.cpp +++ b/dom/src/threads/nsDOMWorkerEvents.cpp @@ -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; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 2098cc7653ac..8df96046125e 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -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); } /* diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 2a5bce5f473d..db85fe474746 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -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); /************************************************************************/ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 80050a0fbe1d..69e93cc82b22 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -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; } diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 4d5a4ff5d19b..293f6e096d45 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -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); diff --git a/js/src/json.cpp b/js/src/json.cpp index 15725f154b56..bd58b0649510 100644 --- a/js/src/json.cpp +++ b/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 { diff --git a/js/src/json.h b/js/src/json.h index b2fc729f0159..0eb04746e078 100644 --- a/js/src/json.h +++ b/js/src/json.h @@ -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 diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 80e2e86e390f..9d3dc330d01e 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -52,7 +52,8 @@ #include #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" diff --git a/js/src/liveconnect/jsj_JavaObject.c b/js/src/liveconnect/jsj_JavaObject.c index 51e8903157fd..27def19580cc 100644 --- a/js/src/liveconnect/jsj_JavaObject.c +++ b/js/src/liveconnect/jsj_JavaObject.c @@ -57,6 +57,7 @@ #include #include +#include "jsapi.h" #include "jsobj.h" #include "jsj_private.h" /* LiveConnect internals */ #include "jsj_hash.h" /* Hash table with Java object as key */