Merge tm to m-c.
authorRobert Sayre <sayrer@gmail.com>
Tue, 03 Mar 2009 13:48:15 -0500
changeset 25719 e31c4f602541b9380abc29e213fc276588d75917
parent 25698 ed2854522e78afbe78b49d812e2659b19813a96c (current diff)
parent 25718 0bec239f232ec3a53412ba6030be4baf408a884f (diff)
child 25720 bc2e5908f1e6ea11015c8645ba53ff1d288d1440
push id5699
push userrsayre@mozilla.com
push dateTue, 03 Mar 2009 18:53:04 +0000
treeherdermozilla-central@e31c4f602541 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.2a1pre
Merge tm to m-c.
dom/src/base/nsJSEnvironment.cpp
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsregexp.cpp
--- a/dom/src/base/nsJSEnvironment.cpp
+++ b/dom/src/base/nsJSEnvironment.cpp
@@ -910,53 +910,59 @@ nsJSContext::DOMOperationCallback(JSCont
   MaybeGC(cx);
 
   // Now restore the callback time and count, in case they got reset.
   ctx->mOperationCallbackTime = callbackTime;
 
   // 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);
   if (lowMemory) {
     // try to clean up:
     nsJSContext::CC();
 
     // never prevent system scripts from running
     if (!::JS_IsSystemObject(cx, ::JS_GetGlobalObject(cx))) {
 
       // lets see if CC() did anything, if not, cancel the script.
       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);
         
         nsXPIDLString title, msg;
         rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                 "LowMemoryTitle",
                                                 title);
         
         rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                  "LowMemoryMessage",
                                                  msg);
         
         //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;
       }
     }
   }
 
   PRTime now = PR_Now();
 
   if (LL_IS_ZERO(callbackTime)) {
@@ -1138,16 +1144,17 @@ nsJSContext::DOMOperationCallback(JSCont
         JS_SetPendingException(cx, rval);
         return JS_FALSE;
       case JSTRAP_CONTINUE:
       default:
         return JS_TRUE;
     }
   }
 
+  JS_ClearPendingException(cx);
   return JS_FALSE;
 }
 
 #define JS_OPTIONS_DOT_STR "javascript.options."
 
 static const char js_options_dot_str[]   = JS_OPTIONS_DOT_STR;
 static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict";
 static const char js_werror_option_str[] = JS_OPTIONS_DOT_STR "werror";
--- a/dom/src/json/nsJSON.cpp
+++ b/dom/src/json/nsJSON.cpp
@@ -524,17 +524,17 @@ nsJSONListener::OnStopRequest(nsIRequest
   nsresult rv;
 
   // This can happen with short UTF-8 messages
   if (!mSniffBuffer.IsEmpty()) {
     rv = ProcessBytes(mSniffBuffer.get(), mSniffBuffer.Length());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  JSBool ok = JS_FinishJSONParse(mCx, mJSONParser);
+  JSBool ok = JS_FinishJSONParse(mCx, mJSONParser, JSVAL_NULL);
   mJSONParser = nsnull;
 
   if (!ok)
     return NS_ERROR_FAILURE;
 
   return NS_OK;
 }
 
@@ -646,17 +646,17 @@ nsJSONListener::ConsumeConverted(const c
   rv = Consume(ustr.get(), unicharLength);
 
   return rv;
 }
 
 void nsJSONListener::Cleanup()
 {
   if (mJSONParser)
-    JS_FinishJSONParse(mCx, mJSONParser);
+    JS_FinishJSONParse(mCx, mJSONParser, JSVAL_NULL);
   mJSONParser = nsnull;
 }
 
 nsresult
 nsJSONListener::Consume(const PRUnichar* aBuffer, PRUint32 aByteLength)
 {
   if (!mJSONParser)
     return NS_ERROR_FAILURE;
--- a/dom/src/json/test/json2.js
+++ b/dom/src/json/test/json2.js
@@ -155,16 +155,17 @@
     test, toJSON, toString, valueOf
 */
 
 // Create a JSON object only if one does not already exist. We create the
 // methods in a closure to avoid creating global variables.
 
 if (!this.crockfordJSON) {
     crockfordJSON = {};
+    crockfordJSON.window = this;
 }
 (function () {
 
     function f(n) {
         // Format integers to have at least two digits.
         return n < 10 ? '0' + n : n;
     }
 
--- a/dom/src/json/test/unit/head_json.js
+++ b/dom/src/json/test/unit/head_json.js
@@ -11,9 +11,9 @@ outputDir.initWithFile(workingDir);
 outputDir.append(outputName);
 
 if (!outputDir.exists()) {
   outputDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0777);
 } else if (!outputDir.isDirectory()) {
   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");
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/src/json/test/unit/test_encoding_errors.js
@@ -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);
+}
\ No newline at end of file
new file mode 100644
--- /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
new file mode 100644
--- /dev/null
+++ b/dom/src/json/test/unit/test_syntax_errors.js
@@ -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');
+}
\ No newline at end of file
--- a/dom/src/threads/nsDOMThreadService.cpp
+++ b/dom/src/threads/nsDOMThreadService.cpp
@@ -402,30 +402,31 @@ DOMWorkerOperationCallback(JSContext* aC
   // Want a strong ref here to make sure that the monitor we wait on won't go
   // away.
   nsRefPtr<nsDOMWorkerPool> pool;
 
   PRBool wasSuspended = PR_FALSE;
   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]",
            static_cast<void*>(worker)));
 
       if (wasSuspended) {
         if (extraThreadAllowed) {
           gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
         }
         JS_ResumeRequest(aCx, suspendDepth);
       }
 
