Merge tm to m-c.

This commit is contained in:
Robert Sayre 2009-03-03 13:48:15 -05:00
commit f4c8ebe128
28 changed files with 713 additions and 401 deletions

View File

@ -915,8 +915,10 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
// Check to see if we are running OOM
nsCOMPtr<nsIMemory> mem;
NS_GetMemoryManager(getter_AddRefs(mem));
if (!mem)
if (!mem) {
JS_ClearPendingException(cx);
return JS_FALSE;
}
PRBool lowMemory;
mem->IsLowMemory(&lowMemory);
@ -931,8 +933,10 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
mem->IsLowMemory(&lowMemory);
if (lowMemory) {
if (nsContentUtils::GetBoolPref("dom.prevent_oom_dialog", PR_FALSE))
if (nsContentUtils::GetBoolPref("dom.prevent_oom_dialog", PR_FALSE)) {
JS_ClearPendingException(cx);
return JS_FALSE;
}
nsCOMPtr<nsIPrompt> prompt = GetPromptFromContext(ctx);
@ -948,10 +952,12 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
//GetStringFromName can return NS_OK and still give NULL string
if (NS_FAILED(rv) || !title || !msg) {
NS_ERROR("Failed to get localized strings.");
JS_ClearPendingException(cx);
return JS_FALSE;
}
prompt->Alert(title, msg);
JS_ClearPendingException(cx);
return JS_FALSE;
}
}
@ -1143,6 +1149,7 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
}
}
JS_ClearPendingException(cx);
return JS_FALSE;
}

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

@ -160,6 +160,7 @@
if (!this.crockfordJSON) {
crockfordJSON = {};
crockfordJSON.window = this;
}
(function () {

View File

@ -16,4 +16,4 @@ if (!outputDir.exists()) {
do_throw(outputName + " is not a directory?")
}
var crockfordJSON = null;
do_import_script("dom/src/json/test/json2.js");
do_import_script("dom/src/json/test/json2.js");

View File

@ -0,0 +1,25 @@
function tooDeep() {
var arr = [];
var root = [arr];
var tail;
for (var i = 0; i < 5000; i++) {
tail = [];
arr.push(tail);
arr = tail;
}
JSON.stringify(root);
}
function run_test() {
do_check_eq("undefined", JSON.stringify(undefined));
do_check_eq("undefined", JSON.stringify(function(){}));
do_check_eq("undefined", JSON.stringify(<x><y></y></x>));
var ok = false;
try {
tooDeep();
} catch (e) {
do_check_true(e instanceof Error);
ok = true;
}
do_check_true(ok);
}

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

@ -0,0 +1,28 @@
function checkForSyntaxErrors(s) {
var crockfordErrored = false;
try {
crockfordJSON.parse(s)
} catch (e) {
var crockfordErrored = true;
do_check_true(e instanceof crockfordJSON.window.SyntaxError);
}
do_check_true(crockfordErrored);
var JSONErrored = false;
try {
JSON.parse(s)
} catch (e) {
var JSONErrored = true;
do_check_true(e instanceof SyntaxError);
}
do_check_true(JSONErrored);
}
function run_test() {
checkForSyntaxErrors("{}...");
checkForSyntaxErrors('{"foo": truBBBB}');
checkForSyntaxErrors('{foo: truBBBB}');
checkForSyntaxErrors('{"foo": undefined}');
checkForSyntaxErrors('{"foo": ]');
checkForSyntaxErrors('{"foo');
}

View File

@ -407,7 +407,7 @@ DOMWorkerOperationCallback(JSContext* aCx)
PRBool extraThreadAllowed = PR_FALSE;
jsrefcount suspendDepth = 0;
do {
for (;;) {
// Kill execution if we're canceled.
if (worker->IsCanceled()) {
LOG(("Forcefully killing JS for worker [0x%p]",
@ -420,7 +420,8 @@ DOMWorkerOperationCallback(JSContext* aCx)
JS_ResumeRequest(aCx, suspendDepth);
}
// Kill exectuion of the currently running JS.
// Kill execution of the currently running JS.
JS_ClearPendingException(aCx);
return JS_FALSE;
}
@ -440,6 +441,7 @@ DOMWorkerOperationCallback(JSContext* aCx)
// the worker was canceled since we checked above.
if (worker->IsCanceled()) {
NS_WARNING("Tried to suspend on a pool that has gone away");
JS_ClearPendingException(aCx);
return JS_FALSE;
}
@ -461,10 +463,7 @@ DOMWorkerOperationCallback(JSContext* aCx)
nsAutoMonitor mon(pool->Monitor());
mon.Wait();
} while (1);
NS_NOTREACHED("Shouldn't get here!");
return JS_FALSE;
}
}
void

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

@ -307,3 +307,8 @@ MSG_DEF(JSMSG_BAD_OBJECT_INIT, 224, 0, JSEXN_SYNTAXERR, "invalid object i
MSG_DEF(JSMSG_CANT_SET_ARRAY_ATTRS, 225, 0, JSEXN_INTERNALERR, "can't set attributes on indexed array properties")
MSG_DEF(JSMSG_EVAL_ARITY, 226, 0, JSEXN_TYPEERR, "eval accepts only one parameter")
MSG_DEF(JSMSG_MISSING_FUN_ARG, 227, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
MSG_DEF(JSMSG_JSON_BAD_PARSE, 228, 0, JSEXN_SYNTAXERR, "JSON.parse")
MSG_DEF(JSMSG_JSON_BAD_STRINGIFY, 229, 0, JSEXN_ERR, "JSON.stringify")

View File

@ -4571,8 +4571,10 @@ js_generic_fast_native_method_dispatcher(JSContext *cx, uintN argc, jsval *vp)
* it as if the static was called with one parameter, the explicit |this|
* object.
*/
if (argc != 0)
--argc;
if (argc != 0) {
/* Clear the last parameter in case too few arguments were passed. */
vp[2 + --argc] = JSVAL_VOID;
}
native =
#ifdef JS_TRACER
@ -4636,8 +4638,10 @@ js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj,
* it as if the static was called with one parameter, the explicit |this|
* object.
*/
if (argc != 0)
--argc;
if (argc != 0) {
/* Clear the last parameter in case too few arguments were passed. */
argv[--argc] = JSVAL_VOID;
}
return fs->call(cx, JSVAL_TO_OBJECT(argv[-1]), argc, argv, rval);
}
@ -5684,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

@ -401,7 +401,10 @@ JS_DECLARE_CALLINFO(js_NumberToString)
/* Defined in jsstr.cpp */
JS_DECLARE_CALLINFO(js_ConcatStrings)
JS_DECLARE_CALLINFO(js_String_getelem)
JS_DECLARE_CALLINFO(js_String_p_charCodeAt0)
JS_DECLARE_CALLINFO(js_String_p_charCodeAt0_int)
JS_DECLARE_CALLINFO(js_String_p_charCodeAt)
JS_DECLARE_CALLINFO(js_String_p_charCodeAt_int)
JS_DECLARE_CALLINFO(js_EqualStrings)
JS_DECLARE_CALLINFO(js_CompareStrings)

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

@ -3205,6 +3205,7 @@ ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr)
/* Finally, do the deed. */
STOBJ_SET_SLOT(obj, slot, OBJECT_TO_JSVAL(pobj));
STOBJ_SET_DELEGATE(pobj);
}
static void

View File

@ -2876,7 +2876,6 @@ js_Interpret(JSContext *cx)
ADD_EMPTY_CASE(JSOP_NOP)
ADD_EMPTY_CASE(JSOP_CONDSWITCH)
ADD_EMPTY_CASE(JSOP_TRY)
ADD_EMPTY_CASE(JSOP_FINALLY)
#if JS_HAS_XML_SUPPORT
ADD_EMPTY_CASE(JSOP_STARTXML)
ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
@ -2947,7 +2946,6 @@ js_Interpret(JSContext *cx)
END_CASE(JSOP_LEAVEWITH)
BEGIN_CASE(JSOP_RETURN)
CHECK_BRANCH();
fp->rval = POP_OPND();
/* FALL THROUGH */
@ -2958,6 +2956,7 @@ js_Interpret(JSContext *cx)
* will be false after the inline_return label.
*/
ASSERT_NOT_THROWING(cx);
CHECK_BRANCH();
if (fp->imacpc) {
/*
@ -6385,8 +6384,13 @@ js_Interpret(JSContext *cx)
JS_ASSERT(cx->throwing);
PUSH(cx->exception);
cx->throwing = JS_FALSE;
CHECK_BRANCH();
END_CASE(JSOP_EXCEPTION)
BEGIN_CASE(JSOP_FINALLY)
CHECK_BRANCH();
END_CASE(JSOP_FINALLY)
BEGIN_CASE(JSOP_THROWING)
JS_ASSERT(!cx->throwing);
cx->throwing = JS_TRUE;
@ -6395,6 +6399,7 @@ js_Interpret(JSContext *cx)
BEGIN_CASE(JSOP_THROW)
JS_ASSERT(!cx->throwing);
CHECK_BRANCH();
cx->throwing = JS_TRUE;
cx->exception = POP_OPND();
/* let the code at error try to catch the exception. */

View File

@ -3334,6 +3334,13 @@ PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id)
if (!OBJ_IS_DELEGATE(cx, obj))
return;
/*
* All scope chains end in a global object, so this will change the global
* shape. jstracer.cpp assumes that the global shape never changes on
* trace, so we must deep-bail here.
*/
js_LeaveTrace(cx);
PurgeProtoChain(cx, OBJ_GET_PROTO(cx, obj), id);
while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) {
if (PurgeProtoChain(cx, obj, id))

View File

@ -128,7 +128,30 @@ struct JSObjectMap {
*/
struct JSObject {
JSObjectMap *map;
/*
* Stores the JSClass* for this object, with the two lowest bits encoding
* whether this object is a delegate or a system object.
*
* A delegate is an object linked on another object's prototype
* (JSSLOT_PROTO) or scope (JSSLOT_PARENT) chain, which might be implicitly
* asked to get or set a property on behalf of another object. Delegates
* may be accessed directly too, as might any object, but only those
* objects linked after the head of a prototype or scope chain are
* delegates. This definition helps to optimize shape-based property cache
* purging (see Purge{Scope,Proto}Chain in jsobj.cpp).
*
* The meaning of the system object bit is defined by the API client. It is
* set in JS_NewSystemObject and is queried by JS_IsSystemObject, but it
* has no intrinsic meaning to SpiderMonkey. Further, JSFILENAME_SYSTEM and
* JS_FlagScriptFilenamePrefix are intended to be complementary to this
* bit, but it is up to the API client to implement any such association.
*
* Both bits are initially zero and may be set or queried using the
* STOBJ_(IS|SET)_(DELEGATE|SYSTEM) macros.
*/
jsuword classword;
jsval fslots[JS_INITIAL_NSLOTS];
jsval *dslots; /* dynamically allocated slots */
};
@ -415,7 +438,9 @@ js_InitEval(JSContext *cx, JSObject *obj);
extern JSObject *
js_InitObjectClass(JSContext *cx, JSObject *obj);
/* Select Object.prototype method names shared between jsapi.c and jsobj.c. */
/*
* Select Object.prototype method names shared between jsapi.cpp and jsobj.cpp.
*/
extern const char js_watch_str[];
extern const char js_unwatch_str[];
extern const char js_hasOwnProperty_str[];
@ -730,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,21 +68,19 @@ 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);
}
if (!ok)
JS_ReportError(cx, "Error parsing JSON");
return ok;
}
@ -123,32 +121,23 @@ JSBool
js_json_stringify(JSContext *cx, uintN argc, jsval *vp)
{
jsval *argv = vp + 2;
JSBool ok = JS_TRUE;
// Must throw an Error if there isn't a first arg
if (!JS_ConvertArguments(cx, argc, argv, "v", vp))
return JS_FALSE;
ok = js_TryJSON(cx, vp);
JSType type;
if (!ok || *vp == JSVAL_VOID ||
((type = JS_TypeOfValue(cx, *vp)) == JSTYPE_FUNCTION || type == JSTYPE_XML)) {
JS_ReportError(cx, "Invalid argument");
if (!js_TryJSON(cx, vp))
return JS_FALSE;
}
JSString *s = JS_NewStringCopyN(cx, "", 0);
if (!s)
ok = JS_FALSE;
return JS_FALSE;
if (ok) {
jsval vec[2] = {STRING_TO_JSVAL(s), JSVAL_VOID};
StringifyClosure sc(cx, 2, vec);
JSAutoTempValueRooter resultTvr(cx, 1, sc.s);
ok = js_Stringify(cx, vp, NULL, &WriteCallback, &sc, 0);
*vp = *sc.s;
}
jsval vec[2] = {STRING_TO_JSVAL(s), JSVAL_VOID};
StringifyClosure sc(cx, 2, vec);
JSAutoTempValueRooter resultTvr(cx, 1, sc.s);
JSBool ok = js_Stringify(cx, vp, NULL, &WriteCallback, &sc, 0);
*vp = *sc.s;
return ok;
}
@ -158,12 +147,12 @@ js_TryJSON(JSContext *cx, jsval *vp)
{
// Checks whether the return value implements toJSON()
JSBool ok = JS_TRUE;
if (!JSVAL_IS_PRIMITIVE(*vp)) {
JSObject *obj = JSVAL_TO_OBJECT(*vp);
ok = js_TryMethod(cx, obj, cx->runtime->atomState.toJSONAtom, 0, NULL, vp);
}
return ok;
}
@ -213,9 +202,9 @@ write_string(JSContext *cx, JSONWriteCallback callback, void *data, const jschar
}
static JSBool
stringify_primitive(JSContext *cx, jsval *vp,
JSONWriteCallback callback, void *data, JSType type) {
stringify_leaf(JSContext *cx, jsval *vp,
JSONWriteCallback callback, void *data, JSType type)
{
JSString *outputString;
JSString *s = js_ValueToString(cx, *vp);
@ -239,9 +228,11 @@ stringify_primitive(JSContext *cx, jsval *vp,
outputString = s;
} else if (JSVAL_IS_NULL(*vp)) {
outputString = JS_NewStringCopyN(cx, "null", 4);
} else if (JSVAL_IS_VOID(*vp) || type == JSTYPE_FUNCTION || type == JSTYPE_XML) {
outputString = JS_NewStringCopyN(cx, "undefined", 9);
} else {
JS_NOT_REACHED("A type we don't know about");
return JS_FALSE; // encoding error
outputString = JS_NewStringCopyN(cx, "undefined", 9);
}
if (!outputString)
@ -254,8 +245,10 @@ static JSBool
stringify(JSContext *cx, jsval *vp, JSObject *replacer,
JSONWriteCallback callback, void *data, uint32 depth)
{
if (depth > JSON_MAX_DEPTH)
if (depth > JSON_MAX_DEPTH) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_STRINGIFY);
return JS_FALSE; /* encoding error */
}
JSBool ok = JS_TRUE;
JSObject *obj = JSVAL_TO_OBJECT(*vp);
@ -288,7 +281,12 @@ 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++;
} else {
ok = js_CallIteratorNext(cx, iterObj, &key);
@ -316,16 +314,15 @@ stringify(JSContext *cx, jsval *vp, JSObject *replacer,
!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) {
ok = JS_FALSE;
break;
} else if (v == JSVAL_TRUE) {
ok = JS_GetUCProperty(cx, obj, JS_GetStringChars(ks),
JS_GetStringLength(ks), &outputValue);
} else {
continue;
}
}
if (!ok)
break;
if (v != JSVAL_TRUE)
continue;
ok = JS_GetPropertyById(cx, obj, id, &outputValue);
if (!ok)
break;
}
// if this is an array, holes are transmitted as null
if (isArray && outputValue == JSVAL_VOID) {
@ -346,8 +343,8 @@ stringify(JSContext *cx, jsval *vp, JSObject *replacer,
if (memberWritten) {
output = jschar(',');
ok = callback(&output, 1, data);
if (!ok)
break;
if (!ok)
break;
}
memberWritten = JS_TRUE;
@ -377,18 +374,18 @@ stringify(JSContext *cx, jsval *vp, JSObject *replacer,
// recurse
ok = stringify(cx, &outputValue, replacer, callback, data, depth + 1);
} else {
ok = stringify_primitive(cx, &outputValue, callback, data, type);
ok = stringify_leaf(cx, &outputValue, callback, data, type);
}
} while (ok);
if (iterObj) {
// Always close the iterator, but make sure not to stomp on OK
ok &= js_CloseIterator(cx, *vp);
// encoding error or propagate? FIXME: Bug 408838.
}
if (!ok)
return JS_FALSE;
output = jschar(isArray ? ']' : '}');
ok = callback(&output, 1, data);
@ -400,17 +397,15 @@ JSBool
js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer,
JSONWriteCallback callback, void *data, uint32 depth)
{
JSBool ok = JS_TRUE;
JSBool ok;
JSType type = JS_TypeOfValue(cx, *vp);
if (JSVAL_IS_PRIMITIVE(*vp)) {
ok = stringify_primitive(cx, vp, callback, data, JS_TypeOfValue(cx, *vp));
if (JSVAL_IS_PRIMITIVE(*vp) || type == JSTYPE_FUNCTION || type == JSTYPE_XML) {
ok = stringify_leaf(cx, vp, callback, data, type);
} else {
ok = stringify(cx, vp, replacer, callback, data, depth);
}
if (!ok)
JS_ReportError(cx, "Error during JSON encoding");
return ok;
}
@ -421,7 +416,108 @@ static JSBool IsNumChar(jschar c)
}
static JSBool HandleData(JSContext *cx, JSONParser *jp, JSONDataType type);
static JSBool PopState(JSONParser *jp);
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)
@ -466,7 +562,7 @@ bad:
}
JSBool
js_FinishJSONParse(JSContext *cx, JSONParser *jp)
js_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver)
{
if (!jp)
return JS_TRUE;
@ -476,11 +572,11 @@ js_FinishJSONParse(JSContext *cx, JSONParser *jp)
if ((jp->statep - jp->stateStack) == 1) {
if (*jp->statep == JSON_PARSE_STATE_KEYWORD) {
if (HandleData(cx, jp, JSON_DATA_KEYWORD)) {
PopState(jp);
PopState(cx, jp);
}
} else if (*jp->statep == JSON_PARSE_STATE_NUMBER) {
if (HandleData(cx, jp, JSON_DATA_NUMBER)) {
PopState(jp);
PopState(cx, jp);
}
}
}
@ -498,18 +594,32 @@ js_FinishJSONParse(JSContext *cx, JSONParser *jp)
JSBool ok = *jp->statep == JSON_PARSE_STATE_FINISHED;
JS_free(cx, 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;
}
static JSBool
PushState(JSONParser *jp, JSONParserState state)
PushState(JSContext *cx, JSONParser *jp, JSONParserState state)
{
if (*jp->statep == JSON_PARSE_STATE_FINISHED)
return JS_FALSE; // extra input
if (*jp->statep == JSON_PARSE_STATE_FINISHED) {
// extra input
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
jp->statep++;
if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack))
return JS_FALSE; // too deep
if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack)) {
// too deep
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
*jp->statep = state;
@ -517,11 +627,12 @@ PushState(JSONParser *jp, JSONParserState state)
}
static JSBool
PopState(JSONParser *jp)
PopState(JSContext *cx, JSONParser *jp)
{
jp->statep--;
if (jp->statep < jp->stateStack) {
jp->statep = jp->stateStack;
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
@ -534,14 +645,15 @@ PopState(JSONParser *jp)
static JSBool
PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value)
{
JSAutoTempValueRooter tvr(cx, 1, &value);
JSBool ok;
if (OBJ_IS_ARRAY(cx, parent)) {
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 {
@ -561,8 +673,10 @@ 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 JS_FALSE; // decoding error
if (len >= JSON_MAX_DEPTH) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
jsval v = OBJECT_TO_JSVAL(obj);
JSAutoTempValueRooter tvr(cx, v);
@ -584,7 +698,7 @@ PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
JS_ASSERT(JSVAL_IS_OBJECT(p));
JSObject *parent = JSVAL_TO_OBJECT(p);
if (!PushValue(cx, jp, parent, OBJECT_TO_JSVAL(obj)))
if (!PushValue(cx, jp, parent, v))
return JS_FALSE;
// This property must be enumerable to keep the array dense
@ -596,24 +710,6 @@ PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
return JS_TRUE;
}
static JSObject *
GetTopOfObjectStack(JSContext *cx, JSONParser *jp)
{
jsuint length;
if (!js_GetLengthProperty(cx, jp->objectStack, &length))
return NULL;
if (length == 0)
return NULL;
jsval o;
if (!OBJ_GET_PROPERTY(cx, jp->objectStack, INT_TO_JSID(length - 1), &o))
return NULL;
JS_ASSERT(!JSVAL_IS_PRIMITIVE(o));
return JSVAL_TO_OBJECT(o);
}
static JSBool
OpenObject(JSContext *cx, JSONParser *jp)
{
@ -653,42 +749,57 @@ CloseArray(JSContext *cx, JSONParser *jp)
return CloseObject(cx, jp);
}
static JSBool
PushPrimitive(JSContext *cx, JSONParser *jp, jsval value)
{
JSAutoTempValueRooter tvr(cx, 1, &value);
jsuint len;
if (!js_GetLengthProperty(cx, jp->objectStack, &len))
return JS_FALSE;
if (len > 0) {
jsval o;
if (!OBJ_GET_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len - 1), &o))
return JS_FALSE;
JS_ASSERT(!JSVAL_IS_PRIMITIVE(o));
return PushValue(cx, jp, JSVAL_TO_OBJECT(o), 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) || ep != buf + len)
if (!js_strtod(cx, buf, buf + len, &ep, &val))
return JS_FALSE;
if (ep != buf + len) {
// bad number input
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
jsval numVal;
JSObject *obj = GetTopOfObjectStack(cx, jp);
jsval numVal;
if (!JS_NewNumberValue(cx, val, &numVal))
return JS_FALSE;
if (obj)
return PushValue(cx, jp, obj, numVal);
// nothing on the object stack, so number value as root
*jp->rootVal = numVal;
return JS_TRUE;
return PushPrimitive(cx, jp, numVal);
}
static JSBool
HandleString(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
{
JSObject *obj = GetTopOfObjectStack(cx, jp);
JSString *str = js_NewStringCopyN(cx, buf, len);
if (!str)
return JS_FALSE;
if (obj)
return PushValue(cx, jp, obj, STRING_TO_JSVAL(str));
// root value must be primitive
*jp->rootVal = STRING_TO_JSVAL(str);
return JS_TRUE;
return PushPrimitive(cx, jp, STRING_TO_JSVAL(str));
}
static JSBool
@ -696,25 +807,24 @@ HandleKeyword(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
{
jsval keyword;
JSTokenType tt = js_CheckKeyword(buf, len);
if (tt != TOK_PRIMARY)
if (tt != TOK_PRIMARY) {
// bad keyword
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
if (buf[0] == 'n')
if (buf[0] == 'n') {
keyword = JSVAL_NULL;
else if (buf[0] == 't')
} else if (buf[0] == 't') {
keyword = JSVAL_TRUE;
else if (buf[0] == 'f')
} else if (buf[0] == 'f') {
keyword = JSVAL_FALSE;
else
} else {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
JSObject *obj = GetTopOfObjectStack(cx, jp);
if (obj)
return PushValue(cx, jp, obj, keyword);
// root value must be primitive
*jp->rootVal = keyword;
return JS_TRUE;
return PushPrimitive(cx, jp, keyword);
}
static JSBool
@ -722,8 +832,11 @@ HandleData(JSContext *cx, JSONParser *jp, JSONDataType type)
{
JSBool ok = JS_FALSE;
if (!STRING_BUFFER_OK(jp->buffer))
if (!STRING_BUFFER_OK(jp->buffer)) {
// what to call this error?
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
switch (type) {
case JSON_DATA_STRING:
@ -759,206 +872,222 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
uint32 i;
if (*jp->statep == JSON_PARSE_STATE_INIT) {
PushState(jp, JSON_PARSE_STATE_VALUE);
PushState(cx, jp, JSON_PARSE_STATE_VALUE);
}
for (i = 0; i < len; i++) {
jschar c = data[i];
switch (*jp->statep) {
case JSON_PARSE_STATE_VALUE :
if (c == ']') {
// empty array
if (!PopState(jp))
return JS_FALSE;
if (*jp->statep != JSON_PARSE_STATE_ARRAY)
return JS_FALSE; // unexpected char
if (!CloseArray(cx, jp) || !PopState(jp))
return JS_FALSE;
break;
case JSON_PARSE_STATE_VALUE:
if (c == ']') {
// empty array
if (!PopState(cx, jp))
return JS_FALSE;
if (*jp->statep != JSON_PARSE_STATE_ARRAY) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
if (c == '}') {
// we should only find these in OBJECT_KEY state
return JS_FALSE; // unexpected failure
}
if (!CloseArray(cx, jp) || !PopState(cx, jp))
return JS_FALSE;
if (c == '"') {
*jp->statep = JSON_PARSE_STATE_STRING;
break;
}
if (IsNumChar(c)) {
*jp->statep = JSON_PARSE_STATE_NUMBER;
js_AppendChar(jp->buffer, c);
break;
}
if (JS7_ISLET(c)) {
*jp->statep = JSON_PARSE_STATE_KEYWORD;
js_AppendChar(jp->buffer, c);
break;
}
// fall through in case the value is an object or array
case JSON_PARSE_STATE_OBJECT_VALUE :
if (c == '{') {
*jp->statep = JSON_PARSE_STATE_OBJECT;
if (!OpenObject(cx, jp) || !PushState(jp, JSON_PARSE_STATE_OBJECT_PAIR))
return JS_FALSE;
} else if (c == '[') {
*jp->statep = JSON_PARSE_STATE_ARRAY;
if (!OpenArray(cx, jp) || !PushState(jp, JSON_PARSE_STATE_VALUE))
return JS_FALSE;
} else if (!JS_ISXMLSPACE(c)) {
return JS_FALSE; // unexpected
}
break;
}
case JSON_PARSE_STATE_OBJECT :
if (c == '}') {
if (!CloseObject(cx, jp) || !PopState(jp))
return JS_FALSE;
} else if (c == ',') {
if (!PushState(jp, JSON_PARSE_STATE_OBJECT_PAIR))
return JS_FALSE;
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
return JS_FALSE; // unexpected
}
break;
if (c == '}') {
// we should only find these in OBJECT_KEY state
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
case JSON_PARSE_STATE_ARRAY :
if (c == ']') {
if (!CloseArray(cx, jp) || !PopState(jp))
return JS_FALSE;
} else if (c == ',') {
if (!PushState(jp, JSON_PARSE_STATE_VALUE))
return JS_FALSE;
} else if (!JS_ISXMLSPACE(c)) {
return JS_FALSE; // unexpected
}
break;
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(jp, JSON_PARSE_STATE_STRING))
return JS_FALSE;
} else if (c == '}') {
// pop off the object pair state and the object state
if (!CloseObject(cx, jp) || !PopState(jp) || !PopState(jp))
return JS_FALSE;
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
return JS_FALSE; // unexpected
}
break;
case JSON_PARSE_STATE_OBJECT_IN_PAIR:
if (c == ':') {
*jp->statep = JSON_PARSE_STATE_VALUE;
} else if (!JS_ISXMLSPACE(c)) {
return JS_FALSE; // unexpected
}
break;
case JSON_PARSE_STATE_STRING:
if (c == '"') {
if (!PopState(jp))
return JS_FALSE;
JSONDataType jdt;
if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) {
jdt = JSON_DATA_KEYSTRING;
} else {
jdt = JSON_DATA_STRING;
}
if (!HandleData(cx, jp, jdt))
return JS_FALSE;
} else if (c == '\\') {
*jp->statep = JSON_PARSE_STATE_STRING_ESCAPE;
} else {
js_AppendChar(jp->buffer, c);
}
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 JS_FALSE; // unexpected
}
}
js_AppendChar(jp->buffer, c);
if (c == '"') {
*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 JS_FALSE; // unexpected
if (IsNumChar(c)) {
*jp->statep = JSON_PARSE_STATE_NUMBER;
js_AppendChar(jp->buffer, c);
break;
}
if (++(jp->numHex) == 4) {
js_AppendChar(jp->buffer, jp->hexChar);
jp->hexChar = 0;
if (JS7_ISLET(c)) {
*jp->statep = JSON_PARSE_STATE_KEYWORD;
js_AppendChar(jp->buffer, c);
break;
}
// fall through in case the value is an object or array
case JSON_PARSE_STATE_OBJECT_VALUE:
if (c == '{') {
*jp->statep = JSON_PARSE_STATE_OBJECT;
if (!OpenObject(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR))
return JS_FALSE;
} else if (c == '[') {
*jp->statep = JSON_PARSE_STATE_ARRAY;
if (!OpenArray(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_VALUE))
return JS_FALSE;
} else if (!JS_ISXMLSPACE(c)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
break;
case JSON_PARSE_STATE_OBJECT:
if (c == '}') {
if (!CloseObject(cx, jp) || !PopState(cx, jp))
return JS_FALSE;
} else if (c == ',') {
if (!PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR))
return JS_FALSE;
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
break;
case JSON_PARSE_STATE_ARRAY :
if (c == ']') {
if (!CloseArray(cx, jp) || !PopState(cx, jp))
return JS_FALSE;
} else if (c == ',') {
if (!PushState(cx, jp, JSON_PARSE_STATE_VALUE))
return JS_FALSE;
} else if (!JS_ISXMLSPACE(c)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
break;
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 (c == '}') {
// pop off the object pair state and the object state
if (!CloseObject(cx, jp) || !PopState(cx, jp) || !PopState(cx, jp))
return JS_FALSE;
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
break;
case JSON_PARSE_STATE_OBJECT_IN_PAIR:
if (c == ':') {
*jp->statep = JSON_PARSE_STATE_VALUE;
} else if (!JS_ISXMLSPACE(c)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
break;
case JSON_PARSE_STATE_STRING:
if (c == '"') {
if (!PopState(cx, jp))
return JS_FALSE;
JSONDataType jdt;
if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) {
jdt = JSON_DATA_KEYSTRING;
} else {
jdt = JSON_DATA_STRING;
}
if (!HandleData(cx, jp, jdt))
return JS_FALSE;
} else if (c == '\\') {
*jp->statep = JSON_PARSE_STATE_STRING_ESCAPE;
} else {
js_AppendChar(jp->buffer, c);
}
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->statep = JSON_PARSE_STATE_STRING;
}
break;
case JSON_PARSE_STATE_KEYWORD:
if (JS7_ISLET(c)) {
js_AppendChar(jp->buffer, c);
jp->hexChar = 0;
*jp->statep = JSON_PARSE_STATE_STRING_HEX;
continue;
} else {
// this character isn't part of the keyword, process it again
i--;
if (!PopState(jp))
return JS_FALSE;
if (!HandleData(cx, jp, JSON_DATA_KEYWORD))
return JS_FALSE;
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
break;
}
case JSON_PARSE_STATE_NUMBER:
if (IsNumChar(c)) {
js_AppendChar(jp->buffer, c);
} else {
// this character isn't part of the number, process it again
i--;
if (!PopState(jp))
return JS_FALSE;
if (!HandleData(cx, jp, JSON_DATA_NUMBER))
return JS_FALSE;
}
break;
js_AppendChar(jp->buffer, c);
*jp->statep = JSON_PARSE_STATE_STRING;
break;
case JSON_PARSE_STATE_FINISHED:
if (!JS_ISXMLSPACE(c))
return JS_FALSE; // extra input
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 {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
if (++(jp->numHex) == 4) {
js_AppendChar(jp->buffer, jp->hexChar);
jp->hexChar = 0;
jp->numHex = 0;
*jp->statep = JSON_PARSE_STATE_STRING;
}
break;
break;
case JSON_PARSE_STATE_KEYWORD:
if (JS7_ISLET(c)) {
js_AppendChar(jp->buffer, c);
} else {
// this character isn't part of the keyword, process it again
i--;
if (!PopState(cx, jp))
return JS_FALSE;
if (!HandleData(cx, jp, JSON_DATA_KEYWORD))
return JS_FALSE;
}
break;
default:
JS_NOT_REACHED("Invalid JSON parser state");
case JSON_PARSE_STATE_NUMBER:
if (IsNumChar(c)) {
js_AppendChar(jp->buffer, c);
} else {
// this character isn't part of the number, process it again
i--;
if (!PopState(cx, jp))
return JS_FALSE;
if (!HandleData(cx, jp, JSON_DATA_NUMBER))
return JS_FALSE;
}
break;
case JSON_PARSE_STATE_FINISHED:
if (!JS_ISXMLSPACE(c)) {
// extra input
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
return JS_FALSE;
}
break;
default:
JS_NOT_REACHED("Invalid JSON parser state");
}
}

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

@ -241,6 +241,12 @@ js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
return JS_TRUE;
}
JSBool
js_DumpScript(JSContext *cx, JSScript *script)
{
return js_Disassemble(cx, script, true, stdout);
}
const char *
ToDisassemblySource(JSContext *cx, jsval v)
{

View File

@ -2042,7 +2042,6 @@ class RegExpNativeCompiler {
JSRegExp* re;
CompilerState* cs; /* RegExp to compile */
Fragment* fragment;
LirBuffer* lirbuf;
LirWriter* lir;
LirBufWriter* lirBufWriter; /* for skip */
@ -2050,7 +2049,7 @@ class RegExpNativeCompiler {
LIns* gdata;
LIns* cpend;
JSBool isCaseInsensitive() const { return cs->flags & JSREG_FOLD; }
JSBool isCaseInsensitive() const { return (cs->flags & JSREG_FOLD) != 0; }
JSBool targetCurrentPoint(LIns* ins)
{

View File

@ -49,3 +49,6 @@ MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY, 4, 0, JSEXN_NONE, "only works on JS scr
MSG_DEF(JSSMSG_UNEXPECTED_EOF, 5, 1, JSEXN_NONE, "unexpected EOF in {0}")
MSG_DEF(JSSMSG_DOEXP_USAGE, 6, 0, JSEXN_NONE, "usage: doexp obj id")
MSG_DEF(JSSMSG_SCRIPTS_ONLY, 7, 0, JSEXN_NONE, "only works on scripts")
MSG_DEF(JSSMSG_NOT_ENOUGH_ARGS, 8, 1, JSEXN_NONE, "{0}: not enough arguments")
MSG_DEF(JSSMSG_TOO_MANY_ARGS, 9, 1, JSEXN_NONE, "{0}: too many arguments")
MSG_DEF(JSSMSG_ASSERT_EQ_FAILED, 10, 2, JSEXN_NONE, "Assertion failed: got {0}, expected {1}")

View File

@ -1035,13 +1035,47 @@ out_of_range:
}
#ifdef JS_TRACER
extern jsdouble js_NaN;
jsdouble FASTCALL
js_String_p_charCodeAt(JSString* str, jsdouble d)
{
d = js_DoubleToInteger(d);
if (d < 0 || (int32)JSSTRING_LENGTH(str) <= d)
return js_NaN;
return jsdouble(JSSTRING_CHARS(str)[jsuint(d)]);
}
int32 FASTCALL
js_String_p_charCodeAt(JSString* str, int32 i)
js_String_p_charCodeAt_int(JSString* str, jsint i)
{
if (i < 0 || (int32)JSSTRING_LENGTH(str) <= i)
return -1;
return 0;
return JSSTRING_CHARS(str)[i];
}
jsdouble FASTCALL
js_String_p_charCodeAt0(JSString* str)
{
if ((int32)JSSTRING_LENGTH(str) == 0)
return js_NaN;
return jsdouble(JSSTRING_CHARS(str)[0]);
}
int32 FASTCALL
js_String_p_charCodeAt0_int(JSString* str)
{
if ((int32)JSSTRING_LENGTH(str) == 0)
return 0;
return JSSTRING_CHARS(str)[0];
}
/*
* The FuncFilter replaces the generic double version of charCodeAt with the
* integer fast path if appropriate.
*/
JS_DEFINE_CALLINFO_1(extern, INT32, js_String_p_charCodeAt0_int, STRING, 1, 1)
JS_DEFINE_CALLINFO_2(extern, INT32, js_String_p_charCodeAt_int, STRING, INT32, 1, 1)
#endif
jsint
@ -2506,8 +2540,9 @@ JS_DEFINE_TRCINFO_2(str_substring,
(3, (static, STRING_RETRY, String_p_substring_1, CONTEXT, THIS_STRING, INT32, 1, 1)))
JS_DEFINE_TRCINFO_1(str_charAt,
(3, (extern, STRING_RETRY, js_String_getelem, CONTEXT, THIS_STRING, INT32, 1, 1)))
JS_DEFINE_TRCINFO_1(str_charCodeAt,
(2, (extern, INT32_RETRY, js_String_p_charCodeAt, THIS_STRING, INT32, 1, 1)))
JS_DEFINE_TRCINFO_2(str_charCodeAt,
(1, (extern, DOUBLE, js_String_p_charCodeAt0, THIS_STRING, 1, 1)),
(2, (extern, DOUBLE, js_String_p_charCodeAt, THIS_STRING, DOUBLE, 1, 1)))
JS_DEFINE_TRCINFO_4(str_concat,
(3, (static, STRING_RETRY, String_p_concat_1int, CONTEXT, THIS_STRING, INT32, 1, 1)),
(3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING, 1, 1)),

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"
@ -905,19 +906,33 @@ public:
if (isi2f(s0) || isu2f(s0))
return iu2fArg(s0);
// XXX ARM -- check for qjoin(call(UnboxDouble),call(UnboxDouble))
if (s0->isCall() && s0->callInfo() == &js_UnboxDouble_ci) {
LIns* args2[] = { callArgN(s0, 0) };
return out->insCall(&js_UnboxInt32_ci, args2);
}
if (s0->isCall() && s0->callInfo() == &js_StringToNumber_ci) {
// callArgN's ordering is that as seen by the builtin, not as stored in args here.
// True story!
LIns* args2[] = { callArgN(s0, 1), callArgN(s0, 0) };
return out->insCall(&js_StringToInt32_ci, args2);
if (s0->isCall()) {
const CallInfo* ci2 = s0->callInfo();
if (ci2 == &js_UnboxDouble_ci) {
LIns* args2[] = { callArgN(s0, 0) };
return out->insCall(&js_UnboxInt32_ci, args2);
} else if (ci2 == &js_StringToNumber_ci) {
// callArgN's ordering is that as seen by the builtin, not as stored in
// args here. True story!
LIns* args2[] = { callArgN(s0, 1), callArgN(s0, 0) };
return out->insCall(&js_StringToInt32_ci, args2);
} else if (ci2 == &js_String_p_charCodeAt0_ci) {
// Use a fast path builtin for a charCodeAt that converts to an int right away.
LIns* args2[] = { callArgN(s0, 0) };
return out->insCall(&js_String_p_charCodeAt0_int_ci, args2);
} else if (ci2 == &js_String_p_charCodeAt_ci) {
LIns* idx = callArgN(s0, 1);
// If the index is not already an integer, force it to be an integer.
idx = isPromote(idx)
? demote(out, idx)
: out->insCall(&js_DoubleToInt32_ci, &idx);
LIns* args2[] = { idx, callArgN(s0, 0) };
return out->insCall(&js_String_p_charCodeAt_int_ci, args2);
}
}
} else if (ci == &js_BoxDouble_ci) {
JS_ASSERT(s0->isQuad());
if (s0->isop(LIR_i2f)) {
if (isi2f(s0)) {
LIns* args2[] = { s0->oprnd1(), args[1] };
return out->insCall(&js_BoxInt32_ci, args2);
}
@ -6742,27 +6757,6 @@ TraceRecorder::functionCall(bool constructing, uintN argc)
}
argp--;
}
/*
* If we got this far, and we have a charCodeAt, check that charCodeAt
* isn't going to return a NaN.
*/
if (!constructing && known->builtin == &js_String_p_charCodeAt_ci) {
JSString* str = JSVAL_TO_STRING(tval);
jsval& arg = stackval(-1);
JS_ASSERT(JSVAL_IS_STRING(tval));
JS_ASSERT(isNumber(arg));
if (JSVAL_IS_INT(arg)) {
if (size_t(JSVAL_TO_INT(arg)) >= JSSTRING_LENGTH(str))
ABORT_TRACE("invalid charCodeAt index");
} else {
double d = js_DoubleToInteger(*JSVAL_TO_DOUBLE(arg));
if (d < 0 || JSSTRING_LENGTH(str) <= d)
ABORT_TRACE("invalid charCodeAt index");
}
}
goto success;
next_specialization:;
@ -7019,30 +7013,6 @@ TraceRecorder::record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop
ABORT_TRACE("lazy import of global slot failed");
LIns* r_ins = get(&r);
/*
* Scope shape is determined by the ordered list of property names and
* other attributes (flags, getter/setter, etc.) but not value or type
* -- except for function-valued properties. This function-based
* distinction enables various function-call optimizations.
*
* This shape requirement is addressed in the interpreter by branding
* the scope and updating any branded scope's shape every time the
* value changes to or from a function or when one function value is
* modified to a different function value; see GC_WRITE_BARRIER.
*
* On trace the shape requirement is mostly handled by normal shape
* guarding. However, the global object's shape is required to be
* invariant at trace recording time, and since a function-to-function
* transition can change shape, we must handle this edge case
* separately with the following guard. See also bug 473256.
*/
if (VALUE_IS_FUNCTION(cx, r)) {
guard(true,
lir->ins2(LIR_eq, r_ins, INS_CONSTPTR(JSVAL_TO_OBJECT(r))),
MISMATCH_EXIT);
}
set(&STOBJ_GET_SLOT(obj, slot), r_ins);
JS_ASSERT(*pc != JSOP_INITPROP);

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

View File

@ -302,7 +302,11 @@ GetContextData(JSContext *cx)
static JSBool
ShellOperationCallback(JSContext *cx)
{
return !gCanceled;
if (!gCanceled)
return JS_TRUE;
JS_ClearPendingException(cx);
return JS_FALSE;
}
static void
@ -1017,6 +1021,48 @@ Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return JS_FALSE;
}
static const char *
ToSource(JSContext *cx, jsval *vp)
{
JSString *str = JS_ValueToSource(cx, *vp);
if (str) {
*vp = STRING_TO_JSVAL(str);
return JS_GetStringBytes(str);
}
JS_ClearPendingException(cx);
return "<<error converting value to string>>";
}
static JSBool
AssertEq(JSContext *cx, uintN argc, jsval *vp)
{
if (argc != 2) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
(argc > 2) ? JSSMSG_TOO_MANY_ARGS : JSSMSG_NOT_ENOUGH_ARGS,
"assertEq");
return JS_FALSE;
}
jsval *argv = JS_ARGV(cx, vp);
if (!js_StrictlyEqual(cx, argv[0], argv[1])) {
const char *actual = ToSource(cx, &argv[0]);
const char *expected = ToSource(cx, &argv[1]);
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED,
actual, expected);
return JS_FALSE;
}
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
#ifdef JS_TRACER
static jsval JS_FASTCALL
AssertEq_tn(JSContext *cx, jsval v1, jsval v2)
{
return (js_StrictlyEqual(cx, v1, v2)) ? JSVAL_VOID : JSVAL_ERROR_COOKIE;
}
#endif
static JSBool
GC(JSContext *cx, uintN argc, jsval *vp)
{
@ -3362,6 +3408,7 @@ Elapsed(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE;
}
JS_DEFINE_TRCINFO_1(AssertEq, (3, (static, JSVAL_RETRY, AssertEq_tn, CONTEXT, JSVAL, JSVAL, 0, 0)))
JS_DEFINE_TRCINFO_1(Print, (2, (static, JSVAL_FAIL, Print_tn, CONTEXT, STRING, 0, 0)))
JS_DEFINE_TRCINFO_1(ShapeOf, (1, (static, INT32, ShapeOf_tn, OBJECT, 0, 0)))
@ -3494,6 +3541,7 @@ static JSFunctionSpec shell_functions[] = {
JS_TN("print", Print, 0,0, Print_trcinfo),
JS_FS("help", Help, 0,0,0),
JS_FS("quit", Quit, 0,0,0),
JS_TN("assertEq", AssertEq, 2,0, AssertEq_trcinfo),
JS_FN("gc", GC, 0,0),
JS_FN("gcparam", GCParameter, 2,0),
JS_FN("countHeap", CountHeap, 0,0),
@ -3572,6 +3620,8 @@ static const char *const shell_help_messages[] = {
"print([exp ...]) Evaluate and print expressions",
"help([name ...]) Display usage and help messages",
"quit() Quit the shell",
"assertEq(actual, expected)\n"
" Throw if the two arguments are not ===",
"gc() Run the garbage collector",
"gcparam(name, value)\n"
" Wrapper for JS_SetGCParameter. The name must be either 'maxBytes' or\n"

View File

@ -4403,23 +4403,6 @@ testConvertibleObjectEqUndefined.jitstats = {
};
test(testConvertibleObjectEqUndefined);
var globalProperty;
function testFunctionToFunctionGlobalPropertyTransition()
{
function g() { return "g" }
function h() { return "h"; }
var a = [g, g, g, g, h];
var results = [];
for (i = 0; i < 5; i++)
{
globalProperty = a[i];
results[i] = globalProperty();
}
return results.join("");
}
testFunctionToFunctionGlobalPropertyTransition.expected = "ggggh";
test(testFunctionToFunctionGlobalPropertyTransition);
function testUndefinedPropertyAccess() {
var x = [1,2,3];
var y = {};