-      // Kill exectuion of the currently running JS.
+      // Kill execution of the currently running JS.
+      JS_ClearPendingException(aCx);
       return JS_FALSE;
     }
 
     // Break out if we're not suspended.
     if (!worker->IsSuspended()) {
       if (wasSuspended) {
         if (extraThreadAllowed) {
           gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
@@ -435,16 +436,17 @@ DOMWorkerOperationCallback(JSContext* aC
       return JS_TRUE;
     }
 
     if (!wasSuspended) {
       // Make sure we can get the monitor we need to wait on. It's possible that
       // 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;
       }
 
       pool = worker->Pool();
 
       // Make sure to suspend our request while we block like this, otherwise we
       // prevent GC for everyone.
       suspendDepth = JS_SuspendRequest(aCx);
@@ -456,20 +458,17 @@ DOMWorkerOperationCallback(JSContext* aC
         NS_SUCCEEDED(gDOMThreadService->ChangeThreadPoolMaxThreads(1));
 
       // Only do all this setup once.
       wasSuspended = PR_TRUE;
     }
 
     nsAutoMonitor mon(pool->Monitor());
     mon.Wait();
-  } while (1);
-
-  NS_NOTREACHED("Shouldn't get here!");
-  return JS_FALSE;
+  }
 }
 
 void
 DOMWorkerErrorReporter(JSContext* aCx,
                        const char* aMessage,
                        JSErrorReport* aReport)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Huh?!");
--- a/dom/src/threads/nsDOMWorkerEvents.cpp
+++ b/dom/src/threads/nsDOMWorkerEvents.cpp
@@ -302,17 +302,17 @@ nsDOMWorkerMessageEvent::GetData(nsAStri
 
   // This is slightly sneaky, but now that JS_BeginJSONParse succeeded we always
   // need call JS_FinishJSONParse even if JS_ConsumeJSONText fails. We'll report
   // an error if either failed, though.
   ok = JS_ConsumeJSONText(cx, parser, (jschar*)mData.get(),
                           (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;
   }
 
   NS_ASSERTION(mCachedJSVal.ToJSObject(), "Bad JSON result!");
 
   if (mIsPrimitive) {
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -302,8 +302,13 @@ MSG_DEF(JSMSG_NON_LIST_XML_METHOD,    21
 MSG_DEF(JSMSG_BAD_DELETE_OPERAND,     220, 0, JSEXN_SYNTAXERR, "invalid delete operand")
 MSG_DEF(JSMSG_BAD_INCOP_OPERAND,      221, 0, JSEXN_SYNTAXERR, "invalid increment/decrement operand")
 MSG_DEF(JSMSG_NULL_OR_UNDEFINED,      222, 2, JSEXN_TYPEERR, "{0} is {1}")
 MSG_DEF(JSMSG_LET_DECL_NOT_IN_BLOCK,  223, 0, JSEXN_SYNTAXERR, "let declaration not directly within block")
 MSG_DEF(JSMSG_BAD_OBJECT_INIT,        224, 0, JSEXN_SYNTAXERR, "invalid object initializer")
 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")
+
+
+
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4566,18 +4566,20 @@ js_generic_fast_native_method_dispatcher
      */
     if (!js_ComputeThis(cx, JS_FALSE, vp + 2))
         return JS_FALSE;
     /*
      * Protect against argc underflowing. By calling js_ComputeThis, we made
      * 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
              (fs->flags & JSFUN_TRACEABLE)
              ? JS_FUNC_TO_DATA_PTR(JSTraceableNative *, fs->call)->native
              :
 #endif
                (JSFastNative) fs->call;
@@ -4631,18 +4633,20 @@ js_generic_native_method_dispatcher(JSCo
     js_GetTopStackFrame(cx)->thisp = JSVAL_TO_OBJECT(argv[-1]);
     JS_ASSERT(cx->fp->argv == argv);
 
     /*
      * Protect against argc underflowing. By calling js_ComputeThis, we made
      * 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);
 }
 JS_END_EXTERN_C
 
 JS_PUBLIC_API(JSBool)
 JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs)
 {
@@ -5679,20 +5683,20 @@ JS_BeginJSONParse(JSContext *cx, jsval *
 JS_PUBLIC_API(JSBool)
 JS_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len)
 {
     CHECK_REQUEST(cx);
     return js_ConsumeJSONText(cx, jp, data, 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);
 }
 
 /*
  * The following determines whether C Strings are to be treated as UTF-8
  * or ISO-8859-1.  For correct operation, it must be set prior to the
  * first call to JS_NewRuntime.
  */
 #ifndef JS_C_STRINGS_ARE_UTF8
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2508,17 +2508,17 @@ JS_TryJSON(JSContext *cx, jsval *vp);
  */
 JS_PUBLIC_API(JSONParser *)
 JS_BeginJSONParse(JSContext *cx, jsval *vp);
 
 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);
 
 /************************************************************************/
 
 /*
  * Locale specific string conversion and error message callbacks.
  */
 struct JSLocaleCallbacks {
     JSLocaleToUpperCase     localeToUpperCase;
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -396,17 +396,20 @@ JS_DECLARE_CALLINFO(js_ArrayCompPush)
 JS_DECLARE_CALLINFO(js_FastNewDate)
 
 /* Defined in jsnum.cpp */
 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)
 
 /* Defined in jsbuiltins.cpp */
 #define BUILTIN1(linkage, rt, op, at0,                     cse, fold)  JS_DECLARE_CALLINFO(op)
 #define BUILTIN2(linkage, rt, op, at0, at1,                cse, fold)  JS_DECLARE_CALLINFO(op)
 #define BUILTIN3(linkage, rt, op, at0, at1, at2,           cse, fold)  JS_DECLARE_CALLINFO(op)
 #define BUILTIN4(linkage, rt, op, at0, at1, at2, at3,      cse, fold)  JS_DECLARE_CALLINFO(op)
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2230,27 +2230,24 @@ js_ValueToFunctionObject(JSContext *cx, 
         return NULL;
     }
     return FUN_OBJECT(fun);
 }
 
 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;
 }
 
 void
 js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
 {
     JSStackFrame *fp;
     uintN error;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3200,16 +3200,17 @@ ProcessSetSlotRequest(JSContext *cx, JSS
             scope = OBJ_SCOPE(oldproto);
             SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
             oldproto = STOBJ_GET_PROTO(scope->object);
         }
     }
 
     /* Finally, do the deed. */
     STOBJ_SET_SLOT(obj, slot, OBJECT_TO_JSVAL(pobj));
+    STOBJ_SET_DELEGATE(pobj);
 }
 
 static void
 DestroyScriptsToGC(JSContext *cx, JSScript **listp)
 {
     JSScript *script;
 
     while ((script = *listp) != NULL) {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2871,17 +2871,16 @@ js_Interpret(JSContext *cx)
             goto do_switch;
 #endif
           }
 
           /* No-ops for ease of decompilation. */
           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)
 #endif
           END_EMPTY_CASES
 
           /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
           BEGIN_CASE(JSOP_LINENO)
@@ -2942,27 +2941,27 @@ js_Interpret(JSContext *cx)
 
           BEGIN_CASE(JSOP_LEAVEWITH)
             JS_ASSERT(regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain));
             regs.sp--;
             js_LeaveWith(cx);
           END_CASE(JSOP_LEAVEWITH)
 
           BEGIN_CASE(JSOP_RETURN)
-            CHECK_BRANCH();
             fp->rval = POP_OPND();
             /* FALL THROUGH */
 
           BEGIN_CASE(JSOP_RETRVAL)    /* fp->rval already set */
           BEGIN_CASE(JSOP_STOP)
             /*
              * When the inlined frame exits with an exception or an error, ok
              * will be false after the inline_return label.
              */
             ASSERT_NOT_THROWING(cx);
+            CHECK_BRANCH();
 
             if (fp->imacpc) {
                 /*
                  * If we are at the end of an imacro, return to its caller in
                  * the current frame.
                  */
                 JS_ASSERT(op == JSOP_STOP);
 
@@ -6380,26 +6379,32 @@ js_Interpret(JSContext *cx)
             len = JSVAL_TO_INT(rval);
             regs.pc = script->main;
           END_VARLEN_CASE
 
           BEGIN_CASE(JSOP_EXCEPTION)
             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;
             cx->exception = POP_OPND();
           END_CASE(JSOP_THROWING)
 
           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. */
             goto error;
 
           BEGIN_CASE(JSOP_SETLOCALPOP)
             /*
              * The stack must have a block with at least one local slot below
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3329,16 +3329,23 @@ PurgeProtoChain(JSContext *cx, JSObject 
 }
 
 static void
 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))
             return;
     }
 }
 
 JSScopeProperty *
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -123,17 +123,40 @@ struct JSObjectMap {
 #define JS_INITIAL_NSLOTS   5
 
 /*
  * When JSObject.dslots is not null, JSObject.dslots[-1] records the number of
  * available slots.
  */
 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 */
 };
 
 #define JSSLOT_PROTO        0
 #define JSSLOT_PARENT       1
 #define JSSLOT_PRIVATE      2
 #define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE)           \
@@ -410,17 +433,19 @@ extern JSObject *
 js_InitBlockClass(JSContext *cx, JSObject* obj);
 
 extern JSObject *
 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[];
 extern const char js_isPrototypeOf_str[];
 extern const char js_propertyIsEnumerable_str[];
 extern const char js_defineGetter_str[];
 extern const char js_defineSetter_str[];
 extern const char js_lookupGetter_str[];
@@ -725,16 +750,24 @@ js_CheckPrincipalsAccess(JSContext *cx, 
 extern JSObject *
 js_GetWrappedObject(JSContext *cx, JSObject *obj);
 
 /* NB: Infallible. */
 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);
 JS_FRIEND_API(void) js_DumpAtom(JSAtom *atom);
 JS_FRIEND_API(void) js_DumpValue(jsval val);
 JS_FRIEND_API(void) js_DumpId(jsid id);
 JS_FRIEND_API(void) js_DumpObject(JSObject *obj);
 #endif
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -63,31 +63,29 @@ JSClass js_JSONClass = {
     JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 JSBool
 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;
 }
 
 class StringifyClosure : JSAutoTempValueRooter
 {
 public:
     StringifyClosure(JSContext *cx, size_t len, jsval *vec)
         : JSAutoTempValueRooter(cx, len, vec), cx(cx), s(vec)
@@ -118,57 +116,48 @@ WriteCallback(const jschar *buf, uint32 
 
     return JS_TRUE;
 }
 
 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;
 }
 
 JSBool
 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;
 }
 
 
 static const jschar quote = jschar('"');
 static const jschar backslash = jschar('\\');
 static const jschar unicodeEscape[] = {'\\', 'u', '0', '0'};
 
@@ -208,19 +197,19 @@ write_string(JSContext *cx, JSONWriteCal
 
     if (!callback(&quote, 1, data))
         return JS_FALSE;
 
     return JS_TRUE;
 }
 
 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);
 
     if (!s)
         return JS_FALSE;
 
     if (type == JSTYPE_STRING)
         return write_string(cx, callback, data, JS_GetStringChars(s), JS_GetStringLength(s));
@@ -234,33 +223,37 @@ stringify_primitive(JSContext *cx, jsval
                 outputString = s;
         } else {
             outputString = s;
         }
     } else if (type == JSTYPE_BOOLEAN) {
         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)
         return JS_FALSE;
 
     return callback(JS_GetStringChars(outputString), JS_GetStringLength(outputString), data);
 }
 
 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);
     JSBool isArray = JS_IsArrayObject(cx, obj);
     jschar output = jschar(isArray ? '[' : '{');
     if (!callback(&output, 1, data))
         return JS_FALSE;
 
@@ -283,17 +276,22 @@ stringify(JSContext *cx, jsval *vp, JSOb
     jsval key;
     JSBool memberWritten = JS_FALSE;
     do {
         outputValue = JSVAL_VOID;
 
         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);
             if (!ok)
                 break;
             if (key == JSVAL_HOLE)
                 break;
 
@@ -311,27 +309,26 @@ stringify(JSContext *cx, jsval *vp, JSOb
             // Don't include prototype properties, since this operation is
             // supposed to be implemented as if by ES3.1 Object.keys()
             jsid id;
             jsval v = JS_FALSE;
             if (!js_ValueToStringId(cx, STRING_TO_JSVAL(ks), &id) ||
                 !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 {
+            }
+
+            if (v != JSVAL_TRUE)
                 continue;
-            }
+
+            ok = JS_GetPropertyById(cx, obj, id, &outputValue);
+            if (!ok)
+                break;
         }
 
-        if (!ok)
-            break;
-
         // if this is an array, holes are transmitted as null
         if (isArray && outputValue == JSVAL_VOID) {
             outputValue = JSVAL_NULL;
         } else if (JSVAL_IS_OBJECT(outputValue)) {
             ok = js_TryJSON(cx, &outputValue);
             if (!ok)
                 break;
         }
@@ -341,18 +338,18 @@ stringify(JSContext *cx, jsval *vp, JSOb
         // elide undefined values and functions and XML
         if (outputValue == JSVAL_VOID || type == JSTYPE_FUNCTION || type == JSTYPE_XML)
             continue;
 
         // output a comma unless this is the first member to write
         if (memberWritten) {
             output = jschar(',');
             ok = callback(&output, 1, data);
-        if (!ok)
-            break;
+            if (!ok)
+                break;
         }
         memberWritten = JS_TRUE;
 
 
         // Be careful below, this string is weakly rooted.
         JSString *s;
 
         // If this isn't an array, we need to output a key
@@ -372,61 +369,160 @@ stringify(JSContext *cx, jsval *vp, JSOb
             if (!ok)
                 break;
         }
 
         if (!JSVAL_IS_PRIMITIVE(outputValue)) {
             // 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);
 
     return ok;                      
 }
 
 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;
 }
 
 // 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 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)
 {
     if (!cx)
         return NULL;
 
     JSObject *arr = js_NewArrayObject(cx, 0, NULL);
@@ -461,31 +557,31 @@ js_BeginJSONParse(JSContext *cx, jsval *
     return jp;
 bad:
     JS_free(cx, jp->buffer);
     JS_free(cx, jp);
     return NULL;
 }
 
 JSBool
-js_FinishJSONParse(JSContext *cx, JSONParser *jp)
+js_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver)
 {
     if (!jp)
         return 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) {
             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);
             }
         }
     }
 
     if (jp->objectKey)
         js_FinishStringBuffer(jp->objectKey);
     JS_free(cx, jp->objectKey);
 
@@ -493,60 +589,76 @@ js_FinishJSONParse(JSContext *cx, JSONPa
         js_FinishStringBuffer(jp->buffer);
     JS_free(cx, jp->buffer);
 
     if (!js_RemoveRoot(cx->runtime, &jp->objectStack))
         return JS_FALSE;
     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;
 
     return JS_TRUE;
 }
 
 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;
     }
 
     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, 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 {
         ok = JS_DefineUCProperty(cx, parent, jp->objectKey->base,
                                  STRING_BUFFER_OFFSET(jp->objectKey), value,
                                  NULL, NULL, JSPROP_ENUMERATE);
         js_FinishStringBuffer(jp->objectKey);
         js_InitStringBuffer(jp->objectKey);
@@ -556,18 +668,20 @@ PushValue(JSContext *cx, JSONParser *jp,
 }
 
 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 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);
 
     // Check if this is the root object
     if (len == 0) {
         *jp->rootVal = v;
         // This property must be enumerable to keep the array dense
@@ -579,46 +693,28 @@ PushObject(JSContext *cx, JSONParser *jp
     }
 
     jsval p;
     if (!OBJ_GET_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len - 1), &p))
         return JS_FALSE;
 
     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
     if (!OBJ_DEFINE_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len), v,
                              NULL, NULL, JSPROP_ENUMERATE, NULL)) {
         return JS_FALSE;
     }
 
     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)
 {
     JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
     if (!obj)
         return JS_FALSE;
 
     return PushObject(cx, jp, obj);
@@ -649,86 +745,103 @@ CloseObject(JSContext *cx, JSONParser *j
 
 static JSBool
 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
 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
 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:
       ok = HandleString(cx, jp, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer));
       break;
 
     case JSON_DATA_KEYSTRING:
       js_AppendUCString(jp->objectKey, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer));
@@ -754,216 +867,232 @@ HandleData(JSContext *cx, JSONParser *jp
 }
 
 JSBool
 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 (c == '}') {
-                    // we should only find these in OBJECT_KEY state
-                    return JS_FALSE; // unexpected failure
-                }
-
-                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;
+                if (*jp->statep != JSON_PARSE_STATE_ARRAY) {
+                    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
+                    return JS_FALSE;
                 }
 
-            // 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;
+                if (!CloseArray(cx, jp) || !PopState(cx, jp))
+                    return JS_FALSE;
 
-            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;
-
-            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;
+            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_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 (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;
 
-                if (++(jp->numHex) == 4) {
-                    js_AppendChar(jp->buffer, jp->hexChar);
-                    jp->hexChar = 0;
-                    jp->numHex = 0;
-                    *jp->statep = JSON_PARSE_STATE_STRING;
-                }
-                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_KEYWORD:
-                if (JS7_ISLET(c)) {
-                    js_AppendChar(jp->buffer, c);
+          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 {
-                    // this character isn't part of the keyword, process it again
-                    i--;
-                    if (!PopState(jp))
-                        return JS_FALSE;
+                    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;
 
-                    if (!HandleData(cx, jp, JSON_DATA_KEYWORD))
-                        return JS_FALSE;
-                }
+          case JSON_PARSE_STATE_STRING_ESCAPE:
+            switch (c) {
+              case '"':
+              case '\\':
+              case '/':
                 break;
-
-            case JSON_PARSE_STATE_NUMBER:
-                if (IsNumChar(c)) {
-                    js_AppendChar(jp->buffer, c);
+              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 {
-                    // 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;
+                    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
+                    return JS_FALSE;
                 }
-                break;
+            }
+
+            js_AppendChar(jp->buffer, 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 {
+                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;
 
-            case JSON_PARSE_STATE_FINISHED:
-                if (!JS_ISXMLSPACE(c))
-                  return JS_FALSE; // extra input
+          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;
 
-                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(cx, jp))
+                    return JS_FALSE;
+                if (!HandleData(cx, jp, JSON_DATA_NUMBER))
+                    return JS_FALSE;
+            }
+            break;
 
-            default:
-                JS_NOT_REACHED("Invalid JSON parser state");
+          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");
       }
     }
 
     return JS_TRUE;
 }
 
 #if JS_HAS_TOSOURCE
 static JSBool
--- a/js/src/json.h
+++ b/js/src/json.h
@@ -95,13 +95,13 @@ struct JSONParser {
 
 extern JSONParser *
 js_BeginJSONParse(JSContext *cx, jsval *rootVal);
 
 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
 
 #endif /* json_h___ */
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -236,16 +236,22 @@ js_Disassemble(JSContext *cx, JSScript *
                               lines, fp);
         if (!len)
             return JS_FALSE;
         pc += len;
     }
     return JS_TRUE;
 }
 
+JSBool
+js_DumpScript(JSContext *cx, JSScript *script)
+{
+    return js_Disassemble(cx, script, true, stdout);
+}
+
 const char *
 ToDisassemblySource(JSContext *cx, jsval v)
 {
     JSObject *obj;
     JSScopeProperty *sprop;
     char *source;
     const char *bytes;
     JSString *str;
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -2037,25 +2037,24 @@ static JSBool
 ProcessCharSet(JSContext *cx, JSRegExp *re, RECharSet *charSet);
 
 class RegExpNativeCompiler {
  private:
     JSContext*       cx;
     JSRegExp*        re;
     CompilerState*   cs;            /* RegExp to compile */
     Fragment*        fragment;
-    LirBuffer*       lirbuf;
     LirWriter*       lir;
     LirBufWriter*    lirBufWriter;  /* for skip */
 
     LIns*            state;
     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) 
     {
         if (fragment->lirbuf->outOMem()) 
             return JS_FALSE;
         ins->target(lir->ins0(LIR_label)); 
         return JS_TRUE;
     }
--- a/js/src/jsshell.msg
+++ b/js/src/jsshell.msg
@@ -44,8 +44,11 @@
 MSG_DEF(JSSMSG_NOT_AN_ERROR,             0, 0, JSEXN_NONE, "<Error #0 is reserved>")
 MSG_DEF(JSSMSG_CANT_OPEN,                1, 2, JSEXN_NONE, "can't open {0}: {1}") 
 MSG_DEF(JSSMSG_TRAP_USAGE,               2, 0, JSEXN_NONE, "usage: trap [fun] [pc] expr") 
 MSG_DEF(JSSMSG_LINE2PC_USAGE,            3, 0, JSEXN_NONE, "usage: line2pc [fun] line") 
 MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY,        4, 0, JSEXN_NONE, "only works on JS scripts read from files") 
 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}")
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1030,23 +1030,57 @@ str_charCodeAt(JSContext *cx, uintN argc
     return JS_TRUE;
 
 out_of_range:
     *vp = JS_GetNaNValue(cx);
     return JS_TRUE;
 }
 
 #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
 js_BoyerMooreHorspool(const jschar *text, jsint textlen,
                       const jschar *pat, jsint patlen,
                       jsint start)
 {
     jsint i, j, k, m;
@@ -2501,18 +2535,19 @@ JS_DEFINE_CALLINFO_2(extern, INT32,  js_
 
 JS_DEFINE_TRCINFO_1(str_toString,
     (2, (extern, STRING_RETRY,      String_p_toString, CONTEXT, THIS,                        1, 1)))
 JS_DEFINE_TRCINFO_2(str_substring,
     (4, (static, STRING_RETRY,      String_p_substring, CONTEXT, THIS_STRING, INT32, INT32,   1, 1)),
     (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)),
     (4, (static, STRING_RETRY,      String_p_concat_2str, CONTEXT, THIS_STRING, STRING, STRING, 1, 1)),
     (5, (static, STRING_RETRY,      String_p_concat_3str, CONTEXT, THIS_STRING, STRING, STRING, STRING, 1, 1)))
 JS_DEFINE_TRCINFO_2(str_match,
     (4, (static, JSVAL_FAIL,        String_p_match, CONTEXT, THIS_STRING, PC, REGEXP,         1, 1)),
     (4, (static, JSVAL_FAIL,        String_p_match_obj, CONTEXT, THIS, PC, REGEXP,            1, 1)))
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -47,17 +47,18 @@
 #define alloca _alloca
 #endif
 #ifdef SOLARIS
 #include <alloca.h>
 #endif
 #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"
 #include "jsemit.h"
 #include "jsfun.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jsobj.h"
@@ -900,29 +901,43 @@ public:
                 if (isPromote(lhs) && isPromote(rhs)) {
                     LOpcode op = LOpcode(s0->opcode() & ~LIR64);
                     return out->ins2(op, demote(out, lhs), demote(out, rhs));
                 }
             }
             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);
             }
             if (s0->isCall() && s0->callInfo() == &js_UnboxDouble_ci)
                 return callArgN(s0, 0);
         }
         return out->insCall(ci, args);
     }
@@ -6737,37 +6752,16 @@ TraceRecorder::functionCall(bool constru
                     goto next_specialization;
             } else if (argtype == 'v') {
                 box_jsval(arg, *argp);
             } else {
                 goto next_specialization;
             }
             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:;
     } while ((known++)->flags & JSTN_MORE);
 
     if (FUN_SLOW_NATIVE(fun) && fun->u.n.native == js_Array)
         return newArray(FUN_OBJECT(fun), argc, &tval + 1, &fval);
 
@@ -7014,40 +7008,16 @@ TraceRecorder::record_SetPropHit(JSPropC
 
     if (obj == globalObj) {
         JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)));
         uint32 slot = sprop->slot;
         if (!lazilyImportGlobalSlot(slot))
             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);
         if (pc[JSOP_SETPROP_LENGTH] != JSOP_POP)
             set(&l, r_ins);
         return true;
     }
 
--- a/js/src/liveconnect/jsj_JavaObject.c
+++ b/js/src/liveconnect/jsj_JavaObject.c
@@ -52,16 +52,17 @@
  *
  * An instance of JavaObject is the JavaScript reflection of a Java object.
  *
  */
 
 #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 */
 
 #ifdef JSJ_THREADSAFE
 #include "prmon.h"
 #endif
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -297,17 +297,21 @@ GetContextData(JSContext *cx)
 
     JS_ASSERT(data);
     return data;
 }
 
 static JSBool
 ShellOperationCallback(JSContext *cx)
 {
-    return !gCanceled;
+    if (!gCanceled)
+        return JS_TRUE;
+
+    JS_ClearPendingException(cx);
+    return JS_FALSE;
 }
 
 static void
 SetContextOptions(JSContext *cx)
 {
     jsuword stackLimit;
 
     if (gMaxStackSize == 0) {
@@ -1012,16 +1016,58 @@ Quit(JSContext *cx, JSObject *obj, uintN
 #endif
 
     JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode);
 
     gQuitting = JS_TRUE;
     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)
 {
     JSRuntime *rt;
     uint32 preBytes;
 
     rt = cx->runtime;
     preBytes = rt->gcBytes;
@@ -3357,16 +3403,17 @@ Elapsed(JSContext *cx, uintN argc, jsval
         if (data)
             d = js_IntervalNow() - data->startTime;
         return JS_NewNumberValue(cx, d, vp);
     }
     JS_ReportError(cx, "Wrong number of arguments");
     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)))
 
 #ifdef XP_UNIX
 
 #include <fcntl.h>
 #include <sys/stat.h>
 
@@ -3489,16 +3536,17 @@ Snarf(JSContext *cx, JSObject *obj, uint
 static JSFunctionSpec shell_functions[] = {
     JS_FS("version",        Version,        0,0,0),
     JS_FS("options",        Options,        0,0,0),
     JS_FS("load",           Load,           1,0,0),
     JS_FN("readline",       ReadLine,       0,0),
     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),
 #ifdef JS_GC_ZEAL
     JS_FN("gczeal",         GCZeal,         1,0),
 #endif
     JS_FS("trap",           Trap,           3,0,0),
     JS_FS("untrap",         Untrap,         2,0,0),
@@ -3567,16 +3615,18 @@ static const char shell_help_header[] =
 static const char *const shell_help_messages[] = {
 "version([number])        Get or set JavaScript version number",
 "options([option ...])    Get or toggle JavaScript options",
 "load(['foo.js' ...])     Load files named by string arguments",
 "readline()               Read a single line from stdin",
 "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"
 "  'maxMallocBytes' and the value must be convertable to a positive uint32",
 "countHeap([start[, kind]])\n"
 "  Count the number of live GC things in the heap or things reachable from\n"
 "  start when it is given and is not null. kind is either 'all' (default) to\n"
 "  count all things or one of 'object', 'double', 'string', 'function',\n"
--- a/js/src/trace-test.js
+++ b/js/src/trace-test.js
@@ -4398,33 +4398,16 @@ function testConvertibleObjectEqUndefine
 testConvertibleObjectEqUndefined.expected =
   x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) +
   x4(false) + x4(false) + x4(false) + x4(false) + "20";
 testConvertibleObjectEqUndefined.jitstats = {
   sideExitIntoInterpreter: 3
 };
 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 = {};
     var a = { foo: 1 };
     y.__proto__ = x;
     var z = [x, x, x, y, y, y, y, a, a, a];
     var s = "";
     for (var i = 0; i < z.length; ++i)