Merge tracemonkey to mozilla-central. a=blockers
authorRobert Sayre <sayrer@gmail.com>
Wed, 15 Dec 2010 12:21:50 -0800
changeset 59254 76f51caf3a169b9d8a4cb60eddce09dae1fd6cde
parent 59215 8755d308796dc5ab5859fa4780131cfc3fd826ab (current diff)
parent 59253 c86a79eb18b95217369d7dc02e0ce3c79a6fdb68 (diff)
child 59255 74bd849a56d633fbb4afdf93468ae33268ef878f
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersblockers
milestone2.0b9pre
Merge tracemonkey to mozilla-central. a=blockers
configure.in
js/src/configure.in
js/src/jit-test/tests/basic/bug616762.js
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsemit.cpp
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jsregexpinlines.h
js/src/jsstr.cpp
js/src/shell/js.cpp
js/src/trace-test/tests/basic/bug616762.js
js/src/trace-test/tests/basic/testArrayIn.js
js/src/trace-test/tests/basic/testArrayInWithIndexedProto.js
--- a/configure.in
+++ b/configure.in
@@ -1906,23 +1906,23 @@ case "$host" in
 
 *)
     HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX"
     HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}"
     ;;
 esac
 
 dnl We require version 2.5 or newer of Python to build.
-AC_MSG_CHECKING([for minimum required Python version >= $PYTHON_VERSION])
+AC_MSG_CHECKING([for Python version >= $PYTHON_VERSION but not 3.x])
 changequote(,)
-$PYTHON -c "import sys; sys.exit(sys.version[:3] < sys.argv[1])" $PYTHON_VERSION
+$PYTHON -c "import sys; sys.exit(sys.version[:3] < sys.argv[1] or sys.version[:2] != '2.')" $PYTHON_VERSION
 _python_res=$?
 changequote([,])
 if test "$_python_res" != 0; then
-    AC_MSG_ERROR([Python $PYTHON_VERSION or higher is required.])
+    AC_MSG_ERROR([Python $PYTHON_VERSION or higher (but not Python 3.x) is required.])
 fi
 AC_MSG_RESULT([yes])
 
 dnl Get mozilla version from central milestone file
 MOZILLA_VERSION=`$PERL $srcdir/config/milestone.pl -topsrcdir $srcdir`
 
 dnl Get version of various core apps from the version files.
 FIREFOX_VERSION=`cat $topsrcdir/browser/config/version.txt`
--- a/dom/src/threads/nsDOMWorker.cpp
+++ b/dom/src/threads/nsDOMWorker.cpp
@@ -154,17 +154,16 @@ nsDOMWorkerFunctions::MakeTimeout(JSCont
                                   uintN aArgc,
                                   jsval* aVp,
                                   PRBool aIsInterval)
 {
   nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
   NS_ASSERTION(worker, "This should be set by the DOM thread service!");
 
   if (worker->IsCanceled()) {
-    JS_ReportError(aCx, "Worker is canceled");
     return JS_FALSE;
   }
 
   PRUint32 id = worker->NextTimeoutId();
 
   if (worker->IsClosing()) {
     // Timeouts won't run in the close handler, fake success and bail.
     JS_SET_RVAL(aCx, aVp, INT_TO_JSVAL(id));
@@ -203,17 +202,16 @@ JSBool
 nsDOMWorkerFunctions::KillTimeout(JSContext* aCx,
                                   uintN aArgc,
                                   jsval* aVp)
 {
   nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
   NS_ASSERTION(worker, "This should be set by the DOM thread service!");
 
   if (worker->IsCanceled()) {
-    JS_ReportError(aCx, "Worker is canceled");
     return JS_FALSE;
   }
 
   if (!aArgc) {
     JS_ReportError(aCx, "Function requires at least 1 parameter");
     return JS_FALSE;
   }
 
@@ -232,17 +230,16 @@ JSBool
 nsDOMWorkerFunctions::LoadScripts(JSContext* aCx,
                                   uintN aArgc,
                                   jsval* aVp)
 {
   nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
   NS_ASSERTION(worker, "This should be set by the DOM thread service!");
 
   if (worker->IsCanceled()) {
-    JS_ReportError(aCx, "Worker is canceled");
     return JS_FALSE;
   }
 
   if (!aArgc) {
     // No argument is ok according to spec.
     return JS_TRUE;
   }
 
@@ -308,17 +305,16 @@ nsDOMWorkerFunctions::NewXMLHttpRequest(
   if (!obj) {
     return JS_FALSE;
   }
 
   nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
   NS_ASSERTION(worker, "This should be set by the DOM thread service!");
 
   if (worker->IsCanceled()) {
-    JS_ReportError(aCx, "Worker is canceled");
     return JS_FALSE;
   }
 
   if (aArgc) {
     JS_ReportError(aCx, "XMLHttpRequest constructor takes no arguments!");
     return JS_FALSE;
   }
 
@@ -358,111 +354,47 @@ JSBool
 nsDOMWorkerFunctions::AtoB(JSContext* aCx,
                            uintN aArgc,
                            jsval* aVp)
 {
   nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
   NS_ASSERTION(worker, "This should be set by the DOM thread service!");
 
   if (worker->IsCanceled()) {
-    JS_ReportError(aCx, "Worker is canceled");
     return JS_FALSE;
   }
 
   if (!aArgc) {
     JS_ReportError(aCx, "Function requires at least 1 parameter");
     return JS_FALSE;
   }
 
-  JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
-  if (!str) {
-    NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
-    return JS_FALSE;
-  }
-
-  size_t len = JS_GetStringEncodingLength(aCx, str);
-  if (len == size_t(-1))
-      return JS_FALSE;
-
-  JSUint32 alloc_len = (len + 1) * sizeof(char);
-  char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len));
-  if (!buffer)
-      return JS_FALSE;
-
-  JS_EncodeStringToBuffer(str, buffer, len);
-  buffer[len] = '\0';
-
-  nsDependentCString string(buffer, len);
-  nsCAutoString result;
-
-  if (NS_FAILED(nsXPConnect::Base64Decode(string, result))) {
-    JS_ReportError(aCx, "Failed to decode base64 string!");
-    return JS_FALSE;
-  }
-
-  str = JS_NewStringCopyN(aCx, result.get(), result.Length());
-  if (!str) {
-    return JS_FALSE;
-  }
-
-  JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(str));
-  return JS_TRUE;
+  return nsXPConnect::Base64Decode(aCx, JS_ARGV(aCx, aVp)[0],
+                                   &JS_RVAL(aCx, aVp));
 }
 
 JSBool
 nsDOMWorkerFunctions::BtoA(JSContext* aCx,
                            uintN aArgc,
                            jsval* aVp)
 {
   nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
   NS_ASSERTION(worker, "This should be set by the DOM thread service!");
 
   if (worker->IsCanceled()) {
-    JS_ReportError(aCx, "Worker is canceled");
     return JS_FALSE;
   }
 
   if (!aArgc) {
     JS_ReportError(aCx, "Function requires at least 1 parameter");
     return JS_FALSE;
   }
 
-  JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
-  if (!str) {
-    NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
-    return JS_FALSE;
-  }
-
-  size_t len = JS_GetStringEncodingLength(aCx, str);
-  if (len == size_t(-1))
-      return JS_FALSE;
-
-  JSUint32 alloc_len = (len + 1) * sizeof(char);
-  char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len));
-  if (!buffer)
-      return JS_FALSE;
-
-  JS_EncodeStringToBuffer(str, buffer, len);
-  buffer[len] = '\0';
-
-  nsDependentCString string(buffer, len);
-  nsCAutoString result;
-
-  if (NS_FAILED(nsXPConnect::Base64Encode(string, result))) {
-    JS_ReportError(aCx, "Failed to encode base64 data!");
-    return JS_FALSE;
-  }
-
-  str = JS_NewStringCopyN(aCx, result.get(), result.Length());
-  if (!str) {
-    return JS_FALSE;
-  }
-
-  JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(str));
-  return JS_TRUE;
+  return nsXPConnect::Base64Encode(aCx, JS_ARGV(aCx, aVp)[0],
+                                   &JS_RVAL(aCx, aVp));
 }
 
 JSBool
 nsDOMWorkerFunctions::NewChromeWorker(JSContext* aCx,
                                       uintN aArgc,
                                       jsval* aVp)
 {
   nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
@@ -486,17 +418,16 @@ nsDOMWorkerFunctions::MakeNewWorker(JSCo
   if (!obj) {
     return JS_FALSE;
   }
 
   nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
   NS_ASSERTION(worker, "This should be set by the DOM thread service!");
 
   if (worker->IsCanceled()) {
-    JS_ReportError(aCx, "Worker is canceled");
     return JS_FALSE;
   }
 
   if (!aArgc) {
     JS_ReportError(aCx, "Worker constructor must have an argument!");
     return JS_FALSE;
   }
 
@@ -580,17 +511,16 @@ nsDOMWorkerFunctions::CTypesLazyGetter(J
     NS_ASSERTION(nsDependentJSString(str).EqualsLiteral("ctypes"), "Bad id!");
   }
 #endif
   nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
   NS_ASSERTION(worker, "This should be set by the DOM thread service!");
   NS_ASSERTION(worker->IsPrivileged(), "This shouldn't be possible!");
 
   if (worker->IsCanceled()) {
-    JS_ReportError(aCx, "Worker is canceled");
     return JS_FALSE;
   }
 
   jsval ctypes;
   return JS_DeletePropertyById(aCx, aObj, aId) &&
          JS_InitCTypesClass(aCx, aObj) &&
          JS_GetProperty(aCx, aObj, "ctypes", &ctypes) &&
          JS_SetCTypesCallbacks(aCx, JSVAL_TO_OBJECT(ctypes), &sCallbacks) &&
--- a/dom/src/threads/test/atob_worker.js
+++ b/dom/src/threads/test/atob_worker.js
@@ -1,12 +1,10 @@
-var data = [ -1, 0, 1, 1.5, undefined, true, false ];
-
-// XXXbent window.atob treats |null| as the empty string, whereas worker.atob
-//         and the js component loader treat it as the string 'null'. Meh.
+var data = [ -1, 0, 1, 1.5, null, undefined, true, false, "foo",
+             "123456789012345", "1234567890123456", "12345678901234567"];
 
 var str = "";
 for (var i = 0; i < 30; i++) {
   data.push(str);
   str += i % 2 ? "b" : "a";
 }
 
 function onmessage(event) {
--- a/js/src/assembler/assembler/MacroAssemblerX86Common.h
+++ b/js/src/assembler/assembler/MacroAssemblerX86Common.h
@@ -1163,28 +1163,42 @@ private:
              "movl %%ecx, %0;"
              "movl %%edx, %1;"
              : "=g" (flags_ecx), "=g" (flags_edx)
              :
              : "%eax", "%ecx", "%edx"
              );
 #endif
 #elif WTF_COMPILER_SUNPRO
+#if WTF_CPU_X86_64
+        asm (
+             "movl $0x1, %%eax;"
+             "pushq %%rbx;"
+             "cpuid;"
+             "popq %%rbx;"
+             "movl %%ecx, (%rsi);"
+             "movl %%edx, (%rdi);"
+             :
+             : "S" (&flags_ecx), "D" (&flags_edx)
+             : "%eax", "%ecx", "%edx"
+             );
+#else
         asm (
              "movl $0x1, %eax;"
              "pushl %ebx;"
              "cpuid;"
              "popl %ebx;"
              "movl %ecx, (%esi);"
              "movl %edx, (%edi);"
              :
              : "S" (&flags_ecx), "D" (&flags_edx)
              : "%eax", "%ecx", "%edx"
              );
 #endif
+#endif
         static const int SSEFeatureBit = 1 << 25;
         static const int SSE2FeatureBit = 1 << 26;
         static const int SSE3FeatureBit = 1 << 0;
         static const int SSSE3FeatureBit = 1 << 9;
         static const int SSE41FeatureBit = 1 << 19;
         static const int SSE42FeatureBit = 1 << 20;
         if (flags_ecx & SSE42FeatureBit)
             s_sseCheckState = HasSSE4_2;
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -981,17 +981,17 @@ AC_MSG_CHECKING([for full perl installat
 _perl_res=$?
 if test "$_perl_res" != 0; then
     AC_MSG_RESULT([no])
     AC_MSG_ERROR([Cannot find Config.pm or \$Config{archlib}.  A full perl installation is required.])
 else
     AC_MSG_RESULT([yes])    
 fi
 
-MOZ_PATH_PROGS(PYTHON, $PYTHON python2.6 python2.5 python2.4 python)
+MOZ_PATH_PROGS(PYTHON, $PYTHON python2.7 python2.6 python2.5 python)
 if test -z "$PYTHON"; then
     AC_MSG_ERROR([python was not found in \$PATH])
 fi
 
 if test -z "$COMPILE_ENVIRONMENT"; then
     NSINSTALL_BIN='$(PYTHON) $(topsrcdir)/config/nsinstall.py'
 fi
 AC_SUBST(NSINSTALL_BIN)
@@ -1895,23 +1895,23 @@ case "$host" in
 *)
     HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX"
     HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}"
     ;;
 esac
 
 dnl We require version 2.4 or newer of Python to build,
 dnl and 2.5 or newer on Windows.
-AC_MSG_CHECKING([for minimum required Python version >= $PYTHON_VERSION])
+AC_MSG_CHECKING([for Python version >= $PYTHON_VERSION but not 3.x])
 changequote(,)
-$PYTHON -c "import sys; sys.exit(sys.version[:3] < sys.argv[1])" $PYTHON_VERSION
+$PYTHON -c "import sys; sys.exit(sys.version[:3] < sys.argv[1] or sys.version[:2] != '2.')" $PYTHON_VERSION
 _python_res=$?
 changequote([,])
 if test "$_python_res" != 0; then
-    AC_MSG_ERROR([Python $PYTHON_VERSION or higher is required.])
+    AC_MSG_ERROR([Python $PYTHON_VERSION or higher (but not Python 3.x) is required.])
 fi
 AC_MSG_RESULT([yes])
 
 dnl ========================================================
 dnl System overrides of the defaults for target
 dnl ========================================================
 
 case "$target" in
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug559912.js
@@ -0,0 +1,12 @@
+
+function s(f) { this._m = f; }
+function C() {
+    Object.defineProperty(this, "m", {set: s});
+    this.m = function () {};
+}
+var last = {};
+for (var i = 0; i < 20; i++) {
+  var a = new C;
+  assertEq(a._m === last._m, false);
+  last = a;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug609502-1.js
@@ -0,0 +1,9 @@
+for(var i = 0; i < RUNLOOP; i++) {
+      x = ''.charCodeAt(NaN);
+}
+
+for(var i = 0; i < RUNLOOP; i++) {
+      x = ''.charAt(NaN);
+}
+
+// Don't assert
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug609502-2.js
@@ -0,0 +1,5 @@
+for (var i = 0; i < RUNLOOP; i++) {
+    Math.abs(-2147483648)
+}
+
+// Don't assert
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug609502-3.js
@@ -0,0 +1,11 @@
+{
+      function a() {}
+}
+Math.floor(Math.d)
+  function c() {}
+  c()
+  for each(let b in [0, 0, 0, 0, 0, 0, 0, -2147483648]) {
+        print(Math.abs(b))
+  }
+
+// Don't assert
rename from js/src/trace-test/tests/basic/bug616762.js
rename to js/src/jit-test/tests/basic/bug616762.js
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug618577.js
@@ -0,0 +1,5 @@
+var x = Uint32Array();
+for (var i = 0; i < 100; i++)
+    x[77] = x[77];
+
+// Don't assert.
rename from js/src/trace-test/tests/basic/testArrayIn.js
rename to js/src/jit-test/tests/basic/testArrayIn.js
rename from js/src/trace-test/tests/basic/testArrayInWithIndexedProto.js
rename to js/src/jit-test/tests/basic/testArrayInWithIndexedProto.js
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testBug614653.js
@@ -0,0 +1,10 @@
+/* Bug 614653 - This test .2 seconds with the fix, 20 minutes without. */
+for (var i = 0; i < 100; ++i) {
+    var arr = [];
+    var s = "abcdefghijklmnop";
+    for (var i = 0; i < 50000; ++i) {
+        s = "<" + s + ">";
+        arr.push(s);
+    }
+    gc();
+}
--- a/js/src/jsapi-tests/testXDR.cpp
+++ b/js/src/jsapi-tests/testXDR.cpp
@@ -90,41 +90,8 @@ BEGIN_TEST(testXDR_bug516827)
     CHECK(scrobj);
     v = OBJECT_TO_JSVAL(scrobj);
 
     // execute with null result meaning no result wanted
     CHECK(JS_ExecuteScript(cx, global, script, NULL));
     return true;
 }
 END_TEST(testXDR_bug516827)
-
-BEGIN_TEST(testXDR_bug525481)
-{
-    // get the empty script const singleton
-    JSScript *script = JSScript::emptyScript();
-    CHECK(script);
-
-    // freeze with junk after the empty script shorthand
-    JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
-    CHECK(w);
-    CHECK(JS_XDRScript(w, &script));
-    const char s[] = "don't decode me; don't encode me";
-    char b[sizeof s - 1];
-    memcpy(b, s, sizeof b);
-    CHECK(JS_XDRBytes(w, b, sizeof b));
-    uint32 nbytes;
-    void *p = JS_XDRMemGetData(w, &nbytes);
-    CHECK(p);
-    void *frozen = JS_malloc(cx, nbytes);
-    CHECK(frozen);
-    memcpy(frozen, p, nbytes);
-    JS_XDRDestroy(w);
-
-    // thaw, reading junk if bug 525481 is not patched
-    script = NULL;
-    JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
-    JS_XDRMemSetData(r, frozen, nbytes);
-    CHECK(JS_XDRScript(r, &script));
-    JS_DestroyScript(cx, script);
-    JS_XDRDestroy(r);  // this frees `frozen`
-    return true;
-}
-END_TEST(testXDR_bug525481)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4018,17 +4018,17 @@ JS_SetReservedSlot(JSContext *cx, JSObje
 
 JS_PUBLIC_API(JSObject *)
 JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     CHECK_REQUEST(cx);
     /* NB: jsuint cast does ToUint32. */
     assertSameCompartment(cx, JSValueArray(vector, vector ? (jsuint)length : 0));
-    return js_NewArrayObject(cx, (jsuint)length, Valueify(vector));
+    return NewDenseCopiedArray(cx, (jsuint)length, Valueify(vector));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsArrayObject(JSContext *cx, JSObject *obj)
 {
     assertSameCompartment(cx, obj);
     return obj->isArray() ||
            (obj->isWrapper() && JSWrapper::wrappedObject(obj)->isArray());
@@ -4667,17 +4667,16 @@ JS_NewScriptObject(JSContext *cx, JSScri
         return NewNonFunction<WithProto::Class>(cx, &js_ScriptClass, NULL, NULL);
 
     /*
      * This function should only ever be applied to JSScripts that had
      * script objects allocated for them when they were created, as
      * described in the comment for JSScript::u.object.
      */
     JS_ASSERT(script->u.object);
-    JS_ASSERT(script != JSScript::emptyScript());
     return script->u.object;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetScriptObject(JSScript *script)
 {
     /*
      * This function should only ever be applied to JSScripts that had
@@ -4884,17 +4883,17 @@ JS_PUBLIC_API(JSBool)
 JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment);
     JSBool ok;
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, script);
     /* This should receive only scripts handed out via the JSAPI. */
-    JS_ASSERT(script == JSScript::emptyScript() || script->u.object);
+    JS_ASSERT(script->u.object);
     ok = Execute(cx, obj, script, NULL, 0, Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ExecuteScriptVersion(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval,
                         JSVersion version)
@@ -5161,42 +5160,16 @@ JS_RestoreFrameChain(JSContext *cx, JSSt
     JS_ASSERT(!cx->hasfp());
     if (!fp)
         return;
     cx->restoreSegment();
     cx->resetCompartment();
 }
 
 /************************************************************************/
-
-JS_PUBLIC_API(JSString *)
-JS_NewString(JSContext *cx, char *bytes, size_t nbytes)
-{
-    size_t length = nbytes;
-    jschar *chars;
-    JSString *str;
-
-    CHECK_REQUEST(cx);
-
-    /* Make a UTF-16 vector from the 8-bit char codes in bytes. */
-    chars = js_InflateString(cx, bytes, &length);
-    if (!chars)
-        return NULL;
-
-    /* Free chars (but not bytes, which caller frees on error) if we fail. */
-    str = js_NewString(cx, chars, length);
-    if (!str) {
-        cx->free(chars);
-        return NULL;
-    }
-
-    js_free(bytes);
-    return str;
-}
-
 JS_PUBLIC_API(JSString *)
 JS_NewStringCopyN(JSContext *cx, const char *s, size_t n)
 {
     jschar *js;
     JSString *str;
 
     CHECK_REQUEST(cx);
     js = js_InflateString(cx, s, &n);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1330,118 +1330,105 @@ namespace js {
  *     ... do stuff with ch, but no uses of str ...;
  *   }
  *
  * This simply ensures that |str| will be live until |a_str| goes out of scope.
  * As long as we don't retain a pointer to the string's characters for longer
  * than that, we have avoided all garbage collection hazards.
  */
 template<typename T> class AnchorPermitted;
+template<> class AnchorPermitted<JSObject *> { };
+template<> class AnchorPermitted<const JSObject *> { };
+template<> class AnchorPermitted<JSFunction *> { };
+template<> class AnchorPermitted<const JSFunction *> { };
+template<> class AnchorPermitted<JSString *> { };
+template<> class AnchorPermitted<const JSString *> { };
+template<> class AnchorPermitted<jsval> { };
+
 template<typename T>
 class Anchor: AnchorPermitted<T> {
   public:
     Anchor() { }
     explicit Anchor(T t) { hold = t; }
-    ~Anchor() {
-#ifdef __GNUC__
-        /* 
-         * No code is generated for this. But because this is marked 'volatile', G++ will
-         * assume it has important side-effects, and won't delete it. (G++ never looks at
-         * the actual text and notices it's empty.) And because we have passed |hold| to
-         * it, GCC will keep |hold| alive until this point.
-         *
-         * The "memory" clobber operand ensures that G++ will not move prior memory
-         * accesses after the asm --- it's a barrier. Unfortunately, it also means that
-         * G++ will assume that all memory has changed after the asm, as it would for a
-         * call to an unknown function. I don't know of a way to avoid that consequence.
-         */
-        asm volatile("":: "g" (hold) : "memory");
-#else
-        /*
-         * An adequate portable substitute.
-         *
-         * The compiler promises that, by the end of an expression statement, the
-         * last-stored value to a volatile object is the same as it would be in an
-         * unoptimized, direct implementation (the "abstract machine" whose behavior the
-         * language spec describes). However, the compiler is still free to reorder
-         * non-volatile accesses across this store --- which is what we must prevent. So
-         * assigning the held value to a volatile variable, as we do here, is not enough.
-         *
-         * In our case, however, garbage collection only occurs at function calls, so it
-         * is sufficient to ensure that the destructor's store isn't moved earlier across
-         * any function calls that could collect. It is hard to imagine the compiler
-         * analyzing the program so thoroughly that it could prove that such motion was
-         * safe. In practice, compilers treat calls to the collector as opaque operations
-         * --- in particular, as operations which could access volatile variables, across
-         * which this destructor must not be moved.
-         *
-         * ("Objection, your honor!  *Alleged* killer whale!")
-         *
-         * The disadvantage of this approach is that it does generate code for the store.
-         * We do need to use Anchors in some cases where cycles are tight.
-         */
-        volatile T sink;
-#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES
-        /*
-         * Can't just do a simple assignment here.
-         */
-        doAssignment(sink, hold);
-#else
-        sink = hold;
-#endif
-#endif
-    }
-    T &get()      { return hold; }
-    void set(T t) { hold = t; }
-    void clear()  { hold = 0; }
+    inline ~Anchor();
+    T &get() { return hold; }
+    void set(const T &t) { hold = t; }
+    void clear() { hold = 0; }
   private:
     T hold;
     /* Anchors should not be assigned or passed to functions. */
     Anchor(const Anchor &);
     const Anchor &operator=(const Anchor &);
 };
 
+#ifdef __GNUC__
+template<typename T>
+inline Anchor<T>::~Anchor() {
+    /* 
+     * No code is generated for this. But because this is marked 'volatile', G++ will
+     * assume it has important side-effects, and won't delete it. (G++ never looks at
+     * the actual text and notices it's empty.) And because we have passed |hold| to
+     * it, GCC will keep |hold| alive until this point.
+     *
+     * The "memory" clobber operand ensures that G++ will not move prior memory
+     * accesses after the asm --- it's a barrier. Unfortunately, it also means that
+     * G++ will assume that all memory has changed after the asm, as it would for a
+     * call to an unknown function. I don't know of a way to avoid that consequence.
+     */
+    asm volatile("":: "g" (hold) : "memory");
+}
+#else
+template<typename T>
+inline Anchor<T>::~Anchor() {
+    /*
+     * An adequate portable substitute, for non-structure types.
+     *
+     * The compiler promises that, by the end of an expression statement, the
+     * last-stored value to a volatile object is the same as it would be in an
+     * unoptimized, direct implementation (the "abstract machine" whose behavior the
+     * language spec describes). However, the compiler is still free to reorder
+     * non-volatile accesses across this store --- which is what we must prevent. So
+     * assigning the held value to a volatile variable, as we do here, is not enough.
+     *
+     * In our case, however, garbage collection only occurs at function calls, so it
+     * is sufficient to ensure that the destructor's store isn't moved earlier across
+     * any function calls that could collect. It is hard to imagine the compiler
+     * analyzing the program so thoroughly that it could prove that such motion was
+     * safe. In practice, compilers treat calls to the collector as opaque operations
+     * --- in particular, as operations which could access volatile variables, across
+     * which this destructor must not be moved.
+     *
+     * ("Objection, your honor!  *Alleged* killer whale!")
+     *
+     * The disadvantage of this approach is that it does generate code for the store.
+     * We do need to use Anchors in some cases where cycles are tight.
+     */
+    volatile T sink;
+    sink = hold;
+}
+
+#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES
 /*
- * Ensure that attempts to create Anchors for types the garbage collector's conservative
- * scanner doesn't actually recgonize fail. Such anchors would have no effect.
+ * The default assignment operator for |struct C| has the signature:
+ *
+ *   C& C::operator=(const C&)
+ *
+ * And in particular requires implicit conversion of |this| to type |C| for the return
+ * value. But |volatile C| cannot thus be converted to |C|, so just doing |sink = hold| as
+ * in the non-specialized version would fail to compile. Do the assignment on asBits
+ * instead, since I don't think we want to give jsval_layout an assignment operator
+ * returning |volatile jsval_layout|.
  */
-class Anchor_base {
-protected:
-#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES
-    template<typename T> void doAssignment(volatile T &lhs, const T &rhs) {
-        lhs = rhs;
-    }
+template<>
+inline Anchor<jsval>::~Anchor() {
+    volatile jsval sink;
+    sink.asBits = hold.asBits;
+}
 #endif
-};
-template<> class AnchorPermitted<JSObject *> : protected Anchor_base { };
-template<> class AnchorPermitted<const JSObject *> : protected Anchor_base { };
-template<> class AnchorPermitted<JSFunction *> : protected Anchor_base { };
-template<> class AnchorPermitted<const JSFunction *> : protected Anchor_base { };
-template<> class AnchorPermitted<JSString *> : protected Anchor_base { };
-template<> class AnchorPermitted<const JSString *> : protected Anchor_base { };
-template<> class AnchorPermitted<jsval> : protected Anchor_base {
-protected:
-#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES
-    void doAssignment(volatile jsval &lhs, const jsval &rhs) {
-        /*
-         * The default assignment operator for |struct C| has the signature:
-         *
-         *   C& C::operator=(const C&)
-         *
-         * And in particular requires implicit conversion of |this| to
-         * type |C| for the return value.  But |volatile C| cannot
-         * thus be converted to |C|, so just doing |sink = hold| here
-         * would fail to compile.  Do the assignment on asBits
-         * instead, since I don't think we want to give jsval_layout
-         * an assignment operator returning |volatile jsval_layout|.
-         */
-        lhs.asBits = rhs.asBits;
-    }
 #endif
-};
 
 }  /* namespace js */
 
 JS_BEGIN_EXTERN_C
 #endif
 
 /*
  * This symbol may be used by embedders to detect the change from the old
@@ -2927,26 +2914,23 @@ JS_SaveFrameChain(JSContext *cx);
 extern JS_PUBLIC_API(void)
 JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp);
 
 /************************************************************************/
 
 /*
  * Strings.
  *
- * NB: JS_NewString takes ownership of bytes on success, avoiding a copy; but
- * on error (signified by null return), it leaves bytes owned by the caller.
- * So the caller must free bytes in the error case, if it has no use for them.
- * In contrast, all the JS_New*StringCopy* functions do not take ownership of
- * the character memory passed to them -- they copy it.
+ * NB: JS_NewUCString takes ownership of bytes on success, avoiding a copy;
+ * but on error (signified by null return), it leaves chars owned by the
+ * caller. So the caller must free bytes in the error case, if it has no use
+ * for them. In contrast, all the JS_New*StringCopy* functions do not take
+ * ownership of the character memory passed to them -- they copy it.
  */
 extern JS_PUBLIC_API(JSString *)
-JS_NewString(JSContext *cx, char *bytes, size_t length);
-
-extern JS_PUBLIC_API(JSString *)
 JS_NewStringCopyN(JSContext *cx, const char *s, size_t n);
 
 extern JS_PUBLIC_API(JSString *)
 JS_NewStringCopyZ(JSContext *cx, const char *s);
 
 extern JS_PUBLIC_API(JSString *)
 JS_InternString(JSContext *cx, const char *s);
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -47,23 +47,25 @@
  * &js_ArrayClass, and can then directly manipulate the slots for efficiency.
  *
  * We track these pieces of metadata for arrays in dense mode:
  *  - The array's length property as a uint32, accessible with
  *    getArrayLength(), setArrayLength().
  *  - The number of element slots (capacity), gettable with
  *    getDenseArrayCapacity().
  *
- * In dense mode, holes in the array are represented by (JS_ARRAY_HOLE) invalid
- * values.  The final slot in fslots is unused.
+ * In dense mode, holes in the array are represented by
+ * MagicValue(JS_ARRAY_HOLE) invalid values.
  *
  * NB: the capacity and length of a dense array are entirely unrelated!  The
- * length may be greater than, less than, or equal to the capacity.  See
- * array_length_setter for an explanation of how the first, most surprising
- * case may occur.
+ * length may be greater than, less than, or equal to the capacity. The first
+ * case may occur when the user writes "new Array(100), in which case the
+ * length is 100 while the capacity remains 0 (indices below length and above
+ * capaicty must be treated as holes). See array_length_setter for another
+ * explanation of how the first case may occur.
  *
  * Arrays are converted to use js_SlowArrayClass when any of these conditions
  * are met:
  *  - there are more than MIN_SPARSE_INDEX slots total
  *  - the load factor (COUNT / capacity) is less than 0.25
  *  - a property is set that is not indexed (and not "length")
  *  - a property is defined that has non-default property attributes.
  *
@@ -165,44 +167,48 @@ js_StringIsIndex(JSString *str, jsuint *
         {
             *indexp = index;
             return JS_TRUE;
         }
     }
     return JS_FALSE;
 }
 
-static jsuint
-ValueIsLength(JSContext *cx, Value* vp)
+static bool 
+ValueToLength(JSContext *cx, Value* vp, jsuint* plength)
 {
     if (vp->isInt32()) {
         int32_t i = vp->toInt32();
         if (i < 0)
             goto error;
-        return (jsuint) i;
+
+        *plength = (jsuint)(i);
+        return true;
     }
 
     jsdouble d;
     if (!ValueToNumber(cx, *vp, &d))
         goto error;
 
     if (JSDOUBLE_IS_NaN(d))
         goto error;
+
     jsuint length;
     length = (jsuint) d;
     if (d != (jsdouble) length)
         goto error;
-    vp->setNumber(length);
-    return length;
-
-  error:
+
+
+    *plength = length;
+    return true;
+
+error:
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                          JSMSG_BAD_ARRAY_LENGTH);
-    vp->setNull();
-    return 0;
+    return false;
 }
 
 JSBool
 js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
 {
     if (obj->isArray()) {
         *lengthp = obj->getArrayLength();
         return true;
@@ -552,18 +558,20 @@ js_HasLengthProperty(JSContext *cx, JSOb
     JSErrorReporter older = JS_SetErrorReporter(cx, NULL);
     AutoValueRooter tvr(cx);
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
     JSBool ok = obj->getProperty(cx, id, tvr.addr());
     JS_SetErrorReporter(cx, older);
     if (!ok)
         return false;
 
-    *lengthp = ValueIsLength(cx, tvr.addr());
-    return !tvr.value().isNull();
+    if (!ValueToLength(cx, tvr.addr(), lengthp))
+        return false;
+
+    return true;
 }
 
 /*
  * Since SpiderMonkey supports cross-class prototype-based delegation, we have
  * to be careful about the length getter and setter being called on an object
  * not of Array class. For the getter, we search obj's prototype chain for the
  * array that caused this getter to be invoked. In the setter case to overcome
  * the JSPROP_SHARED attribute, we must define a shadowing length property.
@@ -576,37 +584,30 @@ array_length_getter(JSContext *cx, JSObj
             vp->setNumber(obj->getArrayLength());
             return JS_TRUE;
         }
     } while ((obj = obj->getProto()) != NULL);
     return JS_TRUE;
 }
 
 static JSBool
-array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
+array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     jsuint newlen, oldlen, gap, index;
     Value junk;
 
-    /* Check for a sealed object first. */
-    if (!obj->isExtensible()) {
-        return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_READ_ONLY,
-                                        JSDVG_IGNORE_STACK, IdToValue(id), NULL,
-                                        NULL, NULL);
-    }
-
     if (!obj->isArray()) {
         jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
 
         return obj->defineProperty(cx, lengthId, *vp, NULL, NULL, JSPROP_ENUMERATE);
     }
 
-    newlen = ValueIsLength(cx, vp);
-    if (vp->isNull())
+    if (!ValueToLength(cx, vp, &newlen))
         return false;
+
     oldlen = obj->getArrayLength();
 
     if (oldlen == newlen)
         return true;
 
     vp->setNumber(newlen);
     if (oldlen < newlen) {
         obj->setArrayLength(newlen);
@@ -628,18 +629,16 @@ array_length_setter(JSContext *cx, JSObj
         do {
             --oldlen;
             if (!JS_CHECK_OPERATION_LIMIT(cx)) {
                 obj->setArrayLength(oldlen + 1);
                 return false;
             }
             if (!DeleteArrayElement(cx, obj, oldlen, true)) {
                 obj->setArrayLength(oldlen + 1);
-                if (strict)
-                    return false;
                 JS_ClearPendingException(cx);
                 return true;
             }
         } while (oldlen != newlen);
         obj->setArrayLength(newlen);
     } else {
         /*
          * We are going to remove a lot of indexes in a presumably sparse
@@ -793,17 +792,17 @@ array_typeOf(JSContext *cx, JSObject *ob
 }
 
 static JSBool
 array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
 {
     uint32 i;
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
-        return array_length_setter(cx, obj, id, vp, strict);
+        return array_length_setter(cx, obj, id, vp);
 
     if (!obj->isDenseArray())
         return js_SetProperty(cx, obj, id, vp, strict);
 
     do {
         if (!js_IdIsIndex(id, &i))
             break;
         if (js_PrototypeHasIndexedProperties(cx, obj))
@@ -823,27 +822,16 @@ array_setProperty(JSContext *cx, JSObjec
         return true;
     } while (false);
 
     if (!obj->makeDenseArraySlow(cx))
         return false;
     return js_SetProperty(cx, obj, id, vp, strict);
 }
 
-static JSBool
-slowarray_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
-{
-    JS_ASSERT(obj->isSlowArray());
-
-    if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
-        return array_length_setter(cx, obj, id, vp, strict);
-
-    return js_SetProperty(cx, obj, id, vp, strict);
-}
-
 JSBool
 js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj)
 {
     /*
      * Walk up the prototype chain and see if this indexed element already
      * exists. If we hit the end of the prototype chain, it's safe to set the
      * element on the original object.
      */
@@ -991,44 +979,17 @@ Class js_SlowArrayClass = {
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
     slowarray_addProperty,
     PropertyStub,   /* delProperty */
     PropertyStub,   /* getProperty */
     PropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
-    js_TryValueOf,
-    NULL,           /* finalize    */
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    NULL,           /* mark        */
-    JS_NULL_CLASS_EXT,
-    {
-        NULL,       /* lookupProperty   */
-        NULL,       /* defineProperty   */
-        NULL,       /* getProperty      */
-        /*
-         * For assignments to 'length', we need to know the setter's strictness. A property's
-         * setter isn't passed that, but the ObjectOps member is, so use that.
-         */
-        slowarray_setProperty,
-        NULL,       /* getAttributes    */
-        NULL,       /* setAttributes    */
-        NULL,       /* deleteProperty   */
-        NULL,       /* enumerate        */
-        NULL,       /* typeOf           */
-        NULL,       /* trace            */
-        NULL,       /* thisObject       */
-        NULL,       /* clear            */
-    }
+    js_TryValueOf
 };
 
 /*
  * Convert an array object from fast-and-dense to slow-and-flexible.
  */
 JSBool
 JSObject::makeDenseArraySlow(JSContext *cx)
 {
@@ -1048,17 +1009,17 @@ JSObject::makeDenseArraySlow(JSContext *
 
     uint32 capacity = getDenseArrayCapacity();
 
     /*
      * Begin with the length property to share more of the property tree.
      * The getter/setter here will directly access the object's private value.
      */
     if (!addProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
-                     array_length_getter, NULL,
+                     array_length_getter, array_length_setter,
                      SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0)) {
         setMap(oldMap);
         return false;
     }
 
     /*
      * Create new properties pointing to existing elements. Pack the array to
      * remove holes, so that shapes use successive slots (as for other objects).
@@ -2280,17 +2241,17 @@ array_splice(JSContext *cx, uintN argc, 
     JSBool hole;
 
     /*
      * Create a new array value to return.  Our ECMA v2 proposal specs
      * that splice always returns an array value, even when given no
      * arguments.  We think this is best because it eliminates the need
      * for callers to do an extra test to handle the empty splice case.
      */
-    JSObject *obj2 = js_NewArrayObject(cx, 0, NULL);
+    JSObject *obj2 = NewDenseEmptyArray(cx);
     if (!obj2)
         return JS_FALSE;
     vp->setObject(*obj2);
 
     /* Nothing to do if no args.  Otherwise get length. */
     if (argc == 0)
         return JS_TRUE;
     Value *argv = JS_ARGV(cx, vp);
@@ -2334,17 +2295,16 @@ array_splice(JSContext *cx, uintN argc, 
         argv++;
     }
 
     AutoValueRooter tvr(cx);
 
     /* If there are elements to remove, put them into the return value. */
     if (count > 0) {
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
-            !js_PrototypeHasIndexedProperties(cx, obj2) &&
             end <= obj->getDenseArrayCapacity()) {
             if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin))
                 return JS_FALSE;
         } else {
             for (last = begin; last < end; last++) {
                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                     !GetElement(cx, obj, last, &hole, tvr.addr())) {
                     return JS_FALSE;
@@ -2454,27 +2414,27 @@ array_concat(JSContext *cx, uintN argc, 
         /*
          * Clone aobj but pass the minimum of its length and capacity, to
          * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In
          * the normal case where length is <= capacity, nobj and aobj will have
          * the same capacity.
          */
         length = aobj->getArrayLength();
         jsuint capacity = aobj->getDenseArrayCapacity();
-        nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements());
+        nobj = NewDenseCopiedArray(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements());
         if (!nobj)
             return JS_FALSE;
         nobj->setArrayLength(length);
         vp->setObject(*nobj);
         if (argc == 0)
             return JS_TRUE;
         argc--;
         p++;
     } else {
-        nobj = js_NewArrayObject(cx, 0, NULL);
+        nobj = NewDenseEmptyArray(cx);
         if (!nobj)
             return JS_FALSE;
         vp->setObject(*nobj);
         length = 0;
     }
 
     AutoValueRooter tvr(cx);
 
@@ -2485,18 +2445,18 @@ array_concat(JSContext *cx, uintN argc, 
         const Value &v = p[i];
         if (v.isObject()) {
             aobj = &v.toObject();
             if (aobj->isArray() ||
                 (aobj->isWrapper() && JSWrapper::wrappedObject(aobj)->isArray())) {
                 jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
                 if (!aobj->getProperty(cx, id, tvr.addr()))
                     return false;
-                jsuint alength = ValueIsLength(cx, tvr.addr());
-                if (tvr.value().isNull())
+                jsuint alength;
+                if (!ValueToLength(cx, tvr.addr(), &alength))
                     return false;
                 for (jsuint slot = 0; slot < alength; slot++) {
                     JSBool hole;
                     if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                         !GetElement(cx, aobj, slot, &hole, tvr.addr())) {
                         return false;
                     }
 
@@ -2567,40 +2527,40 @@ array_slice(JSContext *cx, uintN argc, V
         }
     }
 
     if (begin > end)
         begin = end;
 
     if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() &&
         !js_PrototypeHasIndexedProperties(cx, obj)) {
-        nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin);
+        nobj = NewDenseCopiedArray(cx, end - begin, obj->getDenseArrayElements() + begin);
         if (!nobj)
             return JS_FALSE;
         vp->setObject(*nobj);
         return JS_TRUE;
     }
 
     /* Create a new Array object and root it using *vp. */
-    nobj = js_NewArrayObject(cx, 0, NULL);
+    nobj = NewDenseAllocatedArray(cx, end - begin);
     if (!nobj)
         return JS_FALSE;
     vp->setObject(*nobj);
 
     AutoValueRooter tvr(cx);
     for (slot = begin; slot < end; slot++) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetElement(cx, obj, slot, &hole, tvr.addr())) {
             return JS_FALSE;
         }
         if (!hole && !SetArrayElement(cx, nobj, slot - begin, tvr.value()))
             return JS_FALSE;
     }
 
-    return js_SetLengthProperty(cx, nobj, end - begin);
+    return JS_TRUE;
 }
 
 #if JS_HAS_ARRAY_EXTRAS
 
 static JSBool
 array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp)
 {
     jsuint length, i, stop;
@@ -2752,17 +2712,17 @@ array_extra(JSContext *cx, ArrayExtraMod
                                      JSMSG_EMPTY_ARRAY_REDUCE);
                 return JS_FALSE;
             }
         }
         break;
       case MAP:
       case FILTER:
         newlen = (mode == MAP) ? length : 0;
-        newarr = js_NewArrayObject(cx, newlen, NULL);
+        newarr = NewDenseAllocatedArray(cx, newlen);
         if (!newarr)
             return JS_FALSE;
         vp->setObject(*newarr);
         break;
       case SOME:
         vp->setBoolean(false);
         break;
       case EVERY:
@@ -2959,95 +2919,40 @@ static JSFunctionSpec array_methods[] = 
     JS_FS_END
 };
 
 static JSFunctionSpec array_static_methods[] = {
     JS_FN("isArray",            array_isArray,      1,0),
     JS_FS_END
 };
 
-/* The count here is a guess for the final capacity. */
-static inline JSObject *
-NewDenseArrayObject(JSContext *cx, jsuint count)
-{
-    gc::FinalizeKind kind = GuessObjectGCKind(count, true);
-    return NewNonFunction<WithProto::Class>(cx, &js_ArrayClass, NULL, NULL, kind);
-}
-
 JSBool
 js_Array(JSContext *cx, uintN argc, Value *vp)
 {
-    jsuint length;
-    const Value *vector;
+    JSObject *obj;
 
     if (argc == 0) {
-        length = 0;
-        vector = NULL;
+        obj = NewDenseEmptyArray(cx);
     } else if (argc > 1) {
-        length = (jsuint) argc;
-        vector = vp + 2;
+        obj = NewDenseCopiedArray(cx, argc, vp + 2);
     } else if (!vp[2].isNumber()) {
-        length = 1;
-        vector = vp + 2;
+        obj = NewDenseCopiedArray(cx, 1, vp + 2);
     } else {
-        length = ValueIsLength(cx, vp + 2);
-        if (vp[2].isNull())
+        jsuint length;
+        if (!ValueToLength(cx, vp + 2, &length))
             return JS_FALSE;
-        vector = NULL;
+        obj = NewDenseUnallocatedArray(cx, length);
     }
 
-    /* Whether called with 'new' or not, use a new Array object. */
-    JSObject *obj = NewDenseArrayObject(cx, length);
     if (!obj)
         return JS_FALSE;
     vp->setObject(*obj);
 
-    return InitArrayObject(cx, obj, length, vector);
-}
-
-JSObject* JS_FASTCALL
-js_NewEmptyArray(JSContext* cx, JSObject* proto, int32 len)
-{
-    if (len < 0)
-        return NULL;
-
-    JS_ASSERT(proto->isArray());
-
-    gc::FinalizeKind kind = GuessObjectGCKind(len, true);
-    JSObject* obj = js_NewGCObject(cx, kind);
-    if (!obj)
-        return NULL;
-
-    /* Initialize all fields of JSObject. */
-    obj->init(cx, &js_ArrayClass, proto, proto->getParent(),
-              (void*) len, true);
-    obj->setSharedNonNativeMap();
-    return obj;
+    return JS_TRUE;
 }
-#ifdef JS_TRACER
-JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, INT32, 0,
-                     nanojit::ACCSET_STORE_ANY)
-#endif
-
-JSObject* JS_FASTCALL
-js_NewPreallocatedArray(JSContext* cx, JSObject* proto, int32 len)
-{
-    JSObject *obj = js_NewEmptyArray(cx, proto, len);
-    if (!obj)
-        return NULL;
-
-    /* Avoid ensureDenseArrayElements to skip sparse array checks there. */
-    if (!obj->ensureSlots(cx, len))     
-        return NULL;
-    return obj;
-}
-#ifdef JS_TRACER
-JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewPreallocatedArray, CONTEXT, OBJECT, INT32,
-                     0, nanojit::ACCSET_STORE_ANY)
-#endif
 
 JSObject *
 js_InitArrayClass(JSContext *cx, JSObject *obj)
 {
     JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1,
                                    NULL, array_methods, NULL, array_static_methods);
     if (!proto)
         return NULL;
@@ -3057,41 +2962,93 @@ js_InitArrayClass(JSContext *cx, JSObjec
      * class for proto's emptyShape class.
      */
     JS_ASSERT(proto->emptyShapes && proto->emptyShapes[0]->getClass() == proto->getClass());
 
     proto->setArrayLength(0);
     return proto;
 }
 
-JSObject *
-js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector)
+/*
+ * Array allocation functions.
+ */
+namespace js {
+
+template<bool allocateCapacity>
+static JS_ALWAYS_INLINE JSObject *
+NewArray(JSContext *cx, jsuint length, JSObject *proto)
 {
-    JSObject *obj = NewDenseArrayObject(cx, length);
-    if (!obj)
+    JS_ASSERT_IF(proto, proto->isArray());
+
+    gc::FinalizeKind kind = GuessObjectGCKind(length, true);
+    JSObject *obj = detail::NewObject<WithProto::Class, false>(cx, &js_ArrayClass, proto, NULL, kind);
+
+    obj->setArrayLength(length);
+
+    if (allocateCapacity && !obj->ensureSlots(cx, length))
         return NULL;
 
-    /*
-     * If this fails, the global object was not initialized and its class does
-     * not have JSCLASS_IS_GLOBAL.
-     */
-    JS_ASSERT(obj->getProto());
-
-    return InitArrayObject(cx, obj, length, vector) ? obj : NULL;
+    return obj;
+}
+
+JSObject * JS_FASTCALL
+NewDenseEmptyArray(JSContext *cx, JSObject *proto)
+{
+    return NewArray<false>(cx, 0, proto);
+}
+
+JSObject * JS_FASTCALL
+NewDenseAllocatedArray(JSContext *cx, uint32 length, JSObject *proto)
+{
+    return NewArray<true>(cx, length, proto);
+}
+
+JSObject * JS_FASTCALL
+NewDenseUnallocatedArray(JSContext *cx, uint32 length, JSObject *proto)
+{
+    return NewArray<false>(cx, length, proto);
 }
 
 JSObject *
-js_NewSlowArrayObject(JSContext *cx)
+NewDenseCopiedArray(JSContext *cx, uintN length, Value *vp, JSObject *proto)
+{
+    JSObject* obj = NewArray<true>(cx, length, proto);
+    JS_ASSERT(obj->getDenseArrayCapacity() >= length);
+
+    if (vp)
+        memcpy(obj->getDenseArrayElements(), vp, length * sizeof(Value));
+
+    return obj;
+}
+
+#ifdef JS_TRACER
+JS_DEFINE_CALLINFO_2(extern, OBJECT, NewDenseEmptyArray, CONTEXT, OBJECT, 0,
+                     nanojit::ACCSET_STORE_ANY)
+JS_DEFINE_CALLINFO_3(extern, OBJECT, NewDenseAllocatedArray, CONTEXT, UINT32, OBJECT, 0,
+                     nanojit::ACCSET_STORE_ANY)
+JS_DEFINE_CALLINFO_3(extern, OBJECT, NewDenseUnallocatedArray, CONTEXT, UINT32, OBJECT, 0,
+                     nanojit::ACCSET_STORE_ANY)
+#endif
+
+
+
+JSObject *
+NewSlowEmptyArray(JSContext *cx)
 {
     JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_SlowArrayClass, NULL, NULL);
-    if (obj)
-        obj->setArrayLength(0);
+    if (!obj)
+        return NULL;
+
+    obj->setArrayLength(0);
     return obj;
 }
 
+}
+
+
 #ifdef DEBUG
 JSBool
 js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp)
 {
     uintN i;
     JSObject *array;
 
     for (i = 0; i < argc; i++) {
@@ -3236,15 +3193,16 @@ js_CloneDensePrimitiveArray(JSContext *c
              */
             *clone = NULL;
             return JS_TRUE;
         }
 
         vector.append(val);
     }
 
-    *clone = js_NewArrayObject(cx, jsvalCount, vector.begin());
+    *clone = NewDenseCopiedArray(cx, jsvalCount, vector.begin());
     if (!*clone)
         return JS_FALSE;
+
+    /* The length will be set to the JS_MIN, above, but length might be larger. */
     (*clone)->setArrayLength(length);
-
     return JS_TRUE;
 }
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -172,22 +172,43 @@ js_GetProtoIfDenseArray(JSObject *obj)
 }
 
 extern JSObject *
 js_InitArrayClass(JSContext *cx, JSObject *obj);
 
 extern bool
 js_InitContextBusyArrayTable(JSContext *cx);
 
-extern JSObject *
-js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector);
+namespace js
+{
+
+/* Create a dense array with no capacity allocated, length set to 0. */
+extern JSObject * JS_FASTCALL
+NewDenseEmptyArray(JSContext *cx, JSObject *proto=NULL);
+
+/* Create a dense array with length and capacity == 'length'. */
+extern JSObject * JS_FASTCALL
+NewDenseAllocatedArray(JSContext *cx, uint length, JSObject *proto=NULL);
 
-/* Create an array object that starts out already made slow/sparse. */
+/*
+ * Create a dense array with a set length, but without allocating space for the
+ * contents. This is useful, e.g., when accepting length from the user.
+ */
+extern JSObject * JS_FASTCALL
+NewDenseUnallocatedArray(JSContext *cx, uint length, JSObject *proto=NULL);
+
+/* Create a dense array with a copy of vp. */
 extern JSObject *
-js_NewSlowArrayObject(JSContext *cx);
+NewDenseCopiedArray(JSContext *cx, uint length, Value *vp, JSObject *proto=NULL);
+
+/* Create a sparse array. */
+extern JSObject *
+NewSlowEmptyArray(JSContext *cx);
+
+}
 
 extern JSBool
 js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
 
 extern JSBool
 js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length);
 
 extern JSBool
@@ -279,35 +300,16 @@ JSBool
 js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id,
                              js::Value *vp);
 
 /* Array constructor native. Exposed only so the JIT can know its address. */
 JSBool
 js_Array(JSContext *cx, uintN argc, js::Value *vp);
 
 /*
- * Friend api function that allows direct creation of an array object with a
- * given capacity.  Non-null return value means allocation of the internal
- * buffer for a capacity of at least |capacity| succeeded.  A pointer to the
- * first element of this internal buffer is returned in the |vector| out
- * parameter.  The caller promises to fill in the first |capacity| values
- * starting from that pointer immediately after this function returns and
- * without triggering GC (so this method is allowed to leave those
- * uninitialized) and to set them to non-JS_ARRAY_HOLE-magic-why values, so
- * that the resulting array has length and count both equal to |capacity|.
- *
- * FIXME: for some strange reason, when this file is included from
- * dom/ipc/TabParent.cpp in MSVC, jsuint resolves to a slightly different
- * builtin than when mozjs.dll is built, resulting in a link error in xul.dll.
- * It would be useful to find out what is causing this insanity.
- */
-JS_FRIEND_API(JSObject *)
-js_NewArrayObjectWithCapacity(JSContext *cx, uint32_t capacity, jsval **vector);
-
-/*
  * Makes a fast clone of a dense array as long as the array only contains
  * primitive values.
  *
  * If the return value is JS_FALSE then clone will not be set.
  *
  * If the return value is JS_TRUE then clone will either be set to the address
  * of a new JSObject or to NULL if the array was not dense or contained values
  * that were not primitives.
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -570,18 +570,21 @@ js_dmod(jsdouble a, jsdouble b);
 #define JS_DEFINE_TRCINFO_1(name, tn0)
 #define JS_DEFINE_TRCINFO_2(name, tn0, tn1)
 #define JS_DEFINE_TRCINFO_3(name, tn0, tn1, tn2)
 #define JS_DEFINE_TRCINFO_4(name, tn0, tn1, tn2, tn3)
 
 #endif /* !JS_TRACER */
 
 /* Defined in jsarray.cpp. */
-JS_DECLARE_CALLINFO(js_NewEmptyArray)
-JS_DECLARE_CALLINFO(js_NewPreallocatedArray)
+namespace js {
+JS_DECLARE_CALLINFO(NewDenseEmptyArray)
+JS_DECLARE_CALLINFO(NewDenseAllocatedArray)
+JS_DECLARE_CALLINFO(NewDenseUnallocatedArray)
+}
 JS_DECLARE_CALLINFO(js_ArrayCompPush_tn)
 JS_DECLARE_CALLINFO(js_EnsureDenseArrayCapacity)
 
 /* Defined in jsbuiltins.cpp. */
 JS_DECLARE_CALLINFO(js_UnboxDouble)
 JS_DECLARE_CALLINFO(js_UnboxInt32)
 JS_DECLARE_CALLINFO(js_dmod)
 JS_DECLARE_CALLINFO(js_imod)
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -758,17 +758,17 @@ JSStructuredCloneReader::startRead(Value
             return false;
         vp->setObject(*obj);
         break;
       }
 
       case SCTAG_ARRAY_OBJECT:
       case SCTAG_OBJECT_OBJECT: {
         JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
-                        ? js_NewArrayObject(context(), 0, NULL)
+                        ? NewDenseEmptyArray(context())
                         : NewBuiltinClassInstance(context(), &js_ObjectClass);
         if (!obj || !objs.append(ObjectValue(*obj)))
             return false;
         vp->setObject(*obj);
         break;
       }
 
       case SCTAG_ARRAY_BUFFER_OBJECT:
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -586,17 +586,17 @@ class CompartmentChecker
             for (jsint i = 0; i < ida->length; i++) {
                 if (JSID_IS_OBJECT(ida->vector[i]))
                     check(ida->vector[i]);
             }
         }
     }
 
     void check(JSScript *script) {
-        if (script && script != JSScript::emptyScript()) {
+        if (script) {
             check(script->compartment);
             if (script->u.object)
                 check(script->u.object);
         }
     }
 
     void check(JSStackFrame *fp) {
         check(&fp->scopeChain());
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -260,22 +260,16 @@ JS_SetTrap(JSContext *cx, JSScript *scri
 {
     JSTrap *junk, *trap, *twin;
     JSRuntime *rt;
     uint32 sample;
 
     if (!CheckDebugMode(cx))
         return JS_FALSE;
 
-    if (script == JSScript::emptyScript()) {
-        JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
-                                     NULL, JSMSG_READ_ONLY, "empty script");
-        return JS_FALSE;
-    }
-
     JS_ASSERT((JSOp) *pc != JSOP_TRAP);
     junk = NULL;
     rt = cx->runtime;
     DBG_LOCK(rt);
     trap = FindTrap(rt, script, pc);
     if (trap) {
         JS_ASSERT(trap->script == script && trap->pc == pc);
         JS_ASSERT(*pc == JSOP_TRAP);
@@ -1756,35 +1750,35 @@ JS_GetScriptTotalSize(JSContext *cx, JSS
     if (script->filename)
         nbytes += strlen(script->filename) + 1;
 
     notes = script->notes();
     for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
         continue;
     nbytes += (sn - notes + 1) * sizeof *sn;
 
-    if (script->objectsOffset != 0) {
+    if (JSScript::isValidOffset(script->objectsOffset)) {
         objarray = script->objects();
         i = objarray->length;
         nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
         do {
             nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
         } while (i != 0);
     }
 
-    if (script->regexpsOffset != 0) {
+    if (JSScript::isValidOffset(script->regexpsOffset)) {
         objarray = script->regexps();
         i = objarray->length;
         nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
         do {
             nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
         } while (i != 0);
     }
 
-    if (script->trynotesOffset != 0) {
+    if (JSScript::isValidOffset(script->trynotesOffset)) {
         nbytes += sizeof(JSTryNoteArray) +
             script->trynotes()->length * sizeof(JSTryNote);
     }
 
     principals = script->principals;
     if (principals) {
         JS_ASSERT(principals->refcount);
         pbytes = sizeof *principals;
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -1732,17 +1732,17 @@ LookupCompileTimeConstant(JSContext *cx,
                         *constp = obj->getSlot(shape->slot);
                     }
                 }
 
                 if (shape)
                     break;
             }
         }
-    } while ((cg = (JSCodeGenerator *) cg->parent) != NULL);
+    } while (cg->parent && (cg = cg->parent->asCodeGenerator()));
     return JS_TRUE;
 }
 
 static inline bool
 FitsWithoutBigIndex(uintN index)
 {
     return index < JS_BIT(16);
 }
@@ -2156,31 +2156,25 @@ BindNameToSlot(JSContext *cx, JSCodeGene
 
     /*
      * Turn attempts to mutate const-declared bindings into get ops (for
      * pre-increment and pre-decrement ops, our caller will have to emit
      * JSOP_POS, JSOP_ONE, and JSOP_ADD as well).
      *
      * Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared
      * bindings visible to the compiler are permanent in JS unless the
-     * declaration originates in eval code. We detect eval code by testing
-     * cg->parser->callerFrame, which is set only by eval or a debugger
-     * equivalent.
-     *
-     * Note that this callerFrame non-null test must be qualified by testing
-     * !cg->funbox to exclude function code nested in eval code, which is not
-     * subject to the deletable binding exception.
+     * declaration originates at top level in eval code.
      */
     switch (op) {
       case JSOP_NAME:
       case JSOP_SETCONST:
         break;
       case JSOP_DELNAME:
         if (dn_kind != JSDefinition::UNKNOWN) {
-            if (cg->parser->callerFrame && !cg->funbox)
+            if (cg->parser->callerFrame && dn->isTopLevel())
                 JS_ASSERT(cg->compileAndGo());
             else
                 pn->pn_op = JSOP_FALSE;
             pn->pn_dflags |= PND_BOUND;
             return JS_TRUE;
         }
         break;
       default:
@@ -2348,19 +2342,18 @@ BindNameToSlot(JSContext *cx, JSCodeGene
 #ifdef DEBUG
         JSStackFrame *caller = cg->parser->callerFrame;
 #endif
         JS_ASSERT(caller->isScriptFrame());
 
         JSTreeContext *tc = cg;
         while (tc->staticLevel != level)
             tc = tc->parent;
-        JS_ASSERT(tc->compiling());
-
-        JSCodeGenerator *evalcg = (JSCodeGenerator *) tc;
+
+        JSCodeGenerator *evalcg = tc->asCodeGenerator();
         JS_ASSERT(evalcg->compileAndGo());
         JS_ASSERT(caller->isFunctionFrame());
         JS_ASSERT(cg->parser->callerVarObj == evalcg->scopeChain());
 
         /*
          * Don't generate upvars on the left side of a for loop. See
          * bug 470758 and bug 520513.
          */
@@ -2428,20 +2421,19 @@ BindNameToSlot(JSContext *cx, JSCodeGene
 
             ale = cg->upvarList.add(cg->parser, atom);
             if (!ale)
                 return JS_FALSE;
             index = ALE_INDEX(ale);
             JS_ASSERT(index == cg->upvarList.count - 1);
 
             UpvarCookie *vector = cg->upvarMap.vector;
-            if (!vector) {
-                uint32 length = cg->lexdeps.count;
-
-                vector = (UpvarCookie *) js_calloc(length * sizeof *vector);
+            uint32 length = cg->lexdeps.count;
+            if (!vector || cg->upvarMap.length != length) {
+                vector = (UpvarCookie *) js_realloc(vector, length * sizeof *vector);
                 if (!vector) {
                     JS_ReportOutOfMemory(cx);
                     return JS_FALSE;
                 }
                 cg->upvarMap.vector = vector;
                 cg->upvarMap.length = length;
             }
 
@@ -2450,16 +2442,17 @@ BindNameToSlot(JSContext *cx, JSCodeGene
                 JSTreeContext *tc = cg;
                 do {
                     tc = tc->parent;
                 } while (tc->staticLevel != level);
                 if (tc->inFunction())
                     slot += tc->fun()->nargs;
             }
 
+            JS_ASSERT(index < cg->upvarMap.length);
             vector[index].set(skip, slot);
         }
 
         pn->pn_op = op;
         JS_ASSERT((index & JS_BITMASK(16)) == index);
         pn->pn_cookie.set(0, index);
         pn->pn_dflags |= PND_BOUND;
         return JS_TRUE;
@@ -4596,21 +4589,16 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
              */
             JS_ASSERT(pn->pn_op == JSOP_NOP);
             JS_ASSERT(cg->inFunction());
             if (!EmitFunctionDefNop(cx, cg, pn->pn_index))
                 return JS_FALSE;
             break;
         }
 
-        JS_ASSERT_IF(cx->options & JSOPTION_ANONFUNFIX,
-                     pn->pn_defn ||
-                     (!pn->pn_used && !pn->isTopLevel()) ||
-                     (fun->flags & JSFUN_LAMBDA));
-
         JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT,
                      FUN_KIND(fun) == JSFUN_INTERPRETED);
 
         /* Generate code for the function's body. */
         void *cg2mark = JS_ARENA_MARK(cg->codePool);
         void *cg2space;
         JS_ARENA_ALLOCATE_TYPE(cg2space, JSCodeGenerator, cg->codePool);
         if (!cg2space) {
@@ -4620,17 +4608,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
         JSCodeGenerator *cg2 =
             new (cg2space) JSCodeGenerator(cg->parser,
                                            cg->codePool, cg->notePool,
                                            pn->pn_pos.begin.lineno);
 
         if (!cg2->init())
             return JS_FALSE;
 
-        cg2->flags = pn->pn_funbox->tcflags | TCF_IN_FUNCTION;
+        cg2->flags = pn->pn_funbox->tcflags | TCF_COMPILING | TCF_IN_FUNCTION;
 #if JS_HAS_SHARP_VARS
         if (cg2->flags & TCF_HAS_SHARPS) {
             cg2->sharpSlotBase = fun->sharpSlotBase(cx);
             if (cg2->sharpSlotBase < 0)
                 return JS_FALSE;
         }
 #endif
         cg2->setFunction(fun);
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -359,18 +359,26 @@ struct JSTreeContext {              /* t
                                                         maxScopeDepth));
     }
 
     uintN blockid() { return topStmt ? topStmt->blockid : bodyid; }
 
     JSObject *blockChain() {
         return blockChainBox ? blockChainBox->object : NULL;
     }
-    
-    bool atTopLevel() { return !topStmt || (topStmt->flags & SIF_BODY_BLOCK); }
+
+    /*
+     * True if we are at the topmost level of a entire script or function body.
+     * For example, while parsing this code we would encounter f1 and f2 at
+     * body level, but we would not encounter f3 or f4 at body level:
+     *
+     *   function f1() { function f2() { } }
+     *   if (cond) { function f3() { if (cond) { function f4() { } } } }
+     */
+    bool atBodyLevel() { return !topStmt || (topStmt->flags & SIF_BODY_BLOCK); }
 
     /* Test whether we're in a statement of given type. */
     bool inStatement(JSStmtType type);
 
     bool inStrictMode() const {
         return flags & TCF_STRICT_MODE_CODE;
     }
 
@@ -387,17 +395,19 @@ struct JSTreeContext {              /* t
 
     // Return true there is a generator function within |skip| lexical scopes
     // (going upward) from this context's lexical scope. Always return true if
     // this context is itself a generator.
     bool skipSpansGenerator(unsigned skip);
 
     bool compileAndGo() const { return flags & TCF_COMPILE_N_GO; }
     bool inFunction() const { return flags & TCF_IN_FUNCTION; }
+
     bool compiling() const { return flags & TCF_COMPILING; }
+    inline JSCodeGenerator *asCodeGenerator();
 
     bool usesArguments() const {
         return flags & TCF_FUN_USES_ARGUMENTS;
     }
 
     void noteCallsEval() {
         flags |= TCF_FUN_CALLS_EVAL;
     }
@@ -589,17 +599,17 @@ struct JSCodeGenerator : public JSTreeCo
     JSAtomList      globalMap;      /* per-script map of global name to globalUses vector */
 
     /* Vectors of pn_cookie slot values. */
     typedef js::Vector<uint32, 8, js::ContextAllocPolicy> SlotVector;
     SlotVector      closedArgs;
     SlotVector      closedVars;
 
     uint16          traceIndex;     /* index for the next JSOP_TRACE instruction */
-    
+
     /*
      * Initialize cg to allocate bytecode space from codePool, source note
      * space from notePool, and all other arena-allocated temporaries from
      * parser->context->tempPool.
      */
     JSCodeGenerator(js::Parser *parser,
                     JSArenaPool *codePool, JSArenaPool *notePool,
                     uintN lineno);
@@ -663,16 +673,23 @@ struct JSCodeGenerator : public JSTreeCo
 #define CG_PROLOG_LIMIT(cg)     ((cg)->prolog.limit)
 #define CG_PROLOG_NEXT(cg)      ((cg)->prolog.next)
 #define CG_PROLOG_CODE(cg,poff) (CG_PROLOG_BASE(cg) + (poff))
 #define CG_PROLOG_OFFSET(cg)    (CG_PROLOG_NEXT(cg) - CG_PROLOG_BASE(cg))
 
 #define CG_SWITCH_TO_MAIN(cg)   ((cg)->current = &(cg)->main)
 #define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog)
 
+inline JSCodeGenerator *
+JSTreeContext::asCodeGenerator()
+{
+    JS_ASSERT(compiling());
+    return static_cast<JSCodeGenerator *>(this);
+}
+
 /*
  * Emit one bytecode.
  */
 extern ptrdiff_t
 js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op);
 
 /*
  * Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1).
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -384,56 +384,56 @@ WrapEscapingClosure(JSContext *cx, JSSta
     jssrcnote *sn = snbase;
     while (!SN_IS_TERMINATOR(sn))
         sn = SN_NEXT(sn);
     uintN nsrcnotes = (sn - snbase) + 1;
 
     /* NB: GC must not occur before wscript is homed in wfun->u.i.script. */
     JSScript *wscript = JSScript::NewScript(cx, script->length, nsrcnotes,
                                             script->atomMap.length,
-                                            (script->objectsOffset != 0)
+                                            JSScript::isValidOffset(script->objectsOffset)
                                             ? script->objects()->length
                                             : 0,
                                             fun->u.i.nupvars,
-                                            (script->regexpsOffset != 0)
+                                            JSScript::isValidOffset(script->regexpsOffset)
                                             ? script->regexps()->length
                                             : 0,
-                                            (script->trynotesOffset != 0)
+                                            JSScript::isValidOffset(script->trynotesOffset)
                                             ? script->trynotes()->length
                                             : 0,
-                                            (script->constOffset != 0)
+                                            JSScript::isValidOffset(script->constOffset)
                                             ? script->consts()->length
                                             : 0,
-                                            (script->globalsOffset != 0)
+                                            JSScript::isValidOffset(script->globalsOffset)
                                             ? script->globals()->length
                                             : 0,
                                             script->nClosedArgs,
                                             script->nClosedVars);
     if (!wscript)
         return NULL;
 
     memcpy(wscript->code, script->code, script->length);
     wscript->main = wscript->code + (script->main - script->code);
 
     memcpy(wscript->notes(), snbase, nsrcnotes * sizeof(jssrcnote));
     memcpy(wscript->atomMap.vector, script->atomMap.vector,
            wscript->atomMap.length * sizeof(JSAtom *));
-    if (script->objectsOffset != 0) {
+    if (JSScript::isValidOffset(script->objectsOffset)) {
         memcpy(wscript->objects()->vector, script->objects()->vector,
                wscript->objects()->length * sizeof(JSObject *));
     }
-    if (script->regexpsOffset != 0) {
+    if (JSScript::isValidOffset(script->regexpsOffset)) {
         memcpy(wscript->regexps()->vector, script->regexps()->vector,
                wscript->regexps()->length * sizeof(JSObject *));
     }
-    if (script->trynotesOffset != 0) {
+    if (JSScript::isValidOffset(script->trynotesOffset)) {
         memcpy(wscript->trynotes()->vector, script->trynotes()->vector,
                wscript->trynotes()->length * sizeof(JSTryNote));
     }
-    if (script->globalsOffset != 0) {
+    if (JSScript::isValidOffset(script->globalsOffset)) {
         memcpy(wscript->globals()->vector, script->globals()->vector,
                wscript->globals()->length * sizeof(GlobalSlotArray::Entry));
     }
     if (script->nClosedArgs + script->nClosedVars != 0)
         script->copyClosedSlotsTo(wscript);
 
     if (wfun->u.i.nupvars != 0) {
         JS_ASSERT(wfun->u.i.nupvars == wscript->upvars()->length);
@@ -1954,27 +1954,25 @@ js_XDRFunctionObject(JSXDRState *xdr, JS
         JS_ARENA_RELEASE(&xdr->cx->tempPool, mark);
         if (!ok)
             return false;
 
         if (xdr->mode == JSXDR_DECODE)
             fun->freezeLocalNames(cx);
     }
 
-    if (!js_XDRScript(xdr, &fun->u.i.script, false, NULL))
+    if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
         return false;
 
     if (xdr->mode == JSXDR_DECODE) {
         *objp = FUN_OBJECT(fun);
-        if (fun->u.i.script != JSScript::emptyScript()) {
 #ifdef CHECK_SCRIPT_OWNER
-            fun->u.i.script->owner = NULL;
+        fun->script()->owner = NULL;
 #endif
-            js_CallNewScriptHook(cx, fun->u.i.script, fun);
-        }
+        js_CallNewScriptHook(cx, fun->script(), fun);
     }
 
     return true;
 }
 
 #else  /* !JS_HAS_XDR */
 
 #define js_XDRFunctionObject NULL
@@ -2712,17 +2710,28 @@ js_InitFunctionClass(JSContext *cx, JSOb
                                    NULL, function_methods, NULL, NULL);
     if (!proto)
         return NULL;
 
     JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
     if (!fun)
         return NULL;
     fun->flags |= JSFUN_PROTOTYPE;
-    fun->u.i.script = JSScript::emptyScript();
+
+    JSScript *script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+    if (!script)
+        return NULL;
+    script->setVersion(JSVERSION_DEFAULT);
+    script->noScriptRval = true;
+    script->code[0] = JSOP_STOP;
+    script->code[1] = SRC_NULL;
+#ifdef CHECK_SCRIPT_OWNER
+    script->owner = NULL;
+#endif
+    fun->u.i.script = script;
 
     if (obj->getClass()->flags & JSCLASS_IS_GLOBAL) {
         /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
         JSObject *throwTypeError =
             js_NewFunction(cx, NULL, reinterpret_cast<Native>(ThrowTypeError), 0,
                            0, obj, NULL);
         if (!throwTypeError)
             return NULL;
@@ -2816,28 +2825,26 @@ js_CloneFunctionObject(JSContext *cx, JS
         cfun->nargs = fun->nargs;
         cfun->flags = fun->flags;
         cfun->u = fun->getFunctionPrivate()->u;
         cfun->atom = fun->atom;
         clone->setPrivate(cfun);
         if (cfun->isInterpreted()) {
             JSScript *script = cfun->u.i.script;
             JS_ASSERT(script);
-            if (script != JSScript::emptyScript()) {
-                JS_ASSERT(script->compartment == fun->compartment());
-                JS_ASSERT(script->compartment != cx->compartment);
-                cfun->u.i.script = js_CloneScript(cx, script);
-                if (!cfun->u.i.script)
-                    return NULL;
-                JS_ASSERT(cfun->u.i.script != JSScript::emptyScript());
+            JS_ASSERT(script->compartment == fun->compartment());
+            JS_ASSERT(script->compartment != cx->compartment);
+
+            cfun->u.i.script = js_CloneScript(cx, script);
+            if (!cfun->u.i.script)
+                return NULL;
 #ifdef CHECK_SCRIPT_OWNER
-                cfun->u.i.script->owner = NULL;
+            cfun->script()->owner = NULL;
 #endif
-                js_CallNewScriptHook(cx, cfun->u.i.script, cfun);
-            }
+            js_CallNewScriptHook(cx, cfun->script(), cfun);
         }
     }
     return clone;
 }
 
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CloneFunctionObject, CONTEXT, FUNCTION, OBJECT, OBJECT, 0,
                      nanojit::ACCSET_STORE_ANY)
@@ -2847,17 +2854,17 @@ JS_DEFINE_CALLINFO_4(extern, OBJECT, js_
  * Create a new flat closure, but don't initialize the imported upvar
  * values. The tracer calls this function and then initializes the upvar
  * slots on trace.
  */
 JSObject * JS_FASTCALL
 js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
 {
     JS_ASSERT(FUN_FLAT_CLOSURE(fun));
-    JS_ASSERT((fun->u.i.script->upvarsOffset
+    JS_ASSERT((JSScript::isValidOffset(fun->u.i.script->upvarsOffset)
                ? fun->u.i.script->upvars()->length
                : 0) == fun->u.i.nupvars);
 
     JSObject *closure = CloneFunctionObject(cx, fun, scopeChain);
     if (!closure)
         return closure;
 
     uint32 nslots = fun->countUpvarSlots();
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -346,28 +346,34 @@ TypedMarker(JSTracer *trc, JSFunction *t
 
 static JS_ALWAYS_INLINE void
 TypedMarker(JSTracer *trc, JSShortString *thing)
 {
     thing->asCell()->markIfUnmarked();
 }
 
 static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, JSString *thing)
+TypedMarker(JSTracer *trc, JSString *str)
 {
     /*
-     * Iterate through all nodes and leaves in the rope if this is part of a
-     * rope; otherwise, we only iterate once: on the string itself.
+     * When marking any node of a rope, mark the entire rope. This means if
+     * a given rope node is already marked, we are done.
      */
-    JSRopeNodeIterator iter(thing);
-    JSString *str = iter.init();
+    JSRopeNodeIterator iter;
+    if (str->isRope()) {
+        if (str->asCell()->isMarked())
+            return;
+        str = iter.init(str);
+        goto not_static;
+    }
     do {
         for (;;) {
             if (JSString::isStatic(str))
                 break;
+          not_static:
             JS_ASSERT(JSTRACE_STRING == GetFinalizableTraceKind(str->asCell()->arena()->header()->thingKind));
             if (!str->asCell()->markIfUnmarked())
                 break;
             if (!str->isDependent())
                 break;
             str = str->dependentBase();
         }
         str = iter.next();
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -611,17 +611,17 @@ NoSuchMethod(JSContext *cx, uintN argc, 
     JS_ASSERT(vp[0].isObject());
     JS_ASSERT(vp[1].isObject());
     JSObject *obj = &vp[0].toObject();
     JS_ASSERT(obj->getClass() == &js_NoSuchMethodClass);
 
     args.callee() = obj->getSlot(JSSLOT_FOUND_FUNCTION);
     args.thisv() = vp[1];
     args[0] = obj->getSlot(JSSLOT_SAVED_ID);
-    JSObject *argsobj = js_NewArrayObject(cx, argc, vp + 2);
+    JSObject *argsobj = NewDenseCopiedArray(cx, argc, vp + 2);
     if (!argsobj)
         return JS_FALSE;
     args[1].setObject(*argsobj);
     JSBool ok = (flags & JSINVOKE_CONSTRUCT)
                 ? InvokeConstructor(cx, args)
                 : Invoke(cx, args, flags);
     vp[0] = args.rval();
     return ok;
@@ -903,17 +903,17 @@ Execute(JSContext *cx, JSObject *chain, 
         JSStackFrame *prev, uintN flags, Value *result)
 {
     JS_ASSERT(chain);
     JS_ASSERT_IF(prev, !prev->isDummyFrame());
 
     if (script->isEmpty()) {
         if (result)
             result->setUndefined();
-        return JS_TRUE;
+        return true;
     }
 
     LeaveTrace(cx);
 
     /*
      * Get a pointer to new frame/slots. This memory is not "claimed", so the
      * code before pushExecuteFrame must not reenter the interpreter.
      */
@@ -2501,19 +2501,16 @@ Interpret(JSContext *cx, JSStackFrame *e
     } interpGuard(cx, regs);
 
     /* Copy in hot values that change infrequently. */
     JSRuntime *const rt = cx->runtime;
     JSScript *script = regs.fp->script();
     Value *argv = regs.fp->maybeFormalArgs();
     CHECK_INTERRUPT_HANDLER();
 
-    JS_ASSERT(!script->isEmpty());
-    JS_ASSERT(script->length >= 1);
-
 #if defined(JS_TRACER) && defined(JS_METHODJIT)
     bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT);
 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false))
 #else
 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0)
 #endif
 
     if (!entryFrame)
@@ -4073,18 +4070,18 @@ END_CASE(JSOP_THIS)
 
 BEGIN_CASE(JSOP_UNBRANDTHIS)
 {
     if (!regs.fp->computeThis(cx))
         goto error;
     Value &thisv = regs.fp->thisValue();
     if (thisv.isObject()) {
         JSObject *obj = &thisv.toObject();
-        if (obj->isNative() && !obj->unbrand(cx))
-            goto error;
+        if (obj->isNative())
+            obj->unbrand(cx);
     }
 }
 END_CASE(JSOP_UNBRANDTHIS)
 
 {
     JSObject *obj;
     Value *vp;
     jsint i;
@@ -4288,18 +4285,17 @@ BEGIN_CASE(JSOP_CALLPROP)
             goto error;
     }
 #endif
 }
 END_CASE(JSOP_CALLPROP)
 
 BEGIN_CASE(JSOP_UNBRAND)
     JS_ASSERT(regs.sp - regs.fp->slots() >= 1);
-    if (!regs.sp[-1].toObject().unbrand(cx))
-        goto error;
+    regs.sp[-1].toObject().unbrand(cx);
 END_CASE(JSOP_UNBRAND)
 
 BEGIN_CASE(JSOP_SETGNAME)
 BEGIN_CASE(JSOP_SETNAME)
 BEGIN_CASE(JSOP_SETPROP)
 BEGIN_CASE(JSOP_SETMETHOD)
 {
     Value rval = regs.sp[-1];
@@ -4672,17 +4668,17 @@ BEGIN_CASE(JSOP_FUNCALL)
     if (IsFunctionObject(*vp, &callee)) {
         newfun = callee->getFunctionPrivate();
 
         /* Clear frame flags since this is not a constructor call. */
         flags = 0;
         if (newfun->isInterpreted())
       inline_call:
         {
-            JSScript *newscript = newfun->u.i.script;
+            JSScript *newscript = newfun->script();
             if (JS_UNLIKELY(newscript->isEmpty())) {
                 vp->setUndefined();
                 regs.sp = vp + 1;
                 goto end_call;
             }
 
             /* Restrict recursion of lightweight functions. */
             if (JS_UNLIKELY(inlineCallCount >= JS_MAX_INLINE_CALL_COUNT)) {
@@ -5887,37 +5883,35 @@ END_CASE(JSOP_HOLE)
 BEGIN_CASE(JSOP_NEWINIT)
 {
     jsint i = regs.pc[1];
 
     JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
     JSObject *obj;
 
     if (i == JSProto_Array) {
-        obj = js_NewArrayObject(cx, 0, NULL);
+        obj = NewDenseEmptyArray(cx);
     } else {
         gc::FinalizeKind kind = GuessObjectGCKind(0, false);
         obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
     }
 
     if (!obj)
         goto error;
 
     PUSH_OBJECT(*obj);
     CHECK_INTERRUPT_HANDLER();
 }
 END_CASE(JSOP_NEWINIT)
 
 BEGIN_CASE(JSOP_NEWARRAY)
 {
     unsigned count = GET_UINT24(regs.pc);
-    JSObject *obj = js_NewArrayObject(cx, count, NULL);
-
-    /* Avoid ensureDenseArrayElements to skip sparse array checks there. */
-    if (!obj || !obj->ensureSlots(cx, count))
+    JSObject *obj = NewDenseAllocatedArray(cx, count);
+    if (!obj)
         goto error;
 
     PUSH_OBJECT(*obj);
     CHECK_INTERRUPT_HANDLER();
 }
 END_CASE(JSOP_NEWARRAY)
 
 BEGIN_CASE(JSOP_NEWOBJECT)
@@ -6064,17 +6058,17 @@ BEGIN_CASE(JSOP_DEFSHARP)
     uint32 slot = GET_UINT16(regs.pc);
     JS_ASSERT(slot + 1 < regs.fp->numFixed());
     const Value &lref = regs.fp->slots()[slot];
     JSObject *obj;
     if (lref.isObject()) {
         obj = &lref.toObject();
     } else {
         JS_ASSERT(lref.isUndefined());
-        obj = js_NewArrayObject(cx, 0, NULL);
+        obj = NewDenseEmptyArray(cx);
         if (!obj)
             goto error;
         regs.fp->slots()[slot].setObject(*obj);
     }
     jsint i = (jsint) GET_UINT16(regs.pc + UINT16_LEN);
     jsid id = INT_TO_JSID(i);
     const Value &rref = regs.sp[-1];
     if (rref.isPrimitive()) {
@@ -6827,17 +6821,17 @@ END_CASE(JSOP_ARRAYPUSH)
               default:;
             }
             CHECK_INTERRUPT_HANDLER();
         }
 
         /*
          * Look for a try block in script that can catch this exception.
          */
-        if (script->trynotesOffset == 0)
+        if (!JSScript::isValidOffset(script->trynotesOffset))
             goto no_catch;
 
         offset = (uint32)(regs.pc - script->main);
         tn = script->trynotes()->vector;
         tnlimit = tn + script->trynotes()->length;
         do {
             if (offset - tn->start >= tn->length)
                 continue;
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -56,33 +56,33 @@ struct JSFrameRegs
     js::Value       *sp;                  /* stack pointer */
     jsbytecode      *pc;                  /* program counter */
     JSStackFrame    *fp;                  /* active frame */
 };
 
 /* Flags to toggle js::Interpret() execution. */
 enum JSInterpMode
 {
-    JSINTERP_NORMAL            =     0, /* Interpreter is running normally. */
+    JSINTERP_NORMAL            =     0, /* interpreter is running normally */
     JSINTERP_RECORD            =     1, /* interpreter has been started to record/run traces */
     JSINTERP_SAFEPOINT         =     2, /* interpreter should leave on a method JIT safe point */
     JSINTERP_PROFILE           =     3  /* interpreter should profile a loop */
 };
 
 /* Flags used in JSStackFrame::flags_ */
 enum JSFrameFlags
 {
     /* Primary frame type */
     JSFRAME_GLOBAL             =     0x1, /* frame pushed for a global script */
     JSFRAME_FUNCTION           =     0x2, /* frame pushed for a scripted call */
     JSFRAME_DUMMY              =     0x4, /* frame pushed for bookkeeping */
 
     /* Frame subtypes */
-    JSFRAME_EVAL               =     0x8, /* frame pushed by js::Execute */
-    JSFRAME_DEBUGGER           =    0x10, /* frame pushed by JS_EvaluateInStackFrame */
+    JSFRAME_EVAL               =     0x8, /* frame pushed for eval() or debugger eval */
+    JSFRAME_DEBUGGER           =    0x10, /* frame pushed for debugger eval */
     JSFRAME_GENERATOR          =    0x20, /* frame is associated with a generator */
     JSFRAME_FLOATING_GENERATOR =    0x40, /* frame is is in generator obj, not on stack */
     JSFRAME_CONSTRUCTING       =    0x80, /* frame is for a constructor invocation */
 
     /* Temporary frame states */
     JSFRAME_ASSIGNING          =   0x100, /* not-JOF_ASSIGNING op is assigning */
     JSFRAME_YIELDING           =   0x200, /* js::Interpret dispatched JSOP_YIELD */
     JSFRAME_FINISHED_IN_INTERPRETER = 0x400, /* set if frame finished in Interpret() */
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -160,17 +160,17 @@ struct IdHashPolicy {
 typedef HashSet<jsid, IdHashPolicy, ContextAllocPolicy> IdSet;
 
 static inline bool
 NewKeyValuePair(JSContext *cx, jsid id, const Value &val, Value *rval)
 {
     Value vec[2] = { IdToValue(id), val };
     AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vec), vec);
 
-    JSObject *aobj = js_NewArrayObject(cx, 2, vec);
+    JSObject *aobj = NewDenseCopiedArray(cx, 2, vec);
     if (!aobj)
         return false;
     rval->setObject(*aobj);
     return true;
 }
 
 struct KeyEnumeration
 {
@@ -610,16 +610,24 @@ EnumeratedIdVectorToIterator(JSContext *
             return false;
     }
 
     return VectorToValueIterator(cx, obj, flags, vals, vp);
 }
 
 typedef Vector<uint32, 8> ShapeVector;
 
+static inline void
+UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
+{
+    // Update the object for which the native iterator is associated, so
+    // SuppressDeletedPropertyHelper will recognize the iterator as a match.
+    ni->obj = obj;
+}
+
 bool
 GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
 {
     uint32 hash;
     JSObject **hp;
     Vector<uint32, 8> shapes(cx);
     uint32 key = 0;
 
@@ -649,16 +657,17 @@ GetIterator(JSContext *cx, JSObject *obj
                 NativeIterator *lastni = last->getNativeIterator();
                 if (!(lastni->flags & JSITER_ACTIVE) &&
                     obj->isNative() && 
                     obj->shape() == lastni->shapes_array[0] &&
                     proto && proto->isNative() && 
                     proto->shape() == lastni->shapes_array[1] &&
                     !proto->getProto()) {
                     vp->setObject(*last);
+                    UpdateNativeIterator(lastni, obj);
                     RegisterEnumerator(cx, last, lastni);
                     return true;
                 }
             }
 
             /*
              * The iterator object for JSITER_ENUMERATE never escapes, so we
              * don't care for the proper parent/proto to be set. This also
@@ -686,16 +695,17 @@ GetIterator(JSContext *cx, JSObject *obj
             if (iterobj) {
                 NativeIterator *ni = iterobj->getNativeIterator();
                 if (!(ni->flags & JSITER_ACTIVE) &&
                     ni->shapes_key == key &&
                     ni->shapes_length == shapes.length() &&
                     Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
                     vp->setObject(*iterobj);
 
+                    UpdateNativeIterator(ni, obj);
                     RegisterEnumerator(cx, iterobj, ni);
                     if (shapes.length() == 2)
                         JS_THREAD_DATA(cx)->lastNativeIterator = iterobj;
                     return true;
                 }
             }
         }
 
@@ -962,16 +972,17 @@ public:
 
     bool operator()(jsid id) { return id == this->id; }
     bool matchesAtMostOne() { return true; }
 };
 
 bool
 js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
 {
+    id = js_CheckForStringIndex(id);
     return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id));
 }
 
 class IndexRangePredicate {
     jsint begin, end;
 public:
     IndexRangePredicate(jsint begin, jsint end) : begin(begin), end(end) {}
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1050,17 +1050,17 @@ EvalCacheLookup(JSContext *cx, JSString 
                      * function or regexp object if any. If none, then this
                      * script has no compiled-in dependencies on the prior
                      * eval's scopeobj.
                      */
                     JSObjectArray *objarray = script->objects();
                     int i = 1;
 
                     if (objarray->length == 1) {
-                        if (script->regexpsOffset != 0) {
+                        if (JSScript::isValidOffset(script->regexpsOffset)) {
                             objarray = script->regexps();
                             i = 0;
                         } else {
                             EVAL_CACHE_METER(noscope);
                             i = -1;
                         }
                     }
                     if (i < 0 ||
@@ -1801,17 +1801,17 @@ obj_keys(JSContext *cx, uintN argc, Valu
                 return false;
             JS_ALWAYS_TRUE(vals.append(StringValue(str)));
         } else {
             JS_ASSERT(JSID_IS_OBJECT(id));
         }
     }
 
     JS_ASSERT(props.length() <= UINT32_MAX);
-    JSObject *aobj = js_NewArrayObject(cx, jsuint(vals.length()), vals.begin());
+    JSObject *aobj = NewDenseCopiedArray(cx, jsuint(vals.length()), vals.begin());
     if (!aobj)
         return false;
     vp->setObject(*aobj);
 
     return true;
 }
 
 static bool
@@ -2511,17 +2511,17 @@ obj_getOwnPropertyNames(JSContext *cx, u
              vals[i].setString(str);
          } else if (JSID_IS_ATOM(id)) {
              vals[i].setString(JSID_TO_STRING(id));
          } else {
              vals[i].setObject(*JSID_TO_OBJECT(id));
          }
     }
 
-    JSObject *aobj = js_NewArrayObject(cx, vals.length(), vals.begin());
+    JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
     if (!aobj)
         return false;
 
     vp->setObject(*aobj);
     return true;
 }
 
 static JSBool
@@ -3533,19 +3533,19 @@ js_XDRBlockObject(JSXDRState *xdr, JSObj
     cx = xdr->cx;
 #ifdef __GNUC__
     obj = NULL;         /* quell GCC overwarning */
 #endif
 
     if (xdr->mode == JSXDR_ENCODE) {
         obj = *objp;
         parent = obj->getParent();
-        parentId = (xdr->script->objectsOffset == 0)
-                   ? NO_PARENT_INDEX
-                   : FindObjectIndex(xdr->script->objects(), parent);
+        parentId = JSScript::isValidOffset(xdr->script->objectsOffset)
+                   ? FindObjectIndex(xdr->script->objects(), parent)
+                   : NO_PARENT_INDEX;
         depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj);
         count = (uint16)OBJ_BLOCK_COUNT(cx, obj);
         depthAndCount = (uint32)(depth << 16) | count;
     }
 #ifdef __GNUC__ /* suppress bogus gcc warnings */
     else count = 0;
 #endif
 
@@ -3830,20 +3830,20 @@ js_InitClass(JSContext *cx, JSObject *ob
         (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) {
         goto bad;
     }
 
     /*
      * Pre-brand the prototype and constructor if they have built-in methods.
      * This avoids extra shape guard branch exits in the tracejitted code.
      */
-    if (fs && !proto->brand(cx))
-        goto bad;
-    if (ctor != proto && static_fs && !ctor->brand(cx))
-        goto bad;
+    if (fs)
+        proto->brand(cx);
+    if (ctor != proto && static_fs)
+        ctor->brand(cx);
 
     /*
      * Make sure proto's emptyShape is available to be shared by objects of
      * this class.  JSObject::emptyShape is a one-slot cache. If we omit this,
      * some other class could snap it up. (The risk is particularly great for
      * Object.prototype.)
      *
      * All callers of JSObject::initSharingEmptyShape depend on this.
@@ -5510,17 +5510,16 @@ js_SetPropertyHelper(JSContext *cx, JSOb
              * cases.
              */
             bool identical = shape->isMethod() && &shape->methodObject() == &vp->toObject();
             if (!identical) {
                 if (!obj->methodShapeChange(cx, *shape))
                     return false;
 
                 JS_ASSERT(IsFunctionObject(*vp));
-                JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
 
                 JSObject *funobj = &vp->toObject();
                 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
                 if (fun == funobj) {
                     funobj = CloneFunctionObject(cx, fun, fun->parent);
                     if (!funobj)
                         return JS_FALSE;
                     vp->setObject(*funobj);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -332,26 +332,29 @@ struct JSObject : js::gc::Cell {
 
     inline js::Shape **nativeSearch(jsid id, bool adding = false);
     inline const js::Shape *nativeLookup(jsid id);
 
     inline bool nativeContains(jsid id);
     inline bool nativeContains(const js::Shape &shape);
 
     enum {
-        DELEGATE        = 0x01,
-        SYSTEM          = 0x02,
-        NOT_EXTENSIBLE  = 0x04,
-        BRANDED         = 0x08,
-        GENERIC         = 0x10,
-        METHOD_BARRIER  = 0x20,
-        INDEXED         = 0x40,
-        OWN_SHAPE       = 0x80,
-        BOUND_FUNCTION  = 0x100,
-        HAS_EQUALITY    = 0x200
+        DELEGATE                  =  0x01,
+        SYSTEM                    =  0x02,
+        NOT_EXTENSIBLE            =  0x04,
+        BRANDED                   =  0x08,
+        GENERIC                   =  0x10,
+        METHOD_BARRIER            =  0x20,
+        INDEXED                   =  0x40,
+        OWN_SHAPE                 =  0x80,
+        BOUND_FUNCTION            = 0x100,
+        HAS_EQUALITY              = 0x200,
+        METHOD_THRASH_COUNT_MASK  = 0xc00,
+        METHOD_THRASH_COUNT_SHIFT =    10,
+        METHOD_THRASH_COUNT_MAX   = METHOD_THRASH_COUNT_MASK >> METHOD_THRASH_COUNT_SHIFT
     };
 
     /*
      * Impose a sane upper bound, originally checked only for dense arrays, on
      * number of slots in an object.
      */
     enum {
         NSLOTS_BITS     = 29,
@@ -418,22 +421,36 @@ struct JSObject : js::gc::Cell {
 
     /*
      * A branded object contains plain old methods (function-valued properties
      * without magic getters and setters), and its shape evolves whenever a
      * function value changes.
      */
     bool branded()              { return !!(flags & BRANDED); }
 
+    /*
+     * NB: these return false on shape overflow but do not report any error.
+     * Callers who depend on shape guarantees should therefore bail off trace,
+     * e.g., on false returns.
+     */
     bool brand(JSContext *cx);
     bool unbrand(JSContext *cx);
 
     bool generic()              { return !!(flags & GENERIC); }
     void setGeneric()           { flags |= GENERIC; }
 
+    uintN getMethodThrashCount() const {
+        return (flags & METHOD_THRASH_COUNT_MASK) >> METHOD_THRASH_COUNT_SHIFT;
+    }
+
+    void setMethodThrashCount(uintN count) {
+        JS_ASSERT(count <= METHOD_THRASH_COUNT_MAX);
+        flags = (flags & ~METHOD_THRASH_COUNT_MASK) | (count << METHOD_THRASH_COUNT_SHIFT);
+    }
+
     bool hasSpecialEquality() const { return !!(flags & HAS_EQUALITY); }
     void assertSpecialEqualitySynced() const {
         JS_ASSERT(!!clasp->ext.equality == hasSpecialEquality());
     }
 
     /* Sets an object's HAS_EQUALITY flag based on its clasp. */
     inline void syncSpecialEquality();
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -100,18 +100,23 @@ JSObject::brand(JSContext *cx)
     flags |= BRANDED;
     return true;
 }
 
 inline bool
 JSObject::unbrand(JSContext *cx)
 {
     JS_ASSERT(isNative());
-    if (!branded())
-        setGeneric();
+    if (branded()) {
+        generateOwnShape(cx);
+        if (js_IsPropertyCacheDisabled(cx))  // check for rt->shapeGen overflow
+            return false;
+        flags &= ~BRANDED;
+    }
+    setGeneric();
     return true;
 }
 
 inline void
 JSObject::syncSpecialEquality()
 {
     if (clasp->ext.equality)
         flags |= JSObject::HAS_EQUALITY;
@@ -184,33 +189,31 @@ ChangesMethodValue(const js::Value &prev
     JSObject *prevObj;
     return prev.isObject() && (prevObj = &prev.toObject())->isFunction() &&
            (!v.isObject() || &v.toObject() != prevObj);
 }
 
 inline bool
 JSObject::methodWriteBarrier(JSContext *cx, const js::Shape &shape, const js::Value &v)
 {
-    if (flags & (BRANDED | METHOD_BARRIER)) {
-        if (shape.slot != SHAPE_INVALID_SLOT) {
-            const js::Value &prev = nativeGetSlot(shape.slot);
+    if (brandedOrHasMethodBarrier() && shape.slot != SHAPE_INVALID_SLOT) {
+        const js::Value &prev = nativeGetSlot(shape.slot);
 
-            if (ChangesMethodValue(prev, v)) {
-                JS_FUNCTION_METER(cx, mwritebarrier);
-                return methodShapeChange(cx, shape);
-            }
+        if (ChangesMethodValue(prev, v)) {
+            JS_FUNCTION_METER(cx, mwritebarrier);
+            return methodShapeChange(cx, shape);
         }
     }
     return true;
 }
 
 inline bool
 JSObject::methodWriteBarrier(JSContext *cx, uint32 slot, const js::Value &v)
 {
-    if (flags & (BRANDED | METHOD_BARRIER)) {
+    if (brandedOrHasMethodBarrier()) {
         const js::Value &prev = nativeGetSlot(slot);
 
         if (ChangesMethodValue(prev, v)) {
             JS_FUNCTION_METER(cx, mwslotbarrier);
             return methodShapeChange(cx, slot);
         }
     }
     return true;
@@ -926,30 +929,39 @@ namespace WithProto {
  *    defaults to proto->getParent() if proto is non-null (else to null).
  *
  * If isFunction is true, return a JSFunction-sized object. If isFunction is
  * false, return a normal object.
  *
  * Note that as a template, there will be lots of instantiations, which means
  * the internals will be specialized based on the template parameters.
  */
+static JS_ALWAYS_INLINE bool
+FindProto(JSContext *cx, js::Class *clasp, JSObject *parent, JSObject ** proto)
+{
+    JSProtoKey protoKey = GetClassProtoKey(clasp);
+    if (!js_GetClassPrototype(cx, parent, protoKey, proto, clasp))
+        return false;
+    if (!(*proto) && !js_GetClassPrototype(cx, parent, JSProto_Object, proto))
+        return false;
+
+    return true;
+}
+
 namespace detail
 {
 template <bool withProto, bool isFunction>
 static JS_ALWAYS_INLINE JSObject *
 NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
           gc::FinalizeKind kind)
 {
     /* Bootstrap the ur-object, and make it the default prototype object. */
     if (withProto == WithProto::Class && !proto) {
-        JSProtoKey protoKey = GetClassProtoKey(clasp);
-        if (!js_GetClassPrototype(cx, parent, protoKey, &proto, clasp))
-            return NULL;
-        if (!proto && !js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
-            return NULL;
+        if (!FindProto (cx, clasp, parent, &proto))
+          return NULL;
     }
 
     /*
      * Allocate an object from the GC heap and initialize all its fields before
      * doing any operation that can potentially trigger GC. Functions have a
      * larger non-standard allocation size.
      *
      * The should be specialized by the template.
@@ -1020,23 +1032,16 @@ NewObject(JSContext *cx, js::Class *clas
 template <WithProto::e withProto>
 static JS_ALWAYS_INLINE JSObject *
 NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent)
 {
     gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
     return NewObject<withProto>(cx, clasp, proto, parent, kind);
 }
 
-/* Creates a new array with a zero length and the given finalize kind. */
-static inline JSObject *
-NewArrayWithKind(JSContext* cx, gc::FinalizeKind kind)
-{
-    return NewNonFunction<WithProto::Class>(cx, &js_ArrayClass, NULL, NULL, kind);
-}
-
 /*
  * As for js_GetGCObjectKind, where numSlots is a guess at the final size of
  * the object, zero if the final size is unknown.
  */
 static inline gc::FinalizeKind
 GuessObjectGCKind(size_t numSlots, bool isArray)
 {
     if (numSlots)
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -675,17 +675,17 @@ Revive(JSContext *cx, const Value &reviv
 }
 
 JSONParser *
 js_BeginJSONParse(JSContext *cx, Value *rootVal, bool suppressErrors /*= false*/)
 {
     if (!cx)
         return NULL;
 
-    JSObject *arr = js_NewArrayObject(cx, 0, NULL);
+    JSObject *arr = NewDenseEmptyArray(cx);
     if (!arr)
         return NULL;
 
     JSONParser *jp = cx->create<JSONParser>(cx);
     if (!jp)
         return NULL;
 
     jp->objectStack = arr;
@@ -851,17 +851,17 @@ OpenObject(JSContext *cx, JSONParser *jp
 
     return PushObject(cx, jp, obj);
 }
 
 static JSBool
 OpenArray(JSContext *cx, JSONParser *jp)
 {
     // Add an array to an existing array or object
-    JSObject *arr = js_NewArrayObject(cx, 0, NULL);
+    JSObject *arr = NewDenseEmptyArray(cx);
     if (!arr)
         return JS_FALSE;
 
     return PushObject(cx, jp, arr);
 }
 
 static JSBool
 CloseObject(JSContext *cx, JSONParser *jp)
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1332,17 +1332,17 @@ GetLocal(SprintStack *ss, jsint i)
      * for the block containing this local by its stack index, i.
      *
      * In case of destructuring's use of JSOP_GETLOCAL, however, there may be
      * no such local. This could mean no blocks (no script objects at all, or
      * none of the script's object literals are blocks), or the stack slot i is
      * not in a block. In either case, return GetStr(ss, i).
      */
     JSScript *script = ss->printer->script;
-    if (script->objectsOffset == 0)
+    if (!JSScript::isValidOffset(script->objectsOffset))
         return GetStr(ss, i);
 
     for (jsatomid j = 0, n = script->objects()->length; j != n; j++) {
         JSObject *obj = script->getObject(j);
         if (obj->isBlock()) {
             jsint depth = OBJ_BLOCK_DEPTH(cx, obj);
             jsint count = OBJ_BLOCK_COUNT(cx, obj);
 
@@ -2885,17 +2885,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                     JSStackFrame *fp = js_GetTopStackFrame(cx);
                     if (fp) {
                         while (!fp->isEvalFrame())
                             fp = fp->prev();
                         JS_ASSERT(fp->script() == jp->script);
                         JS_ASSERT(fp->prev()->fun() == jp->fun);
                         JS_ASSERT(FUN_INTERPRETED(jp->fun));
                         JS_ASSERT(jp->script != jp->fun->u.i.script);
-                        JS_ASSERT(jp->script->upvarsOffset != 0);
+                        JS_ASSERT(JSScript::isValidOffset(jp->script->upvarsOffset));
                     }
 #endif
                     uva = jp->script->upvars();
                     index = uva->vector[index].slot();
                 }
                 atom = GetArgOrVarAtom(jp, index);
                 goto do_name;
               }
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -635,19 +635,19 @@ JSParseNode::newBinaryOrAppend(TokenKind
 
 namespace js {
 
 inline void
 NameNode::initCommon(JSTreeContext *tc)
 {
     pn_expr = NULL;
     pn_cookie.makeFree();
-    pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
-    if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
-        pn_dflags |= PND_BLOCKCHILD;
+    pn_dflags = (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
+                ? PND_BLOCKCHILD
+                : 0;
     pn_blockid = tc->blockid();
 }
 
 NameNode *
 NameNode::create(JSAtom *atom, JSTreeContext *tc)
 {
     JSParseNode *pn;
 
@@ -860,32 +860,33 @@ Compiler::compileScript(JSContext *cx, J
 
 #if JS_HAS_XML_SUPPORT
     pn = NULL;
     bool onlyXML;
     onlyXML = true;
 #endif
 
     inDirectivePrologue = true;
+    tokenStream.setOctalCharacterEscape(false);
     for (;;) {
         tt = tokenStream.peekToken(TSF_OPERAND);
         if (tt <= TOK_EOF) {
             if (tt == TOK_EOF)
                 break;
             JS_ASSERT(tt == TOK_ERROR);
             goto out;
         }
 
         pn = parser.statement();
         if (!pn)
             goto out;
         JS_ASSERT(!cg.blockNode);
 
-        if (inDirectivePrologue)
-            inDirectivePrologue = parser.recognizeDirectivePrologue(pn);
+        if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue))
+            goto out;
 
         if (!js_FoldConstants(cx, pn, &cg))
             goto out;
 
         if (cg.functionList) {
             if (!parser.analyzeFunctions(cg.functionList, cg.flags))
                 goto out;
             cg.functionList = NULL;
@@ -973,17 +974,17 @@ Compiler::compileScript(JSContext *cx, J
 #ifdef METER_PARSENODES
     printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
            (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
 #endif
 #ifdef JS_ARENAMETER
     JS_DumpArenaStats(stdout);
 #endif
     script = JSScript::NewScriptFromCG(cx, &cg);
-    if (script && funbox && script != script->emptyScript())
+    if (script && funbox)
         script->savedCallerFun = true;
 
 #ifdef JS_SCOPE_DEPTH_METER
     if (script) {
         JSObject *obj = scopeChain;
         uintN depth = 1;
         while ((obj = obj->getParent()) != NULL)
             ++depth;
@@ -1064,33 +1065,35 @@ Compiler::defineGlobals(JSContext *cx, G
      * go through all global uses. Each global use indexes into globalScope->defs.
      * Use this information to repoint each use to the correct slot in the global
      * object.
      */
     while (worklist.length()) {
         JSScript *inner = worklist.back();
         worklist.popBack();
 
-        if (inner->objectsOffset != 0) {
+        if (JSScript::isValidOffset(inner->objectsOffset)) {
             JSObjectArray *arr = inner->objects();
             for (size_t i = 0; i < arr->length; i++) {
                 JSObject *obj = arr->vector[i];
                 if (!obj->isFunction())
                     continue;
                 JSFunction *fun = obj->getFunctionPrivate();
                 JS_ASSERT(fun->isInterpreted());
                 JSScript *inner = fun->u.i.script;
-                if (inner->globalsOffset == 0 && inner->objectsOffset == 0)
+                if (!JSScript::isValidOffset(inner->globalsOffset) &&
+                    !JSScript::isValidOffset(inner->objectsOffset)) {
                     continue;
+                }
                 if (!worklist.append(inner))
                     return false;
             }
         }
 
-        if (inner->globalsOffset == 0)
+        if (!JSScript::isValidOffset(inner->globalsOffset))
             continue;
 
         GlobalSlotArray *globalUses = inner->globals();
         uint32 nGlobalUses = globalUses->length;
         for (uint32 i = 0; i < nGlobalUses; i++) {
             uint32 index = globalUses->vector[i].slot;
             JS_ASSERT(index < globalScope.defs.length());
             globalUses->vector[i].slot = globalScope.defs[index].knownSlot;
@@ -1474,16 +1477,18 @@ Define(JSParseNode *pn, JSAtom *atom, JS
     }
 
     ale = tc->decls.add(tc->parser, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
     if (!ale)
         return false;
     ALE_SET_DEFN(ale, pn);
     pn->pn_defn = true;
     pn->pn_dflags &= ~PND_PLACEHOLDER;
+    if (!tc->parent)
+        pn->pn_dflags |= PND_TOPLEVEL;
     return true;
 }
 
 static void
 LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
 {
     JS_ASSERT(!pn->pn_used);
     JS_ASSERT(!pn->pn_defn);
@@ -1555,17 +1560,16 @@ MakeDefIntoUse(JSDefinition *dn, JSParse
             if (!lhs)
                 return NULL;
             //pn->dn_uses = lhs;
             dn = (JSDefinition *) lhs;
         }
 
         dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
     } else if (dn->kind() == JSDefinition::FUNCTION) {
-        JS_ASSERT(dn->isTopLevel());
         JS_ASSERT(dn->pn_op == JSOP_NOP);
         dn->pn_type = TOK_NAME;
         dn->pn_arity = PN_NAME;
         dn->pn_atom = atom;
     }
 
     /* Now make dn no longer a definition, rather a use of pn. */
     JS_ASSERT(dn->pn_type == TOK_NAME);
@@ -2538,18 +2542,16 @@ LeaveFunction(JSParseNode *fn, JSTreeCon
 {
     JSTreeContext *tc = funtc->parent;
     tc->blockidGen = funtc->blockidGen;
 
     JSFunctionBox *funbox = fn->pn_funbox;
     funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO | TCF_RETURN_EXPR);
 
     fn->pn_dflags |= PND_INITIALIZED;
-    JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
-                 fn->pn_dflags & PND_TOPLEVEL);
     if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
         fn->pn_dflags |= PND_BLOCKCHILD;
 
     /*
      * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
      * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
      * params and body. We do this only if there are lexical dependencies not
      * satisfied by the function's declarations, to avoid penalizing functions
@@ -2859,22 +2861,22 @@ Parser::functionDef(JSAtom *funAtom, Fun
         return NULL;
     pn->pn_body = NULL;
     pn->pn_cookie.makeFree();
 
     /*
      * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
      * is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
      *
-     * Also treat function sub-statements (non-lambda, non-top-level functions)
-     * as escaping funargs, since we can't statically analyze their definitions
+     * Treat function sub-statements (non-lambda, non-body-level functions) as
+     * escaping funargs, since we can't statically analyze their definitions
      * and uses.
      */
-    bool topLevel = tc->atTopLevel();
-    pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
+    bool bodyLevel = tc->atBodyLevel();
+    pn->pn_dflags = (lambda || !bodyLevel) ? PND_FUNARG : 0;
 
     /*
      * Record names for function statements in tc->decls so we know when to
      * avoid optimizing variable references that might name a function.
      */
     if (lambda == 0 && funAtom) {
         if (JSAtomListElement *ale = tc->decls.lookup(funAtom)) {
             JSDefinition *dn = ALE_DEFN(ale);
@@ -2892,25 +2894,25 @@ Parser::functionDef(JSAtom *funAtom, Fun
                                        : JSREPORT_ERROR,
                                        JSMSG_REDECLARED_VAR,
                                        JSDefinition::kindString(dn_kind),
                                        name.ptr())) {
                     return NULL;
                 }
             }
 
-            if (topLevel) {
+            if (bodyLevel) {
                 ALE_SET_DEFN(ale, pn);
                 pn->pn_defn = true;
                 pn->dn_uses = dn;               /* dn->dn_uses is now pn_link */
 
                 if (!MakeDefIntoUse(dn, pn, funAtom, tc))
                     return NULL;
             }
-        } else if (topLevel) {
+        } else if (bodyLevel) {
             /*
              * If this function was used before it was defined, claim the
              * pre-created definition node for this function that primaryExpr
              * put in tc->lexdeps on first forward reference, and recycle pn.
              */
             JSHashEntry **hep;
 
             ale = tc->lexdeps.rawLookup(funAtom, hep);
@@ -2929,53 +2931,46 @@ Parser::functionDef(JSAtom *funAtom, Fun
                 pn = fn;
             }
 
             if (!Define(pn, funAtom, tc))
                 return NULL;
         }
 
         /*
-         * A function nested at top level inside another's body needs only a
-         * local variable to bind its name to its value, and not an activation
-         * object property (it might also need the activation property, if the
-         * outer function contains with statements, e.g., but the stack slot
-         * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
+         * A function directly inside another's body needs only a local
+         * variable to bind its name to its value, and not an activation object
+         * property (it might also need the activation property, if the outer
+         * function contains with statements, e.g., but the stack slot wins
+         * when jsemit.cpp's BindNameToSlot can optimize a JSOP_NAME into a
          * JSOP_GETLOCAL bytecode).
          */
-        if (topLevel) {
-            pn->pn_dflags |= PND_TOPLEVEL;
-
-            if (tc->inFunction()) {
-                JSLocalKind localKind;
-                uintN index;
-
-                /*
-                 * Define a local in the outer function so that BindNameToSlot
-                 * can properly optimize accesses. Note that we need a local
-                 * variable, not an argument, for the function statement. Thus
-                 * we add a variable even if a parameter with the given name
-                 * already exists.
-                 */
-                localKind = tc->fun()->lookupLocal(context, funAtom, &index);
-                switch (localKind) {
-                  case JSLOCAL_NONE:
-                  case JSLOCAL_ARG:
-                    index = tc->fun()->u.i.nvars;
-                    if (!tc->fun()->addLocal(context, funAtom, JSLOCAL_VAR))
-                        return NULL;
-                    /* FALL THROUGH */
-
-                  case JSLOCAL_VAR:
-                    pn->pn_cookie.set(tc->staticLevel, index);
-                    pn->pn_dflags |= PND_BOUND;
-                    break;
-
-                  default:;
-                }
+        if (bodyLevel && tc->inFunction()) {
+            /*
+             * Define a local in the outer function so that BindNameToSlot
+             * can properly optimize accesses. Note that we need a local
+             * variable, not an argument, for the function statement. Thus
+             * we add a variable even if a parameter with the given name
+             * already exists.
+             */
+            uintN index;
+            switch (tc->fun()->lookupLocal(context, funAtom, &index)) {
+              case JSLOCAL_NONE:
+              case JSLOCAL_ARG:
+                index = tc->fun()->u.i.nvars;
+                if (!tc->fun()->addLocal(context, funAtom, JSLOCAL_VAR))
+                    return NULL;
+                /* FALL THROUGH */
+
+              case JSLOCAL_VAR:
+                pn->pn_cookie.set(tc->staticLevel, index);
+                pn->pn_dflags |= PND_BOUND;
+                break;
+
+              default:;
             }
         }
     }
 
     JSTreeContext *outertc = tc;
 
     /* Initialize early for possible flags mutation via destructuringExpr. */
     JSTreeContext funtc(tc->parser);
@@ -3099,21 +3094,22 @@ Parser::functionDef(JSAtom *funAtom, Fun
      * visible eval call, or assignment to 'arguments'), flag the function as
      * heavyweight (requiring a call object per invocation).
      */
     if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
         fun->flags |= JSFUN_HEAVYWEIGHT;
         outertc->flags |= TCF_FUN_HEAVYWEIGHT;
     } else {
         /*
-         * If this function is a named statement function not at top-level
-         * (i.e. not a top-level function definiton or expression), then our
-         * enclosing function, if any, must be heavyweight.
+         * If this function is not at body level of a program or function (i.e.
+         * it is a function statement that is not a direct child of a program
+         * or function), then our enclosing function, if any, must be
+         * heavyweight.
          */
-        if (!topLevel && lambda == 0 && funAtom)
+        if (!bodyLevel && lambda == 0 && funAtom)
             outertc->flags |= TCF_FUN_HEAVYWEIGHT;
     }
 
     JSParseNode *result = pn;
     JSOp op = JSOP_NOP;
     if (lambda != 0) {
         /*
          * ECMA ed. 3 standard: function expression, possibly anonymous.
@@ -3129,17 +3125,17 @@ Parser::functionDef(JSAtom *funAtom, Fun
          */
         result = UnaryNode::create(outertc);
         if (!result)
             return NULL;
         result->pn_type = TOK_SEMI;
         result->pn_pos = pn->pn_pos;
         result->pn_kid = pn;
         op = JSOP_LAMBDA;
-    } else if (!topLevel) {
+    } else if (!bodyLevel) {
         /*
          * ECMA ed. 3 extension: a function expression statement not at the
          * top level, e.g., in a compound statement such as the "then" part
          * of an "if" statement, binds a closure only if control reaches that
          * sub-statement.
          */
         op = JSOP_DEFFUN;
         outertc->noteMightAliasLocals();
@@ -3151,20 +3147,19 @@ Parser::functionDef(JSAtom *funAtom, Fun
     pn->pn_op = op;
     if (pn->pn_body) {
         pn->pn_body->append(body);
         pn->pn_body->pn_pos = body->pn_pos;
     } else {
         pn->pn_body = body;
     }
 
-    if (!outertc->inFunction() && topLevel && funAtom && !lambda &&
-        outertc->compiling()) {
+    if (!outertc->inFunction() && bodyLevel && funAtom && !lambda && outertc->compiling()) {
         JS_ASSERT(pn->pn_cookie.isFree());
-        if (!DefineGlobal(pn, (JSCodeGenerator *)outertc, funAtom))
+        if (!DefineGlobal(pn, outertc->asCodeGenerator(), funAtom))
             return false;
     }
 
     pn->pn_blockid = outertc->blockid();
 
     if (!LeaveFunction(pn, &funtc, funAtom, lambda))
         return NULL;
 
@@ -3220,23 +3215,43 @@ Parser::functionExpr()
  *   "use\x20loose"
  *   "use strict"
  * }
  *
  * That is, a statement can be a Directive Prologue member, even
  * if it can't possibly be a directive, now or in the future.
  */
 bool
-Parser::recognizeDirectivePrologue(JSParseNode *pn)
-{
-    if (!pn->isDirectivePrologueMember())
-        return false;
+Parser::recognizeDirectivePrologue(JSParseNode *pn, bool *isDirectivePrologueMember)
+{
+    *isDirectivePrologueMember = pn->isDirectivePrologueMember();
+    if (!*isDirectivePrologueMember)
+        return true;
     if (pn->isDirective()) {
         JSAtom *directive = pn->pn_kid->pn_atom;
         if (directive == context->runtime->atomState.useStrictAtom) {
+            /*
+             * Unfortunately, Directive Prologue members in general may contain
+             * escapes, even while "use strict" directives may not.  Therefore
+             * we must check whether an octal character escape has been seen in
+             * any previous directives whenever we encounter a "use strict"
+             * directive, so that the octal escape is properly treated as a
+             * syntax error.  An example of this case:
+             *
+             *   function error()
+             *   {
+             *     "\145"; // octal escape
+             *     "use strict"; // retroactively makes "\145" a syntax error
+             *   }
+             */
+            if (tokenStream.hasOctalCharacterEscape()) {
+                reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_DEPRECATED_OCTAL);
+                return false;
+            }
+
             tc->flags |= TCF_STRICT_MODE_CODE;
             tokenStream.setStrictMode();
         }
     }
     return true;
 }
 
 /*
@@ -3244,29 +3259,30 @@ Parser::recognizeDirectivePrologue(JSPar
  * statements' trees.  If called from block-parsing code, the caller must
  * match { before and } after.
  */
 JSParseNode *
 Parser::statements()
 {
     JSParseNode *pn, *pn2, *saveBlock;
     TokenKind tt;
-    bool inDirectivePrologue = tc->atTopLevel();
 
     JS_CHECK_RECURSION(context, return NULL);
 
     pn = ListNode::create(tc);
     if (!pn)
         return NULL;
     pn->pn_type = TOK_LC;
     pn->makeEmpty();
     pn->pn_blockid = tc->blockid();
     saveBlock = tc->blockNode;
     tc->blockNode = pn;
 
+    bool inDirectivePrologue = tc->atBodyLevel();
+    tokenStream.setOctalCharacterEscape(false);
     for (;;) {
         tt = tokenStream.peekToken(TSF_OPERAND);
         if (tt <= TOK_EOF || tt == TOK_RC) {
             if (tt == TOK_ERROR) {
                 if (tokenStream.isEOF())
                     tokenStream.setUnexpectedEOF();
                 return NULL;
             }
@@ -3274,30 +3290,30 @@ Parser::statements()
         }
         pn2 = statement();
         if (!pn2) {
             if (tokenStream.isEOF())
                 tokenStream.setUnexpectedEOF();
             return NULL;
         }
 
-        if (inDirectivePrologue)
-            inDirectivePrologue = recognizeDirectivePrologue(pn2);
+        if (inDirectivePrologue && !recognizeDirectivePrologue(pn2, &inDirectivePrologue))
+            return NULL;
 
         if (pn2->pn_type == TOK_FUNCTION) {
             /*
-             * PNX_FUNCDEFS notifies the emitter that the block contains top-
+             * PNX_FUNCDEFS notifies the emitter that the block contains body-
              * level function definitions that should be processed before the
              * rest of nodes.
              *
              * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
-             * is relevant only for function definitions not at top-level,
+             * is relevant only for function definitions not at body-level,
              * which we call function statements.
              */
-            if (tc->atTopLevel())
+            if (tc->atBodyLevel())
                 pn->pn_xflags |= PNX_FUNCDEFS;
             else
                 tc->flags |= TCF_HAS_FUNCTION_STMT;
         }
         pn->append(pn2);
     }
 
     /*
@@ -3357,20 +3373,20 @@ static JSBool
 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
 {
     JSParseNode *pn;
     JSObject *blockObj;
     JSAtomListElement *ale;
     jsint n;
 
     /*
-     * Top-level 'let' is the same as 'var' currently -- this may change in a
-     * successor standard to ES3.1 that specifies 'let'.
+     * Body-level 'let' is the same as 'var' currently -- this may change in a
+     * successor standard to ES5 that specifies 'let'.
      */
-    JS_ASSERT(!tc->atTopLevel());
+    JS_ASSERT(!tc->atBodyLevel());
 
     pn = data->pn;
     if (!CheckStrictBinding(cx, tc, atom, pn))
         return false;
 
     blockObj = tc->blockChain();
     ale = tc->decls.lookup(atom);
     if (ale && ALE_DEFN(ale)->pn_blockid == tc->blockid()) {
@@ -3578,17 +3594,17 @@ static bool
 BindGvar(JSParseNode *pn, JSTreeContext *tc)
 {
     JS_ASSERT(pn->pn_op == JSOP_NAME);
     JS_ASSERT(!tc->inFunction());
 
     if (!tc->compiling() || tc->parser->callerFrame)
         return true;
 
-    JSCodeGenerator *cg = (JSCodeGenerator *) tc;
+    JSCodeGenerator *cg = tc->asCodeGenerator();
 
     if (pn->pn_dflags & PND_CONST)
         return true;
 
     return DefineGlobal(pn, cg, pn->pn_atom);
 }
 
 static JSBool
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -474,17 +474,17 @@ public:
     JSParseNode  *maybeExpr()   { return pn_used ? NULL : expr(); }
     JSDefinition *maybeLexDef() { return pn_used ? lexdef() : NULL; }
 
 /* PN_FUNC and PN_NAME pn_dflags bits. */
 #define PND_LET         0x01            /* let (block-scoped) binding */
 #define PND_CONST       0x02            /* const binding (orthogonal to let) */
 #define PND_INITIALIZED 0x04            /* initialized declaration */
 #define PND_ASSIGNED    0x08            /* set if ever LHS of assignment */
-#define PND_TOPLEVEL    0x10            /* function at top of body or prog */
+#define PND_TOPLEVEL    0x10            /* see isTopLevel() below */
 #define PND_BLOCKCHILD  0x20            /* use or def is direct block child */
 #define PND_GVAR        0x40            /* gvar binding, can't close over
                                            because it could be deleted */
 #define PND_PLACEHOLDER 0x80            /* placeholder definition for lexdep */
 #define PND_FUNARG     0x100            /* downward or upward funarg usage */
 #define PND_BOUND      0x200            /* bound to a stack or global slot */
 #define PND_DEOPTIMIZED 0x400           /* former pn_used name node, pn_lexdef
                                            still valid, but this use no longer
@@ -524,24 +524,37 @@ public:
         return pn_cookie.slot();
     }
 
     inline bool test(uintN flag) const;
 
     bool isLet() const          { return test(PND_LET); }
     bool isConst() const        { return test(PND_CONST); }
     bool isInitialized() const  { return test(PND_INITIALIZED); }
-    bool isTopLevel() const     { return test(PND_TOPLEVEL); }
     bool isBlockChild() const   { return test(PND_BLOCKCHILD); }
     bool isPlaceholder() const  { return test(PND_PLACEHOLDER); }
     bool isDeoptimized() const  { return test(PND_DEOPTIMIZED); }
     bool isAssigned() const     { return test(PND_ASSIGNED); }
     bool isFunArg() const       { return test(PND_FUNARG); }
     bool isClosed() const       { return test(PND_CLOSED); }
 
+    /*
+     * True iff this definition creates a top-level binding in the overall
+     * script being compiled -- that is, it affects the whole program's
+     * bindings, not bindings for a specific function (unless this definition
+     * is in the outermost scope in eval code, executed within a function) or
+     * the properties of a specific object (through the with statement).
+     *
+     * NB: Function sub-statements found in overall program code and not nested
+     *     within other functions are not currently top level, even though (if
+     *     executed) they do create top-level bindings; there is no particular
+     *     rationale for this behavior.
+     */
+    bool isTopLevel() const     { return test(PND_TOPLEVEL); }
+
     /* Defined below, see after struct JSDefinition. */
     void setFunArg();
 
     void become(JSParseNode *pn2);
     void clear();
 
     /* True if pn is a parsenode representing a literal constant. */
     bool isLiteral() const {
@@ -1113,17 +1126,17 @@ private:
     JSParseNode *unaryExpr();
     JSParseNode *memberExpr(JSBool allowCallSyntax);
     JSParseNode *primaryExpr(js::TokenKind tt, JSBool afterDot);
     JSParseNode *parenExpr(JSParseNode *pn1, JSBool *genexp);
 
     /*
      * Additional JS parsers.
      */
-    bool recognizeDirectivePrologue(JSParseNode *pn);
+    bool recognizeDirectivePrologue(JSParseNode *pn, bool *isDirectivePrologueMember);
 
     enum FunctionType { GETTER, SETTER, GENERAL };
     bool functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun,
                            JSParseNode **list);
     JSParseNode *functionBody();
     JSParseNode *functionDef(JSAtom *name, FunctionType type, uintN lambda);
 
     JSParseNode *condition();
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -330,18 +330,16 @@ GetDerivedTrap(JSContext *cx, JSObject *
               atom == ATOM(iterate));
 
     return GetTrap(cx, handler, atom, fvalp);
 }
 
 static bool
 Trap(JSContext *cx, JSObject *handler, Value fval, uintN argc, Value* argv, Value *rval)
 {
-    JS_CHECK_RECURSION(cx, return false);
-
     return ExternalInvoke(cx, handler, fval, argc, argv, rval);
 }
 
 static bool
 Trap1(JSContext *cx, JSObject *handler, Value fval, jsid id, Value *rval)
 {
     JSString *str = js_ValueToString(cx, IdToValue(id));
     if (!str)
@@ -667,170 +665,193 @@ class AutoPendingProxyOperation {
         data->pendingProxyOperation = op.next;
     }
 };
 
 bool
 JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
                                PropertyDescriptor *desc)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->getPropertyDescriptor(cx, proxy, id, set, desc);
 }
 
 bool
 JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     AutoPropertyDescriptorRooter desc(cx);
     return JSProxy::getPropertyDescriptor(cx, proxy, id, set, &desc) &&
            MakePropertyDescriptorObject(cx, id, &desc, vp);
 }
 
 bool
 JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
                                   PropertyDescriptor *desc)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->getOwnPropertyDescriptor(cx, proxy, id, set, desc);
 }
 
 bool
 JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     AutoPropertyDescriptorRooter desc(cx);
     return JSProxy::getOwnPropertyDescriptor(cx, proxy, id, set, &desc) &&
            MakePropertyDescriptorObject(cx, id, &desc, vp);
 }
 
 bool
 JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->defineProperty(cx, proxy, id, desc);
 }
 
 bool
 JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     AutoPropertyDescriptorRooter desc(cx);
     return ParsePropertyDescriptorObject(cx, proxy, id, v, &desc) &&
            JSProxy::defineProperty(cx, proxy, id, &desc);
 }
 
 bool
 JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->getOwnPropertyNames(cx, proxy, props);
 }
 
 bool
 JSProxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->delete_(cx, proxy, id, bp);
 }
 
 bool
 JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->enumerate(cx, proxy, props);
 }
 
 bool
 JSProxy::fix(JSContext *cx, JSObject *proxy, Value *vp)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->fix(cx, proxy, vp);
 }
 
 bool
 JSProxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->has(cx, proxy, id, bp);
 }
 
 bool
 JSProxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->hasOwn(cx, proxy, id, bp);
 }
 
 bool
 JSProxy::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->get(cx, proxy, receiver, id, vp);
 }
 
 bool
 JSProxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->set(cx, proxy, receiver, id, vp);
 }
 
 bool
 JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->enumerateOwn(cx, proxy, props);
 }
 
 bool
 JSProxy::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->iterate(cx, proxy, flags, vp);
 }
 
 bool
 JSProxy::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->call(cx, proxy, argc, vp);
 }
 
 bool
 JSProxy::construct(JSContext *cx, JSObject *proxy, uintN argc, Value *argv, Value *rval)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->construct(cx, proxy, argc, argv, rval);
 }
 
 bool
 JSProxy::hasInstance(JSContext *cx, JSObject *proxy, const js::Value *vp, bool *bp)
 {
+    JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->hasInstance(cx, proxy, vp, bp);
 }
 
 JSType
 JSProxy::typeOf(JSContext *cx, JSObject *proxy)
 {
+    // FIXME: API doesn't allow us to report error (bug 618906).
+    JS_CHECK_RECURSION(cx, return JSTYPE_OBJECT);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->typeOf(cx, proxy);
 }
 
 JSString *
 JSProxy::obj_toString(JSContext *cx, JSObject *proxy)
 {
+    JS_CHECK_RECURSION(cx, return NULL);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->obj_toString(cx, proxy);
 }
 
 JSString *
 JSProxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
 {
+    JS_CHECK_RECURSION(cx, return NULL);
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->fun_toString(cx, proxy, indent);
 }
 
 static JSObject *
 proxy_innerObject(JSContext *cx, JSObject *obj)
 {
     return obj->getProxyPrivate().toObjectOrNull();
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -505,17 +505,17 @@ NodeBuilder::newNode(ASTType type, Token
 
     *dst = node;
     return true;
 }
 
 bool
 NodeBuilder::newArray(NodeVector &elts, Value *dst)
 {
-    JSObject *array = js_NewArrayObject(cx, 0, NULL);
+    JSObject *array = NewDenseEmptyArray(cx);
     if (!array)
         return false;
 
     const size_t len = elts.length();
     for (size_t i = 0; i < len; i++) {
         Value val = elts[i];
 
         JS_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE);
--- a/js/src/jsregexpinlines.h
+++ b/js/src/jsregexpinlines.h
@@ -241,17 +241,17 @@ RegExp::checkMatchPairs(JSString *input,
 inline JSObject *
 RegExp::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount)
 {
     /*
      * Create the result array for a match. Array contents:
      *  0:              matched string
      *  1..pairCount-1: paren matches
      */
-    JSObject *array = js_NewSlowArrayObject(cx);
+    JSObject *array = NewSlowEmptyArray(cx);
     if (!array)
         return NULL;
 
     RegExpMatchBuilder builder(cx, array);
     for (size_t i = 0; i < matchItemCount; i += 2) {
         int start = buf[i];
         int end = buf[i + 1];
 
--- a/js/src/jsscan.cpp
+++ b/js/src/jsscan.cpp
@@ -1187,16 +1187,17 @@ TokenStream::getTokenInternal()
 
                             c = peekChar();
                             /* Strict mode code allows only \0, then a non-digit. */
                             if (val != 0 || JS7_ISDEC(c)) {
                                 if (!ReportStrictModeError(cx, this, NULL, NULL,
                                                            JSMSG_DEPRECATED_OCTAL)) {
                                     goto error;
                                 }
+                                setOctalCharacterEscape();
                             }
                             if ('0' <= c && c < '8') {
                                 val = 8 * val + JS7_UNDEC(c);
                                 getChar();
                                 c = peekChar();
                                 if ('0' <= c && c < '8') {
                                     int32 save = val;
                                     val = 8 * val + JS7_UNDEC(c);
@@ -1581,17 +1582,17 @@ TokenStream::getTokenInternal()
                             if (c == '"') {
                                 while ((c = getChar()) != '\n' &&
                                        ScanAsSpace((jschar)c)) {
                                     continue;
                                 }
                             }
                         }
                         filenameBuf[i] = '\0';
-                        if (c == '\n') {
+                        if (c == EOF || c == '\n') {
                             if (i > 0) {
                                 if (flags & TSF_OWNFILENAME)
                                     cx->free((void *) filename);
                                 filename = JS_strdup(cx, filenameBuf);
                                 if (!filename)
                                     goto error;
                                 flags |= TSF_OWNFILENAME;
                             }
--- a/js/src/jsscan.h
+++ b/js/src/jsscan.h
@@ -258,16 +258,17 @@ enum TokenStreamFlags
     TSF_UNEXPECTED_EOF = 0x10,  /* unexpected end of input, i.e. TOK_EOF not at top-level. */
     TSF_KEYWORD_IS_NAME = 0x20, /* Ignore keywords and return TOK_NAME instead to the parser. */
     TSF_STRICT_MODE_CODE = 0x40,/* Tokenize as appropriate for strict mode code. */
     TSF_DIRTYLINE = 0x80,       /* non-whitespace since start of line */
     TSF_OWNFILENAME = 0x100,    /* ts->filename is malloc'd */
     TSF_XMLTAGMODE = 0x200,     /* scanning within an XML tag in E4X */
     TSF_XMLTEXTMODE = 0x400,    /* scanning XMLText terminal from E4X */
     TSF_XMLONLYMODE = 0x800,    /* don't scan {expr} within text/tag */
+    TSF_OCTAL_CHAR = 0x1000,    /* observed a octal character escape */
 
     /*
      * To handle the hard case of contiguous HTML comments, we want to clear the
      * TSF_DIRTYINPUT flag at the end of each such comment.  But we'd rather not
      * scan for --> within every //-style comment unless we have to.  So we set
      * TSF_IN_HTML_COMMENT when a <!-- is scanned as an HTML begin-comment, and
      * clear it (and TSF_DIRTYINPUT) when we scan --> either on a clean line, or
      * only if (ts->flags & TSF_IN_HTML_COMMENT), in a //-style comment.
@@ -328,22 +329,25 @@ class TokenStream
     const char *getFilename() const { return filename; }
     uintN getLineno() const { return lineno; }
 
     /* Flag methods. */
     void setStrictMode(bool enabled = true) { setFlag(enabled, TSF_STRICT_MODE_CODE); }
     void setXMLTagMode(bool enabled = true) { setFlag(enabled, TSF_XMLTAGMODE); }
     void setXMLOnlyMode(bool enabled = true) { setFlag(enabled, TSF_XMLONLYMODE); }
     void setUnexpectedEOF(bool enabled = true) { setFlag(enabled, TSF_UNEXPECTED_EOF); }
+    void setOctalCharacterEscape(bool enabled = true) { setFlag(enabled, TSF_OCTAL_CHAR); }
+
     bool isStrictMode() { return !!(flags & TSF_STRICT_MODE_CODE); }
     bool isXMLTagMode() { return !!(flags & TSF_XMLTAGMODE); }
     bool isXMLOnlyMode() { return !!(flags & TSF_XMLONLYMODE); }
     bool isUnexpectedEOF() { return !!(flags & TSF_UNEXPECTED_EOF); }
     bool isEOF() const { return !!(flags & TSF_EOF); }
     bool isError() const { return !!(flags & TSF_ERROR); }
+    bool hasOctalCharacterEscape() const { return flags & TSF_OCTAL_CHAR; }
 
     /* Mutators. */
     bool reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN errorNumber, va_list ap);
     void mungeCurrentToken(TokenKind newKind) { tokens[cursor].type = newKind; }
     void mungeCurrentToken(JSOp newOp) { tokens[cursor].t_op = newOp; }
     void mungeCurrentToken(TokenKind newKind, JSOp newOp) {
         mungeCurrentToken(newKind);
         mungeCurrentToken(newOp);
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -1380,16 +1380,28 @@ JSObject::methodShapeChange(JSContext *c
         if (!putProperty(cx, shape.id, NULL, shape.rawSetter, shape.slot,
                          shape.attrs,
                          shape.getFlags() & ~Shape::METHOD,
                          shape.shortid)) {
             return false;
         }
     }
 
+    if (branded()) {
+        uintN thrashCount = getMethodThrashCount();
+        if (thrashCount < JSObject::METHOD_THRASH_COUNT_MAX) {
+            ++thrashCount;
+            setMethodThrashCount(thrashCount);
+            if (thrashCount == JSObject::METHOD_THRASH_COUNT_MAX) {
+                unbrand(cx);
+                return true;
+            }
+        }
+    }
+
     generateOwnShape(cx);
     return true;
 }
 
 bool
 JSObject::methodShapeChange(JSContext *cx, uint32 slot)
 {
     if (!hasMethodBarrier()) {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -68,53 +68,30 @@
 
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
-static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL};
-
-/* static */ const JSScript JSScript::emptyScriptConst = {
-    JS_INIT_STATIC_CLIST(NULL),
-    const_cast<jsbytecode*>(emptyScriptCode),
-    1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, 0, 0, true, false, false, false, false,
-    false,      /* usesEval */
-    false,      /* usesArguments */
-    true,       /* warnedAboutTwoArgumentEval */
-#ifdef JS_METHODJIT
-    false,      /* debugMode */
-#endif
-    const_cast<jsbytecode*>(emptyScriptCode),
-    {0, jsatomid(0)}, NULL, NULL, 0, 0, 0,
-    0,          /* nClosedArgs */
-    0,          /* nClosedVars */
-    NULL, {NULL},
-#ifdef CHECK_SCRIPT_OWNER
-    reinterpret_cast<JSThread*>(1)
-#endif
-};
-
 #if JS_HAS_XDR
 
 enum ScriptBits {
     NoScriptRval,
     SavedCallerFun,
     HasSharps,
     StrictModeCode,
     UsesEval,
     CompileAndGo,
     UsesArguments
 };
 
 JSBool
-js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
-             JSBool *hasMagic)
+js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
 {
     JSContext *cx;
     JSScript *script, *oldscript;
     JSBool ok;
     jsbytecode *code;
     uint32 length, lineno, nslots, magic;
     uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, nconsts, i;
     uint32 prologLength, version, encodedClosedCount;
@@ -128,17 +105,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
 
     cx = xdr->cx;
     script = *scriptp;
     nsrcnotes = ntrynotes = natoms = nobjects = nupvars = nregexps = nconsts = 0;
     filenameWasSaved = JS_FALSE;
     notes = NULL;
 
     /* Should not XDR scripts optimized for a single global object. */
-    JS_ASSERT_IF(script, !script->globalsOffset);
+    JS_ASSERT_IF(script, !JSScript::isValidOffset(script->globalsOffset));
 
     if (xdr->mode == JSXDR_ENCODE)
         magic = JSXDR_MAGIC_SCRIPT_CURRENT;
     if (!JS_XDRUint32(xdr, &magic))
         return JS_FALSE;
     if (magic != JSXDR_MAGIC_SCRIPT_CURRENT) {
         /* We do not provide binary compatibility with older scripts. */
         if (!hasMagic) {
@@ -147,55 +124,20 @@ js_XDRScript(JSXDRState *xdr, JSScript *
             return JS_FALSE;
         }
         *hasMagic = JS_FALSE;
         return JS_TRUE;
     }
     if (hasMagic)
         *hasMagic = JS_TRUE;
 
-    /*
-     * Since the shortest possible script has JSOP_STOP as its only bytecode,
-     * encode only the length 0 for the emptyScript singleton, and return the
-     * emptyScript instead of a new script when decoding a script of length 0.
-     */
     if (xdr->mode == JSXDR_ENCODE)
-        length = (script == JSScript::emptyScript()) ? 0 : script->length;
+        length = script->length;
     if (!JS_XDRUint32(xdr, &length))
         return JS_FALSE;
-    if (length == 0) {
-        if (xdr->mode == JSXDR_ENCODE) {
-            JS_ASSERT(*scriptp == JSScript::emptyScript());
-            return JS_TRUE;
-        }
-
-        /* Decoding: check whether we need a mutable empty script. */
-        if (cx->debugHooks->newScriptHook)
-            needMutableScript = true;
-        if (needMutableScript) {
-            /*
-             * We need a mutable empty script but the encoder serialized only
-             * the shorthand (0 length word) for us. Make a new mutable empty
-             * script here and return it immediately.
-             */
-            script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-            if (!script)
-                return JS_FALSE;
-
-            script->setVersion(JSVERSION_DEFAULT);
-            script->noScriptRval = true;
-            script->code[0] = JSOP_STOP;
-            script->code[1] = SRC_NULL;
-            *scriptp = script;
-            return JS_TRUE;
-        }
-
-        *scriptp = JSScript::emptyScript();
-        return JS_TRUE;
-    }
 
     if (xdr->mode == JSXDR_ENCODE) {
         prologLength = script->main - script->code;
         JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN);
         version = (uint32)script->getVersion() | (script->nfixed << 16);
         lineno = (uint32)script->lineno;
         nslots = (uint32)script->nslots;
         nslots = (uint32)((script->staticLevel << 16) | script->nslots);
@@ -203,25 +145,25 @@ js_XDRScript(JSXDRState *xdr, JSScript *
 
         /* Count the srcnotes, keeping notes pointing at the first one. */
         notes = script->notes();
         for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
             continue;
         nsrcnotes = sn - notes;
         nsrcnotes++;            /* room for the terminator */
 
-        if (script->objectsOffset != 0)
+        if (JSScript::isValidOffset(script->objectsOffset))
             nobjects = script->objects()->length;
-        if (script->upvarsOffset != 0)
+        if (JSScript::isValidOffset(script->upvarsOffset))
             nupvars = script->upvars()->length;
-        if (script->regexpsOffset != 0)
+        if (JSScript::isValidOffset(script->regexpsOffset))
             nregexps = script->regexps()->length;
-        if (script->trynotesOffset != 0)
+        if (JSScript::isValidOffset(script->trynotesOffset))
             ntrynotes = script->trynotes()->length;
-        if (script->constOffset != 0)
+        if (JSScript::isValidOffset(script->constOffset))
             nconsts = script->consts()->length;
 
         nClosedArgs = script->nClosedArgs;
         nClosedVars = script->nClosedVars;
         encodedClosedCount = (nClosedArgs << 16) | nClosedVars;
 
         if (script->noScriptRval)
             scriptBits |= (1 << NoScriptRval);
@@ -951,49 +893,62 @@ JSScript::NewScript(JSContext *cx, uint3
     script = (JSScript *) cx->malloc(size);
     if (!script)
         return NULL;
 
     PodZero(script);
     script->length = length;
     script->setVersion(cx->findVersion());
 
+    uint8 *scriptEnd = reinterpret_cast<uint8 *>(script + 1);
+
     cursor = (uint8 *)script + sizeof(JSScript);
     if (nobjects != 0) {
-        script->objectsOffset = (uint8)(cursor - (uint8 *)script);
+        script->objectsOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(JSObjectArray);
+    } else {
+        script->objectsOffset = JSScript::INVALID_OFFSET;
     }
     if (nupvars != 0) {
-        script->upvarsOffset = (uint8)(cursor - (uint8 *)script);
+        script->upvarsOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(JSUpvarArray);
+    } else {
+        script->upvarsOffset = JSScript::INVALID_OFFSET;
     }
     if (nregexps != 0) {
-        script->regexpsOffset = (uint8)(cursor - (uint8 *)script);
+        script->regexpsOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(JSObjectArray);
+    } else {
+        script->regexpsOffset = JSScript::INVALID_OFFSET;
     }
     if (ntrynotes != 0) {
-        script->trynotesOffset = (uint8)(cursor - (uint8 *)script);
+        script->trynotesOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(JSTryNoteArray);
+    } else {
+        script->trynotesOffset = JSScript::INVALID_OFFSET;
     }
     if (nglobals != 0) {
-        script->globalsOffset = (uint8)(cursor - (uint8 *)script);
+        script->globalsOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(GlobalSlotArray);
+    } else {
+        script->globalsOffset = JSScript::INVALID_OFFSET;
     }
-    JS_ASSERT((cursor - (uint8 *)script) <= 0xFF);
+    JS_ASSERT((cursor - (uint8 *)script) < 0xFF);
     if (nconsts != 0) {
-        script->constOffset = (uint8)(cursor - (uint8 *)script);
+        script->constOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(JSConstArray);
+    } else {
+        script->constOffset = JSScript::INVALID_OFFSET;
     }
 
-    JS_STATIC_ASSERT(sizeof(JSScript) +
-                     sizeof(JSObjectArray) +
+    JS_STATIC_ASSERT(sizeof(JSObjectArray) +
                      sizeof(JSUpvarArray) +
                      sizeof(JSObjectArray) +
                      sizeof(JSTryNoteArray) +
-                     sizeof(GlobalSlotArray) <= 0xFF);
+                     sizeof(GlobalSlotArray) < 0xFF);
 
     if (natoms != 0) {
         script->atomMap.length = natoms;
         script->atomMap.vector = (JSAtom **)cursor;
         vectorSize = natoms * sizeof(script->atomMap.vector[0]);
 
         /*
          * Clear object map's vector so the GC tracing can run when not yet
@@ -1092,94 +1047,16 @@ JSScript::NewScriptFromCG(JSContext *cx,
     /* The counts of indexed things must be checked during code generation. */
     JS_ASSERT(cg->atomList.count <= INDEX_LIMIT);
     JS_ASSERT(cg->objectList.length <= INDEX_LIMIT);
     JS_ASSERT(cg->regexpList.length <= INDEX_LIMIT);
 
     mainLength = CG_OFFSET(cg);
     prologLength = CG_PROLOG_OFFSET(cg);
 
-    if (prologLength + mainLength <= 3 &&
-        !(cg->flags & TCF_IN_FUNCTION)) {
-        /*
-         * Check very short scripts to see whether they are "empty" and return
-         * the const empty-script singleton if so.
-         */
-        jsbytecode *pc = prologLength ? CG_PROLOG_BASE(cg) : CG_BASE(cg);
-
-        if ((cg->flags & TCF_NO_SCRIPT_RVAL) && JSOp(*pc) == JSOP_FALSE)
-            ++pc;
-
-        if (JSOp(*pc) == JSOP_STOP &&
-            !cx->debugHooks->newScriptHook &&
-            !(cg->flags & TCF_NEED_MUTABLE_SCRIPT))
-        {
-            /*
-             * We can probably use the immutable empty script singleton, just
-             * two hard cases (nupvars != 0, strict mode code) may stand in our
-             * way.
-             */
-            JSScript *empty = JSScript::emptyScript();
-
-            if (cg->inFunction()) {
-                fun = cg->fun();
-                JS_ASSERT(fun->isInterpreted() && !FUN_SCRIPT(fun));
-                if (cg->flags & TCF_STRICT_MODE_CODE) {
-                    /*
-                     * We can't use a script singleton for empty strict mode
-                     * functions because they have poison-pill caller and
-                     * arguments properties:
-                     *
-                     * function strict() { "use strict"; }
-                     * strict.caller; // calls [[ThrowTypeError]] function
-                     */
-                    goto skip_empty;
-                }
-                if (fun->u.i.nupvars != 0) {
-                    /*
-                     * FIXME: upvar uses that were all optimized away may leave
-                     * fun->u.i.nupvars non-zero, and since that count is added
-                     * into fun->countLocalNames() in order to discriminate the
-                     * fun->u.i.names union, we cannot force fun->u.i.nupvars
-                     * to 0 to match JSScript::emptyScript()->upvars()->length.
-                     * So we skip the empty script optimization.
-                     *
-                     * Fixing this requires the compiler to track upvar uses as
-                     * it analyzes and optimizes closures, and subsequently as
-                     * the emitter performs useless expression elimination.
-                     */
-                    goto skip_empty;
-                }
-                fun->freezeLocalNames(cx);
-                fun->u.i.script = empty;
-            }
-
-#ifdef DEBUG
-            {
-                jsrefcount newEmptyLive = JS_RUNTIME_METER(cx->runtime, liveEmptyScripts);
-                jsrefcount newLive = cx->runtime->liveScripts;
-                jsrefcount newTotal =
-                    JS_RUNTIME_METER(cx->runtime, totalEmptyScripts) + cx->runtime->totalScripts;
-
-                jsrefcount oldHigh = cx->runtime->highWaterLiveScripts;
-                if (newEmptyLive + newLive > oldHigh) {
-                    JS_ATOMIC_SET(&cx->runtime->highWaterLiveScripts, newEmptyLive + newLive);
-                    if (getenv("JS_DUMP_LIVE_SCRIPTS")) {
-                        fprintf(stderr, "high water script count: %d empty, %d not (total %d)\n",
-                                newEmptyLive, newLive, newTotal);
-                    }
-                }
-            }
-#endif
-
-            return empty;
-        }
-    }
-
-  skip_empty:
     CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
     uint16 nClosedArgs = uint16(cg->closedArgs.length());
     JS_ASSERT(nClosedArgs == cg->closedArgs.length());
     uint16 nClosedVars = uint16(cg->closedVars.length());
     JS_ASSERT(nClosedVars == cg->closedVars.length());
     script = NewScript(cx, prologLength + mainLength, nsrcnotes,
                        cg->atomList.count, cg->objectList.length,
                        cg->upvarList.count, cg->regexpList.length,
@@ -1263,17 +1140,17 @@ JSScript::NewScriptFromCG(JSContext *cx,
     /*
      * We initialize fun->u.script to be the script constructed above
      * so that the debugger has a valid FUN_SCRIPT(fun).
      */
     fun = NULL;
     if (cg->inFunction()) {
         fun = cg->fun();
         JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
-        if (script->upvarsOffset != 0)
+        if (JSScript::isValidOffset(script->upvarsOffset))
             JS_ASSERT(script->upvars()->length == fun->u.i.nupvars);
         else
             fun->u.i.nupvars = 0;
 
         fun->freezeLocalNames(cx);
         fun->u.i.script = script;
 #ifdef CHECK_SCRIPT_OWNER
         script->owner = NULL;
@@ -1281,20 +1158,29 @@ JSScript::NewScriptFromCG(JSContext *cx,
         if (cg->flags & TCF_FUN_HEAVYWEIGHT)
             fun->flags |= JSFUN_HEAVYWEIGHT;
     }
 
     /* Tell the debugger about this compiled script. */
     js_CallNewScriptHook(cx, script, fun);
 #ifdef DEBUG
     {
-        jsrefcount newLive = JS_RUNTIME_METER(cx->runtime, liveScripts);
-        jsrefcount newEmptyLive = cx->runtime->liveEmptyScripts;
-        jsrefcount newTotal =
-            JS_RUNTIME_METER(cx->runtime, totalScripts) + cx->runtime->totalEmptyScripts;
+        jsrefcount newEmptyLive, newLive, newTotal;
+        if (script->isEmpty()) {
+            newEmptyLive = JS_RUNTIME_METER(cx->runtime, liveEmptyScripts);
+            newLive = cx->runtime->liveScripts;
+            newTotal =
+                JS_RUNTIME_METER(cx->runtime, totalEmptyScripts) + cx->runtime->totalScripts;
+        } else {
+            newEmptyLive = cx->runtime->liveEmptyScripts;
+            newLive = JS_RUNTIME_METER(cx->runtime, liveScripts);
+            newTotal =
+                cx->runtime->totalEmptyScripts + JS_RUNTIME_METER(cx->runtime, totalScripts);
+        }
+
         jsrefcount oldHigh = cx->runtime->highWaterLiveScripts;
         if (newEmptyLive + newLive > oldHigh) {
             JS_ATOMIC_SET(&cx->runtime->highWaterLiveScripts, newEmptyLive + newLive);
             if (getenv("JS_DUMP_LIVE_SCRIPTS")) {
                 fprintf(stderr, "high water script count: %d empty, %d not (total %d)\n",
                         newEmptyLive, newLive, newTotal);
             }
         }
@@ -1306,50 +1192,44 @@ JSScript::NewScriptFromCG(JSContext *cx,
 bad:
     js_DestroyScript(cx, script);
     return NULL;
 }
 
 JS_FRIEND_API(void)
 js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
 {
-    JS_ASSERT(script != JSScript::emptyScript());
-
     JSNewScriptHook hook;
 
     hook = cx->debugHooks->newScriptHook;
     if (hook) {
         AutoKeepAtoms keep(cx->runtime);
         hook(cx, script->filename, script->lineno, script, fun,
              cx->debugHooks->newScriptHookData);
     }
 }
 
 JS_FRIEND_API(void)
 js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
 {
-    JS_ASSERT(script != JSScript::emptyScript());
-
     JSDestroyScriptHook hook;
 
     hook = cx->debugHooks->destroyScriptHook;
     if (hook)
         hook(cx, script, cx->debugHooks->destroyScriptHookData);
 }
 
 static void
 DestroyScript(JSContext *cx, JSScript *script, JSThreadData *data)
 {
-    if (script == JSScript::emptyScript()) {
+#ifdef DEBUG
+    if (script->isEmpty())
         JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts);
-        return;
-    }
-
-#ifdef DEBUG
-    JS_RUNTIME_UNMETER(cx->runtime, liveScripts);
+    else
+        JS_RUNTIME_UNMETER(cx->runtime, liveScripts);
 #endif
 
     js_CallDestroyScriptHook(cx, script);
     JS_ClearScriptTraps(cx, script);
 
     if (script->principals)
         JSPRINCIPALS_DROP(cx, script->principals);
 
@@ -1432,41 +1312,41 @@ js_DestroyScriptFromGC(JSContext *cx, JS
 }
 
 void
 js_TraceScript(JSTracer *trc, JSScript *script)
 {
     JSAtomMap *map = &script->atomMap;
     MarkAtomRange(trc, map->length, map->vector, "atomMap");
 
-    if (script->objectsOffset != 0) {
+    if (JSScript::isValidOffset(script->objectsOffset)) {
         JSObjectArray *objarray = script->objects();
         uintN i = objarray->length;
         do {
             --i;
             if (objarray->vector[i]) {
                 JS_SET_TRACING_INDEX(trc, "objects", i);
                 Mark(trc, objarray->vector[i]);
             }
         } while (i != 0);
     }
 
-    if (script->regexpsOffset != 0) {
+    if (JSScript::isValidOffset(script->regexpsOffset)) {
         JSObjectArray *objarray = script->regexps();
         uintN i = objarray->length;
         do {
             --i;
             if (objarray->vector[i]) {
                 JS_SET_TRACING_INDEX(trc, "regexps", i);
                 Mark(trc, objarray->vector[i]);
             }
         } while (i != 0);
     }
 
-    if (script->constOffset != 0) {
+    if (JSScript::isValidOffset(script->constOffset)) {
         JSConstArray *constarray = script->consts();
         MarkValueRange(trc, constarray->length, constarray->vector, "consts");
     }
 
     if (script->u.object) {
         JS_SET_TRACING_NAME(trc, "object");
         Mark(trc, script->u.object);
     }
@@ -1476,17 +1356,16 @@ js_TraceScript(JSTracer *trc, JSScript *
 }
 
 JSBool
 js_NewScriptObject(JSContext *cx, JSScript *script)
 {
     AutoScriptRooter root(cx, script);
 
     JS_ASSERT(!script->u.object);
-    JS_ASSERT(script != JSScript::emptyScript());
 
     JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_ScriptClass, NULL, NULL);
     if (!obj)
         return JS_FALSE;
     obj->setPrivate(script);
     script->u.object = obj;
 
     /*
@@ -1728,17 +1607,16 @@ class DisablePrincipalsTranscoding {
         if (callbacks)
             callbacks->principalsTranscoder = temp;
     }
 };
 
 JSScript *
 js_CloneScript(JSContext *cx, JSScript *script)
 {
-    JS_ASSERT(script != JSScript::emptyScript());
     JS_ASSERT(cx->compartment != script->compartment);
     JS_ASSERT(script->compartment);
 
     // serialize script
     JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
     if (!w)
         return NULL;
 
@@ -1765,17 +1643,17 @@ js_CloneScript(JSContext *cx, JSScript *
     }
 
     // Hand p off from w to r.  Don't want them to share the data
     // mem, lest they both try to free it in JS_XDRDestroy
     JS_XDRMemSetData(r, p, nbytes);
     JS_XDRMemSetData(w, NULL, 0);
 
     // We can't use the public API because it makes a script object.
-    if (!js_XDRScript(r, &script, true, NULL))
+    if (!js_XDRScript(r, &script, NULL))
         return NULL;
 
     JS_XDRDestroy(r);
     JS_XDRDestroy(w);
 
     // set the proper principals for the script
     script->principals = script->compartment->principals;
     if (script->principals)
@@ -1784,9 +1662,8 @@ js_CloneScript(JSContext *cx, JSScript *
     return script;
 }
 
 void
 JSScript::copyClosedSlotsTo(JSScript *other)
 {
     memcpy(other->closedSlots, closedSlots, nClosedArgs + nClosedVars);
 }
-
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -193,50 +193,47 @@ struct JSScript {
      * does *not* call a non-null cx->runtime->newScriptHook -- only the second,
      * NewScriptFromCG, calls this optional debugger hook.
      *
      * The NewScript function can't know whether the script it creates belongs
      * to a function, or is top-level or eval code, but the debugger wants access
      * to the newly made script's function, if any -- so callers of NewScript
      * are responsible for notifying the debugger after successfully creating any
      * kind (function or other) of new JSScript.
-     *
-     * NB: NewScript always creates a new script; it never returns the empty
-     * script singleton (JSScript::emptyScript()). Callers who know they can use
-     * that read-only singleton are responsible for choosing it instead of calling
-     * NewScript with length and nsrcnotes equal to 1 and other parameters save
-     * cx all zero.
      */
     static JSScript *NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
                                uint32 nobjects, uint32 nupvars, uint32 nregexps,
                                uint32 ntrynotes, uint32 nconsts, uint32 nglobals,
                                uint16 nClosedArgs, uint16 nClosedVars);
 
     static JSScript *NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg);
 
     /* FIXME: bug 586181 */
     JSCList         links;      /* Links for compartment script list */
     jsbytecode      *code;      /* bytecodes and their immediate operands */
     uint32          length;     /* length of code vector */
     uint16          version;    /* JS version under which script was compiled */
     uint16          nfixed;     /* number of slots besides stack operands in
                                    slot array */
+
+    /*
+     * Offsets to various array structures from the end of this script, or
+     * JSScript::INVALID_OFFSET if the array has length 0.
+     */
     uint8           objectsOffset;  /* offset to the array of nested function,
                                        block, scope, xml and one-time regexps
-                                       objects or 0 if none */
+                                       objects */
     uint8           upvarsOffset;   /* offset of the array of display ("up")
-                                       closure vars or 0 if none */
+                                       closure vars */
     uint8           regexpsOffset;  /* offset to the array of to-be-cloned
-                                       regexps or 0 if none. */
-    uint8           trynotesOffset; /* offset to the array of try notes or
-                                       0 if none */
-    uint8           globalsOffset;  /* offset to the array of global slots or
-                                       0 if none */
-    uint8           constOffset;    /* offset to the array of constants or
-                                       0 if none */
+                                       regexps  */
+    uint8           trynotesOffset; /* offset to the array of try notes */
+    uint8           globalsOffset;  /* offset to the array of global slots */
+    uint8           constOffset;    /* offset to the array of constants */
+
     bool            noScriptRval:1; /* no need for result value of last
                                        expression statement */
     bool            savedCallerFun:1; /* object 0 is caller function */
     bool            hasSharps:1;      /* script uses sharp variables */
     bool            strictModeCode:1; /* code is in strict mode */
     bool            compileAndGo:1;   /* script was compiled with TCF_COMPILE_N_GO */
     bool            usesEval:1;       /* script uses eval() */
     bool            usesArguments:1;  /* script uses arguments */
@@ -317,44 +314,47 @@ struct JSScript {
             return JITScript_Invalid;
         return JITScript_Valid;
     }
 #endif
 
     /* Script notes are allocated right after the code. */
     jssrcnote *notes() { return (jssrcnote *)(code + length); }
 
+    static const uint8 INVALID_OFFSET = 0xFF;
+    static bool isValidOffset(uint8 offset) { return offset != INVALID_OFFSET; }
+
     JSObjectArray *objects() {
-        JS_ASSERT(objectsOffset != 0);
-        return (JSObjectArray *)((uint8 *) this + objectsOffset);
+        JS_ASSERT(isValidOffset(objectsOffset));
+        return (JSObjectArray *)((uint8 *) (this + 1) + objectsOffset);
     }
 
     JSUpvarArray *upvars() {
-        JS_ASSERT(upvarsOffset != 0);
-        return (JSUpvarArray *) ((uint8 *) this + upvarsOffset);
+        JS_ASSERT(isValidOffset(upvarsOffset));
+        return (JSUpvarArray *) ((uint8 *) (this + 1) + upvarsOffset);
     }
 
     JSObjectArray *regexps() {
-        JS_ASSERT(regexpsOffset != 0);
-        return (JSObjectArray *) ((uint8 *) this + regexpsOffset);
+        JS_ASSERT(isValidOffset(regexpsOffset));
+        return (JSObjectArray *) ((uint8 *) (this + 1) + regexpsOffset);
     }
 
     JSTryNoteArray *trynotes() {
-        JS_ASSERT(trynotesOffset != 0);
-        return (JSTryNoteArray *) ((uint8 *) this + trynotesOffset);
+        JS_ASSERT(isValidOffset(trynotesOffset));
+        return (JSTryNoteArray *) ((uint8 *) (this + 1) + trynotesOffset);
     }
 
     js::GlobalSlotArray *globals() {
-        JS_ASSERT(globalsOffset != 0);
-        return (js::GlobalSlotArray *) ((uint8 *)this + globalsOffset);
+        JS_ASSERT(isValidOffset(globalsOffset));
+        return (js::GlobalSlotArray *) ((uint8 *) (this + 1) + globalsOffset);
     }
 
     JSConstArray *consts() {
-        JS_ASSERT(constOffset != 0);
-        return (JSConstArray *) ((uint8 *) this + constOffset);
+        JS_ASSERT(isValidOffset(constOffset));
+        return (JSConstArray *) ((uint8 *) (this + 1) + constOffset);
     }
 
     JSAtom *getAtom(size_t index) {
         JS_ASSERT(index < atomMap.length);
         return atomMap.vector[index];
     }
 
     JSObject *getObject(size_t index) {
@@ -392,51 +392,31 @@ struct JSScript {
         JSConstArray *arr = consts();
         JS_ASSERT(index < arr->length);
         return arr->vector[index];
     }
 
     /*
      * The isEmpty method tells whether this script has code that computes any
      * result (not return value, result AKA normal completion value) other than
-     * JSVAL_VOID, or any other effects. It has a fast path for the case where
-     * |this| is the emptyScript singleton, but it also checks this->length and
-     * this->code, to handle debugger-generated mutable empty scripts.
+     * JSVAL_VOID, or any other effects.
      */
     inline bool isEmpty() const;
 
-    /*
-     * Accessor for the emptyScriptConst singleton, to consolidate const_cast.
-     * See the private member declaration.
-     */
-    static JSScript *emptyScript() {
-        return const_cast<JSScript *>(&emptyScriptConst);
-    }
-
     uint32 getClosedArg(uint32 index) {
         JS_ASSERT(index < nClosedArgs);
         return closedSlots[index];
     }
 
     uint32 getClosedVar(uint32 index) {
         JS_ASSERT(index < nClosedVars);
         return closedSlots[nClosedArgs + index];
     }
 
     void copyClosedSlotsTo(JSScript *other);
-
-  private:
-    /*
-     * Use const to put this in read-only memory if possible. We are stuck with
-     * non-const JSScript * and jsbytecode * by legacy code (back in the 1990s,
-     * const wasn't supported correctly on all target platforms). The debugger
-     * does mutate bytecode, and script->u.object may be set after construction
-     * in some cases, so making JSScript pointers const will be "hard".
-     */
-    static const JSScript emptyScriptConst;
 };
 
 #define SHARP_NSLOTS            2       /* [#array, #depth] slots if the script
                                            uses sharp variables */
 
 static JS_INLINE uintN
 StackDepth(JSScript *script)
 {
@@ -570,23 +550,16 @@ extern JSScript *
 js_CloneScript(JSContext *cx, JSScript *script);
 
 /*
  * If magic is non-null, js_XDRScript succeeds on magic number mismatch but
  * returns false in *magic; it reflects a match via a true *magic out param.
  * If magic is null, js_XDRScript returns false on bad magic number errors,
  * which it reports.
  *
- * NB: after a successful JSXDR_DECODE, and provided that *scriptp is not the
- * JSScript::emptyScript() immutable singleton, js_XDRScript callers must do
- * any required subsequent set-up of owning function or script object and then
- * call js_CallNewScriptHook.
- *
- * If the caller requires a mutable empty script (for debugging or u.object
- * ownership setting), pass true for needMutableScript. Otherwise pass false.
- * Call js_CallNewScriptHook only with a mutable script, i.e. never with the
- * JSScript::emptyScript() singleton.
+ * NB: after a successful JSXDR_DECODE, js_XDRScript callers must do any
+ * required subsequent set-up of owning function or script object and then call
+ * js_CallNewScriptHook.
  */
 extern JSBool
-js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
-             JSBool *hasMagic);
+js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic);
 
 #endif /* jsscript_h___ */
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -65,28 +65,18 @@ JSScript::getRegExp(size_t index)
     JSObject *obj = arr->vector[index];
     JS_ASSERT(obj->getClass() == &js_RegExpClass);
     return obj;
 }
 
 inline bool
 JSScript::isEmpty() const
 {
-    return (this == emptyScript());
-
-    // See bug 603044 comment #21.
-#if 0
-    if (this == emptyScript())
-        return true;
+    if (length > 3)
+        return false;
 
-    if (length <= 3) {
-        jsbytecode *pc = code;
-
-        if (noScriptRval && JSOp(*pc) == JSOP_FALSE)
-            ++pc;
-        if (JSOp(*pc) == JSOP_STOP)
-            return true;
-    }
-    return false;
-#endif
+    jsbytecode *pc = code;
+    if (noScriptRval && JSOp(*pc) == JSOP_FALSE)
+        ++pc;
+    return JSOp(*pc) == JSOP_STOP;
 }
 
 #endif /* jsscriptinlines_h___ */
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1379,18 +1379,18 @@ RopeMatch(JSString *textstr, const jscha
     /*
      * We don't want to do rope matching if there is a poor node-to-char ratio,
      * since this means spending a lot of time in the match loop below. We also
      * need to build the list of leaf nodes. Do both here: iterate over the
      * nodes so long as there are not too many.
      */
     size_t textstrlen = textstr->length();
     size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2;
-    JSRopeLeafIterator iter(textstr);
-    for (JSString *str = iter.init(); str; str = iter.next()) {
+    JSRopeLeafIterator iter;
+    for (JSString *str = iter.init(textstr); str; str = iter.next()) {
         if (threshold-- == 0 || !strs.append(str))
             return StringMatch(textstr->chars(), textstrlen, pat, patlen);
     }
 
     /* Absolute offset from the beginning of the logical string textstr. */
     jsint pos = 0;
 
     // TODO: consider branching to a simple loop if patlen == 1
@@ -1866,17 +1866,17 @@ static bool
 BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value *vp)
 {
     if (fm.match() < 0) {
         vp->setNull();
         return true;
     }
 
     /* For this non-global match, produce a RegExp.exec-style array. */
-    JSObject *obj = js_NewSlowArrayObject(cx);
+    JSObject *obj = NewSlowEmptyArray(cx);
     if (!obj)
         return false;
     vp->setObject(*obj);
 
     return obj->defineProperty(cx, INT_TO_JSID(0), StringValue(fm.pattern())) &&
            obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.indexAtom),
                                Int32Value(fm.match())) &&
            obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.inputAtom),
@@ -1891,17 +1891,17 @@ typedef JSObject **MatchArgType;
  */
 static bool
 MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
 {
     JS_ASSERT(count <= JSID_INT_MAX);  /* by max string length */
 
     JSObject *&arrayobj = *static_cast<MatchArgType>(p);
     if (!arrayobj) {
-        arrayobj = js_NewArrayObject(cx, 0, NULL);
+        arrayobj = NewDenseEmptyArray(cx);
         if (!arrayobj)
             return false;
     }
 
     Value v;
     if (!res->createLastMatch(cx, &v))
         return false;
 
@@ -2237,19 +2237,19 @@ BuildFlatReplacement(JSContext *cx, JSSt
     size_t match = fm.match(); /* Avoid signed/unsigned warnings. */
     size_t matchEnd = match + fm.patternLength();
 
     if (textstr->isTopNode()) {
         /*
          * If we are replacing over a rope, avoid flattening it by iterating
          * through it, building a new rope.
          */
-        JSRopeLeafIterator iter(textstr);
+        JSRopeLeafIterator iter;
         size_t pos = 0;
-        for (JSString *str = iter.init(); str; str = iter.next()) {
+        for (JSString *str = iter.init(textstr); str; str = iter.next()) {
             size_t len = str->length();
             size_t strEnd = pos + len;
             if (pos < matchEnd && strEnd > match) {
                 /*
                  * We need to special-case any part of the rope that overlaps
                  * with the replacement string.
                  */
                 if (match >= pos) {
@@ -2674,17 +2674,17 @@ find_split(JSContext *cx, RegExpStatics 
 static JSBool
 str_split(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *str;
     NORMALIZE_THIS(cx, vp, str);
 
     if (argc == 0) {
         Value v = StringValue(str);
-        JSObject *aobj = js_NewArrayObject(cx, 1, &v);
+        JSObject *aobj = NewDenseCopiedArray(cx, 1, &v);
         if (!aobj)
             return false;
         vp->setObject(*aobj);
         return true;
     }
 
     RegExp *re;
     JSSubString *sep, tmp;
@@ -2757,17 +2757,17 @@ str_split(JSContext *cx, uintN argc, Val
             sep->chars = NULL;
         }
         i = j + sep->length;
     }
 
     if (j == -2)
         return false;
 
-    JSObject *aobj = js_NewArrayObject(cx, splits.length(), splits.begin());
+    JSObject *aobj = NewDenseCopiedArray(cx, splits.length(), splits.begin());
     if (!aobj)
         return false;
     vp->setObject(*aobj);
     return true;
 }
 
 #if JS_HAS_PERL_SUBSTR
 static JSBool
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -625,30 +625,23 @@ class JSRopeNodeIterator {
   private:
     JSString *mStr;
     size_t mUsedFlags;
 
     static const size_t DONE_LEFT = 0x1;
     static const size_t DONE_RIGHT = 0x2;
 
   public:
-    JSRopeNodeIterator(JSString *str)
-      : mUsedFlags(0)
-    {
+    JSRopeNodeIterator()
+      : mStr(NULL), mUsedFlags(0)
+    {}
+
+    JSString *init(JSString *str) {
+        /* Move to the farthest-left leaf in the rope. */
         mStr = str;
-    }
-    
-    JSString *init() {
-        /* If we were constructed with a non-rope string, just return that. */
-        if (!mStr->isRope()) {
-            JSString *oldStr = mStr;
-            mStr = NULL;
-            return oldStr;
-        }
-        /* Move to the farthest-left leaf in the rope. */
         while (mStr->isInteriorNode())
             mStr = mStr->interiorNodeParent();
         while (mStr->ropeLeft()->isInteriorNode())
             mStr = mStr->ropeLeft();
         JS_ASSERT(mUsedFlags == 0);
         return mStr;
     }
 
@@ -692,24 +685,19 @@ class JSRopeNodeIterator {
  * An iterator that returns the leaves of a rope (which hold the actual string
  * data) in order. The usage is the same as JSRopeNodeIterator.
  */
 class JSRopeLeafIterator {
   private:
     JSRopeNodeIterator mNodeIterator;
 
   public:
-
-    JSRopeLeafIterator(JSString *topNode) :
-        mNodeIterator(topNode) {
-        JS_ASSERT(topNode->isTopNode());
-    }
-
-    inline JSString *init() {
-        JSString *str = mNodeIterator.init();
+    inline JSString *init(JSString *str) {
+        JS_ASSERT(str->isTopNode());
+        str = mNodeIterator.init(str);
         while (str->isRope()) {
             str = mNodeIterator.next();
             JS_ASSERT(str);
         }
         return str;
     }
 
     inline JSString *next() {
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2216,17 +2216,17 @@ TraceRecorder::TraceRecorder(JSContext* 
     lirbuf(new (tempAlloc()) LirBuffer(tempAlloc())),
     mark(*traceMonitor->traceAlloc),
     numSideExitsBefore(tree->sideExits.length()),
     tracker(),
     nativeFrameTracker(),
     global_slots(NULL),
     callDepth(anchor ? anchor->calldepth : 0),
     atoms(FrameAtomBase(cx, cx->fp())),
-    consts(cx->fp()->script()->constOffset
+    consts(JSScript::isValidOffset(cx->fp()->script()->constOffset)
            ? cx->fp()->script()->consts()->vector
            : NULL),
     strictModeCode_ins(NULL),
     cfgMerges(&tempAlloc()),
     trashSelf(false),
     whichTreesToTrash(&tempAlloc()),
     guardedShapeTable(cx),
     initDepth(0),
@@ -2441,17 +2441,17 @@ TraceRecorder::finishSuccessfully()
 JS_REQUIRES_STACK TraceRecorder::AbortResult
 TraceRecorder::finishAbort(const char* reason)
 {
     JS_ASSERT(traceMonitor->recorder == this);
     JS_ASSERT(!fragment->code());
 
     AUDIT(recorderAborted);
 #ifdef DEBUG
-    debug_only_printf(LC_TMMinimal,
+    debug_only_printf(LC_TMMinimal | LC_TMAbort,
                       "Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n",
                       tree->treeFileName,
                       tree->treeLineNumber,
                       tree->treePCOffset,
                       cx->fp()->script()->filename,
                       js_FramePCToLineNumber(cx, cx->fp()),
                       FramePCOffset(cx, cx->fp()),
                       reason);
@@ -2783,16 +2783,23 @@ HasUnreachableGCThings(TreeFragment *f)
 }
 
 void
 TraceMonitor::sweep()
 {
     JS_ASSERT(!ontrace());
     debug_only_print0(LC_TMTracer, "Purging fragments with dead things");
 
+    bool shouldAbortRecording = false;
+    TreeFragment *recorderTree = NULL;
+    if (recorder) {
+        recorderTree = recorder->getTree();
+        shouldAbortRecording = HasUnreachableGCThings(recorderTree);
+    }
+        
     for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
         TreeFragment** fragp = &vmfragments[i];
         while (TreeFragment* frag = *fragp) {
             TreeFragment* peer = frag;
             do {
                 if (HasUnreachableGCThings(peer))
                     break;
                 peer = peer->peer;
@@ -2800,27 +2807,29 @@ TraceMonitor::sweep()
             if (peer) {
                 debug_only_printf(LC_TMTracer,
                                   "TreeFragment peer %p has dead gc thing."
                                   "Disconnecting tree %p with ip %p\n",
                                   (void *) peer, (void *) frag, frag->ip);
                 JS_ASSERT(frag->root == frag);
                 *fragp = frag->next;
                 do {
-                    verbose_only( FragProfiling_FragFinalizer(frag, this); )
+                    verbose_only( FragProfiling_FragFinalizer(frag, this); );
+                    if (recorderTree == frag)
+                        shouldAbortRecording = true;
                     TrashTree(frag);
                     frag = frag->peer;
                 } while (frag);
             } else {
                 fragp = &frag->next;
             }
         }
     }
 
-    if (recorder && HasUnreachableGCThings(recorder->getTree()))
+    if (shouldAbortRecording)
         recorder->finishAbort("dead GC things");
 }
 
 /*
  * Box a value from the native stack back into the Value format.
  */
 static inline void
 NativeToValue(JSContext* cx, Value& v, JSValueType type, double* slot)
@@ -4284,26 +4293,26 @@ TraceRecorder::guard(bool expected, LIns
     JS_ASSERT(isCond(cond));
 
     if ((cond->isImmI(0) && expected) || (cond->isImmI(1) && !expected)) {
         if (abortIfAlwaysExits) {
             /* The guard always exits, the caller must check for an abort. */
             RETURN_STOP("Constantly false guard detected");
         }
         /*
-         * If this assertion fails, first decide if you want recording to
+         * If you hit this assertion, first decide if you want recording to
          * abort in the case where the guard always exits.  If not, find a way
          * to detect that case and avoid calling guard().  Otherwise, change
          * the invocation of guard() so it passes in abortIfAlwaysExits=true,
          * and have the caller check the return value, eg. using
          * CHECK_STATUS().  (In optimized builds, we'll fall through to the
          * insGuard() below and an always-exits guard will be inserted, which
          * is correct but sub-optimal.)
          */
-        JS_ASSERT(0);
+        JS_NOT_REACHED("unexpected constantly false guard detected");
     }
 
     /*
      * Nb: if the guard is never taken, no instruction will be created and
      * insGuard() will return NULL.  This is a good thing.
      */
     GuardRecord* guardRec = createGuardRecord(exit);
     expected ? w.xf(cond, guardRec) : w.xt(cond, guardRec);
@@ -7879,27 +7888,27 @@ TraceRecorder::stackval(int n) const
     return cx->regs->sp[n];
 }
 
 JS_REQUIRES_STACK void
 TraceRecorder::updateAtoms()
 {
     JSScript *script = cx->fp()->script();
     atoms = FrameAtomBase(cx, cx->fp());
-    consts = cx->fp()->hasImacropc() || script->constOffset == 0
-           ? 0 
-           : script->consts()->vector;
+    consts = (cx->fp()->hasImacropc() || !JSScript::isValidOffset(script->constOffset))
+             ? 0
+             : script->consts()->vector;
     strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict");
 }
 
 JS_REQUIRES_STACK void
 TraceRecorder::updateAtoms(JSScript *script)
 {
     atoms = script->atomMap.vector;
-    consts = script->constOffset == 0 ? 0 : script->consts()->vector;
+    consts = JSScript::isValidOffset(script->constOffset) ? script->consts()->vector : 0;
     strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict");
 }
 
 /*
  * Generate LIR to compute the scope chain.
  */
 JS_REQUIRES_STACK LIns*
 TraceRecorder::scopeChain()
@@ -10834,23 +10843,30 @@ TraceRecorder::newString(JSObject* ctor,
 
 RecordingStatus
 TraceRecorder::newArray(JSObject* ctor, uint32 argc, Value* argv, Value* rval)
 {
     LIns *proto_ins;
     CHECK_STATUS(getClassPrototype(ctor, proto_ins));
 
     LIns *arr_ins;
-    if (argc == 0 || (argc == 1 && argv[0].isNumber())) {
-        LIns *args[] = { argc == 0 ? w.immi(0) : d2i(get(argv)), proto_ins, cx_ins };
-        arr_ins = w.call(&js_NewEmptyArray_ci, args);
+    if (argc == 0) {
+        LIns *args[] = { proto_ins, cx_ins };
+        arr_ins = w.call(&js::NewDenseEmptyArray_ci, args);
         guard(false, w.eqp0(arr_ins), OOM_EXIT);
-    } else {
-        LIns *args[] = { w.nameImmi(argc), proto_ins, cx_ins };
-        arr_ins = w.call(&js_NewPreallocatedArray_ci, args);
+
+    } else if (argc == 1 && argv[0].isNumber()) {
+        /* Abort on RangeError if the double doesn't fit in a uint. */
+        LIns *args[] = { proto_ins, d2i(get(argv)), cx_ins };
+        arr_ins = w.call(&js::NewDenseUnallocatedArray_ci, args);
+        guard(false, w.eqp0(arr_ins), OOM_EXIT);
+
+    } else {
+        LIns *args[] = { proto_ins, w.nameImmi(argc), cx_ins };
+        arr_ins = w.call(&js::NewDenseAllocatedArray_ci, args);
         guard(false, w.eqp0(arr_ins), OOM_EXIT);
 
         // arr->slots[i] = box_jsval(vp[i]);  for i in 0..argc
         LIns *slots_ins = NULL;
         for (uint32 i = 0; i < argc && !outOfMemory(); i++) {
             stobj_set_dslot(arr_ins, i, slots_ins, argv[i], get(&argv[i]));
         }
     }
@@ -11160,17 +11176,17 @@ TraceRecorder::callNative(uintN argc, JS
                     if (ceilReturningInt(vp[2].toNumber(), &result))
                         return callFloatReturningInt(argc, &ceilReturningInt_ci);
                 } else if (native == js_math_round) {
                     if (roundReturningInt(vp[2].toNumber(), &result))
                         return callFloatReturningInt(argc, &roundReturningInt_ci);
                 }
             } else if (native == js_math_abs) {
                 LIns* a = get(&vp[2]);
-                if (IsPromoteInt(a)) {
+                if (IsPromoteInt(a) && vp[2].toNumber() != INT_MIN) {
                     a = w.demote(a);
                     /* abs(INT_MIN) can't be done using integers;  exit if we see it. */
                     LIns* intMin_ins = w.name(w.immi(0x80000000), "INT_MIN");
                     LIns* isIntMin_ins = w.name(w.eqi(a, intMin_ins), "isIntMin");
                     guard(false, isIntMin_ins, MISMATCH_EXIT);
                     LIns* neg_ins = w.negi(a);
                     LIns* isNeg_ins = w.name(w.ltiN(a, 0), "isNeg");
                     LIns* abs_ins = w.name(w.cmovi(isNeg_ins, neg_ins, a), "abs");
@@ -11178,27 +11194,31 @@ TraceRecorder::callNative(uintN argc, JS
                     pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK;
                     return RECORD_CONTINUE;
                 }
             }
             if (vp[1].isString()) {
                 JSString *str = vp[1].toString();
                 if (native == js_str_charAt) {
                     jsdouble i = vp[2].toNumber();
+                    if (JSDOUBLE_IS_NaN(i))
+                      i = 0;
                     if (i < 0 || i >= str->length())
                         RETURN_STOP("charAt out of bounds");
                     LIns* str_ins = get(&vp[1]);
                     LIns* idx_ins = get(&vp[2]);
                     LIns* char_ins;
                     CHECK_STATUS(getCharAt(str, str_ins, idx_ins, mode, &char_ins));
                     set(&vp[0], char_ins);
                     pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK;
                     return RECORD_CONTINUE;
                 } else if (native == js_str_charCodeAt) {
                     jsdouble i = vp[2].toNumber();
+                    if (JSDOUBLE_IS_NaN(i))
+                      i = 0;
                     if (i < 0 || i >= str->length())
                         RETURN_STOP("charCodeAt out of bounds");
                     LIns* str_ins = get(&vp[1]);
                     LIns* idx_ins = get(&vp[2]);
                     LIns* charCode_ins;
                     CHECK_STATUS(getCharCodeAt(str, str_ins, idx_ins, &charCode_ins));
                     set(&vp[0], charCode_ins);
                     pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK;
@@ -11442,17 +11462,17 @@ TraceRecorder::functionCall(uintN argc, 
      *
      * Bytecode sequences that push shapeless callees must guard on the callee
      * class being Function and the function being interpreted.
      */
     JSFunction* fun = GET_FUNCTION_PRIVATE(cx, &fval.toObject());
 
     if (Probes::callTrackingActive(cx)) {
         JSScript *script = FUN_SCRIPT(fun);
-        if (! script || ! script->isEmpty()) {
+        if (!script || !script->isEmpty()) {
             LIns* args[] = { w.immi(1), w.nameImmpNonGC(fun), cx_ins };
             LIns* call_ins = w.call(&functionProbe_ci, args);
             guard(false, w.eqi0(call_ins), MISMATCH_EXIT);
         }
     }
 
     if (FUN_INTERPRETED(fun))
         return interpretedFunctionCall(fval, fun, argc, mode == JSOP_NEW);
@@ -12768,19 +12788,20 @@ TraceRecorder::setElem(int lval_spindex,
 
         LIns* priv_ins = w.ldpObjPrivate(obj_ins);
 
         // The index was on the stack and is therefore a LIR float; force it to
         // be an integer.                              
         CHECK_STATUS_A(makeNumberInt32(idx_ins, &idx_ins));
 
         // Ensure idx >= 0 && idx < length (by using uint32)
-        guard(true,
-              w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)),
-              OVERFLOW_EXIT);
+        CHECK_STATUS_A(guard(true,
+                             w.name(w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)),
+                                    "inRange"),
+                             OVERFLOW_EXIT, /* abortIfAlwaysExits = */true));
 
         // We're now ready to store
         LIns* data_ins = w.ldpConstTypedArrayData(priv_ins);
         LIns* pidx_ins = w.ui2p(idx_ins);
         LIns* typed_v_ins = v_ins;
 
         // If it's not a number, convert objects to NaN,
         // null to 0, and call StringToNumber or BooleanOrUndefinedToNumber
@@ -13193,23 +13214,21 @@ TraceRecorder::guardArguments(JSObject *
     return afp;
 }
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, bool constructing)
 {
     /*
      * The function's identity (JSFunction and therefore JSScript) is guarded,
-     * so we can optimize for the empty script singleton right away. No need to
-     * worry about crossing globals or relocating argv, even, in this case!
-     *
-     * Note that the interpreter shortcuts empty-script call and construct too,
-     * and does not call any TR::record_*CallComplete hook.
-     */
-    if (fun->u.i.script->isEmpty()) {
+     * so we can optimize away the function call if the corresponding script is
+     * empty. No need to worry about crossing globals or relocating argv, even,
+     * in this case!
+     */
+    if (fun->script()->isEmpty()) {
         LIns* rval_ins;
         if (constructing) {
             LIns* args[] = { get(&fval), w.nameImmpNonGC(&js_ObjectClass), cx_ins };
             LIns* tv_ins = w.call(&js_CreateThisFromTrace_ci, args);
             guard(false, w.eqp0(tv_ins), OOM_EXIT);
             rval_ins = tv_ins;
         } else {
             rval_ins = w.immiUndefined();
@@ -13824,17 +13843,17 @@ TraceRecorder::typedArrayElement(Value& 
      *
      * NOTE! mLength is uint32, but it's guaranteed to fit in a Value
      * int, so we can treat it as either signed or unsigned.
      * If the index happens to be negative, when it's treated as
      * unsigned it'll be a very large int, and thus won't be less than
      * length.
      */
     guard(true,
-          w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)),
+          w.name(w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)), "inRange"),
           BRANCH_EXIT);
 
     /* We are now ready to load.  Do a different type of load
      * depending on what type of thing we're loading. */
     LIns* data_ins = w.ldpConstTypedArrayData(priv_ins);
 
     switch (tarray->type) {
       case js::TypedArray::TYPE_INT8:
@@ -14070,18 +14089,18 @@ TraceRecorder::record_JSOP_NEWINIT()
 
     JSProtoKey key = JSProtoKey(cx->regs->pc[1]);
 
     LIns* proto_ins;
     CHECK_STATUS_A(getClassPrototype(key, proto_ins));
 
     LIns *v_ins;
     if (key == JSProto_Array) {
-        LIns *args[] = { w.immi(0), proto_ins, cx_ins };
-        v_ins = w.call(&js_NewPreallocatedArray_ci, args);
+        LIns *args[] = { proto_ins, cx_ins };
+        v_ins = w.call(&NewDenseEmptyArray_ci, args);
     } else {
         LIns *args[] = { w.immpNull(), proto_ins, cx_ins };
         v_ins = w.call(&js_InitializerObject_ci, args);
     }
     guard(false, w.eqp0(v_ins), OOM_EXIT);
     stack(0, v_ins);
     return ARECORD_CONTINUE;
 }
@@ -14090,18 +14109,18 @@ JS_REQUIRES_STACK AbortableRecordingStat
 TraceRecorder::record_JSOP_NEWARRAY()
 {
     initDepth++;
 
     LIns* proto_ins;
     CHECK_STATUS_A(getClassPrototype(JSProto_Array, proto_ins));
 
     unsigned count = GET_UINT24(cx->regs->pc);
-    LIns *args[] = { w.immi(count), proto_ins, cx_ins };
-    LIns *v_ins = w.call(&js_NewPreallocatedArray_ci, args);
+    LIns *args[] = { proto_ins, w.immi(count), cx_ins };
+    LIns *v_ins = w.call(&NewDenseAllocatedArray_ci, args);
 
     guard(false, w.eqp0(v_ins), OOM_EXIT);
     stack(0, v_ins);
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_NEWOBJECT()
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -1208,17 +1208,17 @@ class TypedArrayTemplate
         }
 
         return true;
     }
 
     bool
     copyFromWithOverlap(JSContext *cx, TypedArray *tarray, jsuint offset = 0)
     {
-        JS_ASSERT(offset < length);
+        JS_ASSERT(offset <= length);
 
         NativeType *dest = static_cast<NativeType*>(data) + offset;
 
         if (tarray->type == type) {
             memmove(dest, tarray->data, tarray->byteLength);
             return true;
         }
 
--- a/js/src/jsval.h
+++ b/js/src/jsval.h
@@ -784,20 +784,20 @@ extern "C++"
     operator!=(jsval lhs, jsval rhs)
     {
         return lhs.asBits != rhs.asBits;
     }
 }
 # endif /* defined(__cplusplus) */
 
 /* Internal helper macros */
-#define JSVAL_BITS(v)    (v.asBits)
+#define JSVAL_BITS(v)    ((v).asBits)
 #define JSVAL_FROM_LAYOUT(l) (l)
 #define IMPL_TO_JSVAL(v) (v)
-#define JSID_BITS(id)    (id.asBits)
+#define JSID_BITS(id)    ((id).asBits)
 
 #else /* defined(JS_USE_JSVAL_JSID_STRUCT_TYPES) */
 
 /* Use different primitive types so overloading works. */
 typedef JSVAL_ALIGNMENT uint64 jsval;
 typedef ptrdiff_t              jsid;
 
 /* Internal helper macros */
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -521,22 +521,37 @@ static bool
 CanReify(Value *vp)
 {
     JSObject *obj;
     return vp->isObject() &&
            (obj = &vp->toObject())->getClass() == &js_IteratorClass &&
            (obj->getNativeIterator()->flags & JSITER_ENUMERATE);
 }
 
+struct AutoCloseIterator
+{
+    AutoCloseIterator(JSContext *cx, JSObject *obj) : cx(cx), obj(obj) {}
+
+    ~AutoCloseIterator() { if (obj) js_CloseIterator(cx, obj); }
+
+    void clear() { obj = NULL; }
+
+  private:
+    JSContext *cx;
+    JSObject *obj;
+};
+
 static bool
 Reify(JSContext *cx, JSCompartment *origin, Value *vp)
 {
     JSObject *iterObj = &vp->toObject();
     NativeIterator *ni = iterObj->getNativeIterator();
 
+    AutoCloseIterator close(cx, iterObj);
+
     /* Wrap the iteratee. */
     JSObject *obj = ni->obj;
     if (!origin->wrap(cx, &obj))
         return false;
 
     /*
      * Wrap the elements in the iterator's snapshot.
      * N.B. the order of closing/creating iterators is important due to the
@@ -551,16 +566,17 @@ Reify(JSContext *cx, JSCompartment *orig
                 return false;
             for (size_t i = 0; i < length; ++i) {
                 keys[i] = ni->beginKey()[i];
                 if (!origin->wrapId(cx, &keys[i]))
                     return false;
             }
         }
 
+        close.clear();
         return js_CloseIterator(cx, iterObj) &&
                VectorToKeyIterator(cx, obj, ni->flags, keys, vp);
     }
 
     size_t length = ni->numValues();
     AutoValueVector vals(cx);
     if (length > 0) {
         if (!vals.resize(length))
@@ -568,16 +584,17 @@ Reify(JSContext *cx, JSCompartment *orig
         for (size_t i = 0; i < length; ++i) {
             vals[i] = ni->beginValue()[i];
             if (!origin->wrap(cx, &vals[i]))
                 return false;
         }
 
     }
 
+    close.clear();
     return js_CloseIterator(cx, iterObj) &&
            VectorToValueIterator(cx, obj, ni->flags, vals, vp);
 }
 
 bool
 JSCrossCompartmentWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp)
 {
     PIERCE(cx, wrapper, GET,
--- a/js/src/jsxdrapi.cpp
+++ b/js/src/jsxdrapi.cpp
@@ -664,23 +664,22 @@ js_XDRAtom(JSXDRState *xdr, JSAtom **ato
         return JS_FALSE;
     *atomp = atom;
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_XDRScript(JSXDRState *xdr, JSScript **scriptp)
 {
-    if (!js_XDRScript(xdr, scriptp, true, NULL))
+    if (!js_XDRScript(xdr, scriptp, NULL))
         return JS_FALSE;
 
     if (xdr->mode == JSXDR_DECODE) {
         js_CallNewScriptHook(xdr->cx, *scriptp, NULL);
-        if (*scriptp != JSScript::emptyScript() &&
-            !js_NewScriptObject(xdr->cx, *scriptp)) {
+        if (!js_NewScriptObject(xdr->cx, *scriptp)) {
             js_DestroyScript(xdr->cx, *scriptp);
             *scriptp = NULL;
             return JS_FALSE;
         }
     }
 
     return JS_TRUE;
 }
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -5769,17 +5769,17 @@ FindInScopeNamespaces(JSContext *cx, JSX
 
 /*
  * Populate a new JS array with elements of array and place the result into
  * rval.  rval must point to a rooted location.
  */
 static bool
 NamespacesToJSArray(JSContext *cx, JSXMLArray *array, jsval *rval)
 {
-    JSObject *arrayobj = js_NewArrayObject(cx, 0, NULL);
+    JSObject *arrayobj = NewDenseEmptyArray(cx);
     if (!arrayobj)
         return false;
     *rval = OBJECT_TO_JSVAL(arrayobj);
 
     AutoValueRooter tvr(cx);
     for (uint32 i = 0, n = array->length; i < n; i++) {
         JSObject *ns = XMLARRAY_MEMBER(array, i, JSObject);
         if (!ns)
@@ -7461,17 +7461,17 @@ GetXMLFunction(JSContext *cx, JSObject *
     JSObject *target = obj;
     AutoObjectRooter tvr(cx);
     for (;;) {
         if (!js_GetProperty(cx, target, id, Valueify(vp)))
             return false;
         if (VALUE_IS_FUNCTION(cx, *vp))
             return true;
         target = target->getProto();
-        if (target == NULL)
+        if (target == NULL || !target->isNative())
             break;
         tvr.setObject(target);
     }
 
     JSXML *xml = (JSXML *) obj->getPrivate();
     if (!HasSimpleContent(xml))
         return true;
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -117,17 +117,16 @@ mjit::Compiler::Compiler(JSContext *cx, 
     oomInVector(false),
     applyTricks(NoApplyTricks)
 {
 }
 
 CompileStatus
 mjit::Compiler::compile()
 {
-    JS_ASSERT(!script->isEmpty());
     JS_ASSERT_IF(isConstructing, !script->jitCtor);
     JS_ASSERT_IF(!isConstructing, !script->jitNormal);
 
     JITScript **jit = isConstructing ? &script->jitCtor : &script->jitNormal;
     void **checkAddr = isConstructing
                        ? &script->jitArityCheckCtor
                        : &script->jitArityCheckNormal;
 
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -76,17 +76,17 @@ using namespace JSC;
 
 static jsbytecode *
 FindExceptionHandler(JSContext *cx)
 {
     JSStackFrame *fp = cx->fp();
     JSScript *script = fp->script();
 
 top:
-    if (cx->throwing && script->trynotesOffset) {
+    if (cx->throwing && JSScript::isValidOffset(script->trynotesOffset)) {
         // The PC is updated before every stub call, so we can use it here.
         unsigned offset = cx->regs->pc - script->main;
 
         JSTryNoteArray *tnarray = script->trynotes();
         for (unsigned i = 0; i < tnarray->length; ++i) {
             JSTryNote *tn = &tnarray->vector[i];
 
             // The following if condition actually tests two separate conditions:
@@ -303,28 +303,16 @@ stubs::CompileFunction(VMFrame &f, uint3
 
     /*
      * FixupArity/RemovePartialFrame expect to be called after the early
      * prologue. Pass the existing value for ncode, it has already been set
      * by the jit code calling into this stub.
      */
     fp->initCallFrameEarlyPrologue(fun, nactual);
 
-    /* Empty script does nothing. */
-    bool callingNew = fp->isConstructing();
-    if (script->isEmpty()) {
-        RemovePartialFrame(cx, fp);
-        Value *vp = f.regs.sp - (nactual + 2);
-        if (callingNew)
-            vp[0] = vp[1];
-        else
-            vp[0].setUndefined();
-        return NULL;
-    }
-
     if (nactual != fp->numFormalArgs()) {
         fp = (JSStackFrame *)FixupArity(f, nactual);
         if (!fp)
             return NULL;
     }
 
     /* Finish frame initialization. */
     fp->initCallFrameLatePrologue();
@@ -334,17 +322,17 @@ stubs::CompileFunction(VMFrame &f, uint3
     f.regs.sp = fp->base();
     f.regs.pc = script->code;
 
     if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
         THROWV(NULL);
 
     CompileStatus status = CanMethodJIT(cx, script, fp);
     if (status == Compile_Okay)
-        return script->getJIT(callingNew)->invokeEntry;
+        return script->getJIT(fp->isConstructing())->invokeEntry;
 
     /* Function did not compile... interpret it. */
     JSBool ok = Interpret(cx, fp);
     InlineReturn(f);
 
     if (!ok)
         THROWV(NULL);
 
@@ -417,19 +405,17 @@ void
 stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
 {
     ucr->init();
 
     JSContext *cx = f.cx;
     Value *vp = f.regs.sp - (argc + 2);
 
     /* Try to do a fast inline call before the general Invoke path. */
-    if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted() && 
-        !ucr->fun->script()->isEmpty())
-    {
+    if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted()) {
         ucr->callee = &vp->toObject();
         if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, argc))
             THROW();
     } else {
         if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
             THROW();
     }
 }
@@ -471,22 +457,16 @@ stubs::UncachedCallHelper(VMFrame &f, ui
     JSContext *cx = f.cx;
     Value *vp = f.regs.sp - (argc + 2);
 
     if (IsFunctionObject(*vp, &ucr->callee)) {
         ucr->callee = &vp->toObject();
         ucr->fun = GET_FUNCTION_PRIVATE(cx, ucr->callee);
 
         if (ucr->fun->isInterpreted()) {
-            if (ucr->fun->u.i.script->isEmpty()) {
-                vp->setUndefined();
-                f.regs.sp = vp + 1;
-                return;
-            }
-
             if (!UncachedInlineCall(f, 0, &ucr->codeAddr, argc))
                 THROW();
             return;
         }
 
         if (ucr->fun->isNative()) {
             if (!CallJSNative(cx, ucr->fun->u.n.native, argc, vp))
                 THROW();
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1363,24 +1363,20 @@ stubs::Neg(VMFrame &f)
         THROW();
     d = -d;
     f.regs.sp[-1].setNumber(d);
 }
 
 JSObject * JS_FASTCALL
 stubs::NewInitArray(VMFrame &f, uint32 count)
 {
-    JSContext *cx = f.cx;
-    gc::FinalizeKind kind = GuessObjectGCKind(count, true);
-
-    JSObject *obj = NewArrayWithKind(cx, kind);
-    if (!obj || !obj->ensureSlots(cx, count))
+    JSObject *obj = NewDenseAllocatedArray(f.cx, count);
+    if (!obj)
         THROWV(NULL);
 
-    obj->setArrayLength(count);
     return obj;
 }
 
 JSObject * JS_FASTCALL
 stubs::NewInitObject(VMFrame &f, JSObject *baseobj)
 {
     JSContext *cx = f.cx;
 
@@ -2569,18 +2565,18 @@ finally:
 
 void JS_FASTCALL
 stubs::Unbrand(VMFrame &f)
 {
     const Value &thisv = f.regs.sp[-1];
     if (!thisv.isObject())
         return;
     JSObject *obj = &thisv.toObject();
-    if (obj->isNative() && !obj->unbrand(f.cx))
-        THROW();
+    if (obj->isNative())
+        obj->unbrand(f.cx);
 }
 
 void JS_FASTCALL
 stubs::Pos(VMFrame &f)
 {
     if (!ValueToNumber(f.cx, &f.regs.sp[-1]))
         THROW();
 }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1970,17 +1970,17 @@ JS_STATIC_ASSERT(JSTRY_ITER == 2);
 
 static const char* const TryNoteNames[] = { "catch", "finally", "iter" };
 
 static JSBool
 TryNotes(JSContext *cx, JSScript *script)
 {
     JSTryNote *tn, *tnlimit;
 
-    if (script->trynotesOffset == 0)
+    if (!JSScript::isValidOffset(script->trynotesOffset))
         return JS_TRUE;
 
     tn = script->trynotes()->vector;
     tnlimit = tn + script->trynotes()->length;
     fprintf(gOutFile, "\nException table:\n"
             "kind      stack    start      end\n");
     do {
         JS_ASSERT(tn->kind < JS_ARRAY_LENGTH(TryNoteNames));
@@ -2048,17 +2048,17 @@ DisassembleValue(JSContext *cx, jsval v,
         }
     }
 
     if (!js_Disassemble(cx, script, lines, stdout))
         return false;
     SrcNotes(cx, script);
     TryNotes(cx, script);
 
-    if (recursive && script->objectsOffset != 0) {
+    if (recursive && JSScript::isValidOffset(script->objectsOffset)) {
         JSObjectArray *objects = script->objects();
         for (uintN i = 0; i != objects->length; ++i) {
             JSObject *obj = objects->vector[i];
             if (obj->isFunction()) {
                 putchar('\n');
                 if (!DisassembleValue(cx, OBJECT_TO_JSVAL(obj),
                                       lines, recursive)) {
                     return false;
@@ -2134,21 +2134,16 @@ DisassFile(JSContext *cx, uintN argc, js
 
     uint32 oldopts = JS_GetOptions(cx);
     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
     JSScript *script = JS_CompileFile(cx, thisobj, filename.ptr());
     JS_SetOptions(cx, oldopts);
     if (!script)
         return JS_FALSE;
 
-    if (script->isEmpty()) {
-        JS_SET_RVAL(cx, vp, JSVAL_VOID);
-        return JS_TRUE;
-    }
-
     JSObject *obj = JS_NewScriptObject(cx, script);
     if (!obj)
         return JS_FALSE;
 
     argv[0] = OBJECT_TO_JSVAL(obj); /* I like to root it, root it. */
     JSBool ok = Disassemble(cx, _argc, vp); /* gross, but works! */
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return ok;
@@ -4288,17 +4283,17 @@ static JSFunctionSpec shell_functions[] 
     JS_FN("stopVtune",      js_StopVtune,     0,0),
     JS_FN("pauseVtune",     js_PauseVtune,    0,0),
     JS_FN("resumeVtune",    js_ResumeVtune,   0,0),
 #endif
 #ifdef MOZ_TRACEVIS
     JS_FN("startTraceVis",  StartTraceVisNative, 1,0),
     JS_FN("stopTraceVis",   StopTraceVisNative,  0,0),
 #endif
-#ifdef DEBUG_ARRAYS
+#ifdef DEBUG
     JS_FN("arrayInfo",      js_ArrayInfo,   1,0),
 #endif
 #ifdef JS_THREADSAFE
     JS_FN("sleep",          Sleep_fn,       1,0),
     JS_FN("scatter",        Scatter,        1,0),
 #endif
     JS_FN("snarf",          Snarf,          0,0),
     JS_FN("read",           Snarf,          0,0),
@@ -4415,17 +4410,17 @@ static const char *const shell_help_mess
 "stopVtune()              Stop vtune instrumentation",
 "pauseVtune()             Pause vtune collection",
 "resumeVtune()            Resume vtune collection",
 #endif
 #ifdef MOZ_TRACEVIS
 "startTraceVis(filename)  Start TraceVis recording (stops any current recording)",
 "stopTraceVis()           Stop TraceVis recording",
 #endif
-#ifdef DEBUG_ARRAYS
+#ifdef DEBUG
 "arrayInfo(a1, a2, ...)   Report statistics about arrays",
 #endif
 #ifdef JS_THREADSAFE
 "sleep(dt)                Sleep for dt seconds",
 "scatter(fns)             Call functions concurrently (ignoring errors)",
 #endif
 "snarf(filename)          Read filename into returned string",
 "read(filename)           Synonym for snarf",
--- a/js/src/tests/ecma_5/Array/jstests.list
+++ b/js/src/tests/ecma_5/Array/jstests.list
@@ -1,5 +1,6 @@
 url-prefix ../../jsreftest.html?test=ecma_5/Array/
 script sort-01.js
 script toString-01.js
 script toLocaleString-01.js
+script regress-599159.js
 script unshift-01.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Array/regress-599159.js
@@ -0,0 +1,10 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var b = Object.create(Array.prototype);
+b.length = 12;
+assertEq(b.length, 12);
+
+reportCompare(true,true);
--- a/js/src/tests/ecma_5/Expressions/jstests.list
+++ b/js/src/tests/ecma_5/Expressions/jstests.list
@@ -1,4 +1,5 @@
 url-prefix ../../jsreftest.html?test=ecma_5/Expressions/
 script 11.1.5-01.js
 script named-accessor-function.js
+script nested-delete-name-in-evalcode.js
 script object-literal-accessor-arguments.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Expressions/nested-delete-name-in-evalcode.js
@@ -0,0 +1,163 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 616294;
+var summary =
+  "|delete x| inside a function in eval code, where that eval code includes " +
+  "|var x| at top level, actually does delete the binding for x";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var f;
+
+function testOuterVar()
+{
+  return eval("var x; (function() { return delete x; })");
+}
+
+f = testOuterVar();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testOuterFunction()
+{
+  return eval("function x() { } (function() { return delete x; })");
+}
+
+f = testOuterFunction();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testOuterForVar()
+{
+  return eval("for (var x; false; ); (function() { return delete x; })");
+}
+
+f = testOuterForVar();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testOuterForInVar()
+{
+  return eval("for (var x in {}); (function() { return delete x; })");
+}
+
+f = testOuterForInVar();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testOuterNestedVar()
+{
+  return eval("for (var q = 0; q < 5; q++) { var x; } (function() { return delete x; })");
+}
+
+f = testOuterNestedVar();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testOuterNestedConditionalVar()
+{
+  return eval("for (var q = 0; q < 5; q++) { if (false) { var x; } } (function() { return delete x; })");
+}
+
+f = testOuterNestedConditionalVar();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testVarInWith()
+{
+  return eval("with ({}) { var x; } (function() { return delete x; })");
+}
+
+f = testVarInWith();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testForVarInWith()
+{
+  return eval("with ({}) { for (var x = 0; x < 5; x++); } (function() { return delete x; })");
+}
+
+f = testForVarInWith();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testForInVarInWith()
+{
+  return eval("with ({}) { for (var x in {}); } (function() { return delete x; })");
+}
+
+f = testForInVarInWith();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testUnknown()
+{
+  return eval("nameToDelete = 17; (function() { return delete nameToDelete; })");
+}
+
+f = testUnknown();
+
+assertEq(f(), true); // configurable global property, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testArgumentShadow()
+{
+  return eval("var x; (function(x) { return delete x; })");
+}
+
+f = testArgumentShadow();
+
+assertEq(f(), false); // non-configurable argument => false
+
+
+function testArgument()
+{
+  return eval("(function(x) { return delete x; })");
+}
+
+f = testArgument();
+
+assertEq(f(), false); // non-configurable argument => false
+
+
+function testFunctionLocal()
+{
+  return eval("(function() { var x; return delete x; })");
+}
+
+f = testFunctionLocal();
+
+assertEq(f(), false); // defined by function code => not configurable => false
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("All tests passed!");
--- a/js/src/tests/ecma_5/extensions/jstests.list
+++ b/js/src/tests/ecma_5/extensions/jstests.list
@@ -10,8 +10,9 @@ script string-literal-getter-setter-deco
 script watch-inherited-property.js
 script watch-replaced-setter.js
 script watch-setter-become-setter.js
 script watch-value-prop-becoming-setter.js
 script watchpoint-deletes-JSPropertyOp-setter.js
 script eval-native-callback-is-indirect.js
 script regress-bug607284.js
 script Object-keys-and-object-ids.js
+fails script nested-delete-name-in-evalcode.js # bug 604301, at a minimum
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/extensions/nested-delete-name-in-evalcode.js
@@ -0,0 +1,98 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 616294;
+var summary =
+  "|delete x| inside a function in eval code, where that eval code includes " +
+  "|var x| at top level, actually does delete the binding for x";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var f;
+
+function testOuterLet()
+{
+  return eval("let x; (function() { return delete x; })");
+}
+
+f = testOuterLet();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testOuterForLet()
+{
+  return eval("for (let x; false; ); (function() { return delete x; })");
+}
+
+f = testOuterForLet();
+
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testOuterForInLet()
+{
+  return eval("for (let x in {}); (function() { return delete x; })");
+}
+
+f = testOuterForInLet();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // not there => true (only non-configurable => false)
+
+
+function testOuterNestedVarInLetBlock()
+{
+  return eval("let (x = 7) { var x = 9; } (function() { return delete x; })");
+}
+
+f = testOuterNestedVarInLetBlock();
+
+assertEq(f(), true); // configurable var, so remove => true
+assertEq(f(), true); // let still there, configurable => true
+assertEq(f(), true); // nothing at all => true
+
+
+function testOuterNestedVarInForLet()
+{
+  return eval("for (let q = 0; q < 5; q++) { var x; } (function() { return delete x; })");
+}
+
+f = testOuterNestedVarInForLet();
+
+assertEq(f(), true); // configurable, so remove => true
+assertEq(f(), true); // there => true
+
+
+function testArgumentShadowLet()
+{
+  return eval("let x; (function(x) { return delete x; })");
+}
+
+f = testArgumentShadowLet();
+
+assertEq(f(), false); // non-configurable argument => false
+
+
+function testFunctionLocal()
+{
+  return eval("(function() { let x; return delete x; })");
+}
+
+f = testFunctionLocal();
+
+assertEq(f(), false); // defined by function code => not configurable => false
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("All tests passed!");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/strict/directive-prologue-01.js
@@ -0,0 +1,78 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 601262;
+var summary =
+  "A string literal containing an octal escape before a strict mode " +
+  "directive should be a syntax error";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+try
+{
+  eval(" '\\145'; 'use strict'; ");
+  throw new Error("no error thrown for eval");
+}
+catch (e)
+{
+  assertEq(e instanceof SyntaxError, true,
+           "wrong error for octal-escape before strict directive in eval");
+}
+
+try
+{
+  Function(" '\\145'; 'use strict'; ");
+  throw new Error("no error thrown for Function");
+}
+catch (e)
+{
+  assertEq(e instanceof SyntaxError, true,
+           "wrong error for octal-escape before strict directive in Function");
+}
+
+try
+{
+  eval(" function f(){ '\\145'; 'use strict'; } ");
+  throw new Error("no error thrown for eval of function");
+}
+catch (e)
+{
+  assertEq(e instanceof SyntaxError, true,
+           "wrong error for octal-escape before strict directive in eval of " +
+           "function");
+}
+
+try
+{
+  Function(" function f(){ '\\145'; 'use strict'; } ");
+  throw new Error("no error thrown for eval of function");
+}
+catch (e)
+{
+  assertEq(e instanceof SyntaxError, true,
+           "wrong error for octal-escape before strict directive in eval of " +
+           "function");
+}
+
+eval("function notAnError1() { 5; '\\145'; function g() { 'use strict'; } }");
+
+Function("function notAnError2() { 5; '\\145'; function g() { 'use strict'; } }");
+
+function notAnError3()
+{
+  5;
+  "\145";
+  function g() { "use strict"; }
+}
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("All tests passed!");
--- a/js/src/tests/ecma_5/strict/jstests.list
+++ b/js/src/tests/ecma_5/strict/jstests.list
@@ -20,20 +20,21 @@ script 13.1.js
 script 15.3.4.5.js
 script 15.3.5.1.js
 script 15.3.5.2.js
 script 15.4.4.6.js
 script 15.4.4.8.js
 script 15.4.4.9.js
 script 15.4.4.12.js
 script 15.4.4.13.js
-script 15.4.5.1.js
+skip script 15.4.5.1.js  # Waiting for bug 537873 to be fully resolved.
 script 15.5.5.1.js
 script 15.5.5.2.js
 script 15.10.7.js
 script B.1.1.js
 script B.1.2.js
 script function-name-arity.js
 script primitive-this-no-writeback.js
 script regress-532254.js
 script regress-532041.js
 script unbrand-this.js
 script this-for-function-expression-recursion.js
+script directive-prologue-01.js
--- a/js/src/tests/js1_8_5/extensions/typedarray.js
+++ b/js/src/tests/js1_8_5/extensions/typedarray.js
@@ -231,24 +231,26 @@ function test()
     check(function() a[4] == 1);
 
     // test set()
     var empty = new Int32Array(0);
     a = new Int32Array(9);
 
     empty.set([]);
     empty.set([], 0);
+    empty.set(empty);
 
     checkThrows(function() empty.set([1]));
     checkThrows(function() empty.set([1], 0));
     checkThrows(function() empty.set([1], 1));
 
     a.set([]);
     a.set([], 3);
     a.set([], 9);
+    a.set(a);
 
     a.set(empty);
     a.set(empty, 3);
     a.set(empty, 9);
     a.set(Array.prototype);
     checkThrows(function() a.set(empty, 100));
 
     checkThrows(function() a.set([1,2,3,4,5,6,7,8,9,10]));
--- a/js/src/tests/js1_8_5/regress/jstests.list
+++ b/js/src/tests/js1_8_5/regress/jstests.list
@@ -51,8 +51,10 @@ script regress-600137.js
 script regress-601399.js
 script regress-602621.js
 fails-if(!xulRuntime.shell) script regress-607799.js
 fails-if(!xulRuntime.shell) script regress-607863.js
 script regress-610026.js
 script regress-609617.js
 script regress-617405-1.js
 script regress-617405-2.js
+skip-if(!xulRuntime.shell) script regress-618576.js  # uses evalcx
+fails-if(!xulRuntime.shell) script regress-618652.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-618576.js
@@ -0,0 +1,12 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ * Contributors: Gary Kwong and Jason Orendorff
+ */
+
+var x = <x/>;
+x.function::__proto__ = evalcx('');
+for (a in x)  // don't assert
+    ;
+
+reportCompare(0, 0, 'ok');
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-618652.js
@@ -0,0 +1,10 @@
+options('atline')
+
+var F, L;
+
+eval('//@line 42 "foo"\n' +
+     'try { nosuchname; } catch (e) { F = e.fileName; L = e.lineNumber; }');
+assertEq(F, "foo");
+assertEq(L, 42);
+
+reportCompare(0, 0, "ok");
--- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp
@@ -216,84 +216,26 @@ Debug(JSContext *cx, uintN argc, jsval *
 }
 
 static JSBool
 Atob(JSContext *cx, uintN argc, jsval *vp)
 {
     if (!argc)
         return JS_TRUE;
 
-    JSString *str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
-    if (!str)
-        return JS_FALSE;
-
-    size_t len = JS_GetStringEncodingLength(cx, str);
-    if (len == size_t(-1))
-        return JS_FALSE;
-
-    JSUint32 alloc_len = (len + 1) * sizeof(char);
-    char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len));
-    if (!buffer)
-        return JS_FALSE;
-
-    JS_EncodeStringToBuffer(str, buffer, len);
-    buffer[len] = '\0';
-
-    nsDependentCString string(buffer, JS_GetStringLength(str));
-    nsCAutoString result;
-
-    if (NS_FAILED(nsXPConnect::Base64Decode(string, result))) {
-        JS_ReportError(cx, "Failed to decode base64 string!");
-        return JS_FALSE;
-    }
-
-    str = JS_NewStringCopyN(cx, result.get(), result.Length());
-    if (!str)
-        return JS_FALSE;
-
-    JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
-    return JS_TRUE;
+    return nsXPConnect::Base64Decode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
 }
 
 static JSBool
 Btoa(JSContext *cx, uintN argc, jsval *vp)
 {
     if (!argc)
         return JS_TRUE;
 
-    JSString *str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
-    if (!str)
-        return JS_FALSE;
-
-    size_t len = JS_GetStringEncodingLength(cx, str);
-    if (len == size_t(-1))
-        return JS_FALSE;
-
-    JSUint32 alloc_len = (len + 1) * sizeof(char);
-    char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len));
-    if (!buffer)
-        return JS_FALSE;
-
-    JS_EncodeStringToBuffer(str, buffer, len);
-    buffer[len] = '\0';
-
-    nsDependentCString data(buffer, len);
-    nsCAutoString result;
-
-    if (NS_FAILED(nsXPConnect::Base64Encode(data, result))) {
-        JS_ReportError(cx, "Failed to encode base64 data!");
-        return JS_FALSE;
-    }
-
-    str = JS_NewStringCopyN(cx, result.get(), result.Length());
-    if (!str)
-        return JS_FALSE;
-
-    JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
-    return JS_TRUE;
+    return nsXPConnect::Base64Encode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
 }
 
 static JSFunctionSpec gGlobalFun[] = {
     {"dump",    Dump,   1,0},
     {"debug",   Debug,  1,0},
     {"atob",    Atob,   1,0},
     {"btoa",    Btoa,   1,0},
 #ifdef MOZ_SHARK
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -59,16 +59,18 @@
 #include "jstypedarray.h"
 
 #include "XrayWrapper.h"
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
 
 #include "jsdIDebuggerService.h"
 
+#include "xpcquickstubs.h"
+
 NS_IMPL_THREADSAFE_ISUPPORTS6(nsXPConnect,
                               nsIXPConnect,
                               nsISupportsWeakReference,
                               nsIThreadObserver,
                               nsIJSRuntimeService,
                               nsIJSContextStack,
                               nsIThreadJSContextStack)
 
@@ -2610,18 +2612,17 @@ nsXPConnect::GetCaller(JSContext **aJSCo
     *aJSContext = ccx->GetJSContext();
 
     // Set to the caller in XPC_WN_Helper_{Call,Construct}
     *aObj = ccx->GetFlattenedJSObject();
 }
 
 // static
 nsresult
-nsXPConnect::Base64Encode(const nsACString &aBinaryData,
-                          nsACString &aString)
+nsXPConnect::Base64Encode(const nsACString &aBinaryData, nsACString &aString)
 {
   // Check for overflow.
   if(aBinaryData.Length() > (PR_UINT32_MAX / 4) * 3)
       return NS_ERROR_FAILURE;
 
   PRUint32 stringLen = ((aBinaryData.Length() + 2) / 3) * 4;
 
   char *buffer;
@@ -2640,35 +2641,61 @@ nsXPConnect::Base64Encode(const nsACStri
   }
 
   aString.Truncate();
   return NS_ERROR_INVALID_ARG;
 }
 
 // static
 nsresult
-nsXPConnect::Base64Encode(const nsAString &aString,
-                          nsAString &aBinaryData)
+nsXPConnect::Base64Encode(const nsAString &aString, nsAString &aBinaryData)
 {
     NS_LossyConvertUTF16toASCII string(aString);
     nsCAutoString binaryData;
 
     nsresult rv = Base64Encode(string, binaryData);
     if(NS_SUCCEEDED(rv))
         CopyASCIItoUTF16(binaryData, aBinaryData);
     else
         aBinaryData.Truncate();
 
     return rv;
 }
 
 // static
+JSBool
+nsXPConnect::Base64Encode(JSContext *cx, jsval val, jsval *out)
+{
+    NS_ASSERTION(cx, "Null context!");
+    NS_ASSERTION(out, "Null jsval pointer!");
+
+    jsval root = val;
+    xpc_qsACString encodedString(cx, root, &root, xpc_qsACString::eNull,
+                                 xpc_qsACString::eStringify);
+    if(!encodedString.IsValid())
+        return JS_FALSE;
+
+    nsCAutoString result;
+    if(NS_FAILED(nsXPConnect::Base64Encode(encodedString, result)))
+    {
+        JS_ReportError(cx, "Failed to encode base64 data!");
+        return JS_FALSE;
+    }
+
+    JSString *str = JS_NewStringCopyN(cx, result.get(), result.Length());
+    if (!str)
+        return JS_FALSE;
+
+    *out = STRING_TO_JSVAL(str);
+    return JS_TRUE;
+}
+
+// static
 nsresult
-nsXPConnect::Base64Decode(const nsACString &aString,
-                          nsACString &aBinaryData)
+nsXPConnect::Base64Decode(const nsACString &aString, nsACString &aBinaryData)
 {
   // Check for overflow.
   if(aString.Length() > PR_UINT32_MAX / 3)
       return NS_ERROR_FAILURE;
 
   PRUint32 binaryDataLen = ((aString.Length() * 3) / 4);
 
   char *buffer;
@@ -2695,31 +2722,58 @@ nsXPConnect::Base64Decode(const nsACStri
   }
 
   aBinaryData.Truncate();
   return NS_ERROR_INVALID_ARG;
 }
 
 // static
 nsresult
-nsXPConnect::Base64Decode(const nsAString &aBinaryData,
-                          nsAString &aString)
+nsXPConnect::Base64Decode(const nsAString &aBinaryData, nsAString &aString)
 {
     NS_LossyConvertUTF16toASCII binaryData(aBinaryData);
     nsCAutoString string;
 
     nsresult rv = Base64Decode(binaryData, string);
     if(NS_SUCCEEDED(rv))
         CopyASCIItoUTF16(string, aString);
     else
         aString.Truncate();
 
     return rv;
 }
 
+// static
+JSBool
+nsXPConnect::Base64Decode(JSContext *cx, jsval val, jsval *out)
+{
+    NS_ASSERTION(cx, "Null context!");
+    NS_ASSERTION(out, "Null jsval pointer!");
+
+    jsval root = val;
+    xpc_qsACString encodedString(cx, root, &root, xpc_qsACString::eNull,
+                                 xpc_qsACString::eNull);
+    if(!encodedString.IsValid())
+        return JS_FALSE;
+
+    nsCAutoString result;
+    if(NS_FAILED(nsXPConnect::Base64Decode(encodedString, result)))
+    {
+        JS_ReportError(cx, "Failed to decode base64 string!");
+        return JS_FALSE;
+    }
+
+    JSString *str = JS_NewStringCopyN(cx, result.get(), result.Length());
+    if(!str)
+        return JS_FALSE;
+
+    *out = STRING_TO_JSVAL(str);
+    return JS_TRUE;
+}
+
 NS_IMETHODIMP
 nsXPConnect::SetDebugModeWhenPossible(PRBool mode)
 {
     gDesiredDebugMode = mode;
     return NS_OK;
 }
 
 /* These are here to be callable from a debugger */
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -565,22 +565,28 @@ public:
     nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);
 
     static nsresult Base64Encode(const nsACString &aString,
                                  nsACString &aBinary);
 
     static nsresult Base64Encode(const nsAString &aString,
                                  nsAString &aBinaryData);
 
+    // If this returns JS_FALSE then an exception will be set on cx.
+    static JSBool Base64Encode(JSContext *cx, jsval val, jsval *out);
+
     static nsresult Base64Decode(const nsACString &aBinaryData,
                                  nsACString &aString);
 
     static nsresult Base64Decode(const nsAString &aBinaryData,
                                  nsAString &aString);
 
+    // If this returns JS_FALSE then an exception will be set on cx.
+    static JSBool Base64Decode(JSContext *cx, jsval val, jsval *out);
+
     // nsCycleCollectionParticipant
     NS_IMETHOD RootAndUnlinkJSObjects(void *p);
     NS_IMETHOD Unlink(void *p);
     NS_IMETHOD Unroot(void *p);
     NS_IMETHOD Traverse(void *p,
                         nsCycleCollectionTraversalCallback &cb);
     
     // nsCycleCollectionLanguageRuntime
--- a/js/src/xpconnect/src/xpcquickstubs.cpp
+++ b/js/src/xpconnect/src/xpcquickstubs.cpp
@@ -704,32 +704,42 @@ xpc_qsDOMString::xpc_qsDOMString(JSConte
     size_t len = s->length();
     const PRUnichar* chars =
         (len == 0 ? traits::sEmptyBuffer :
                     reinterpret_cast<const PRUnichar*>(JS_GetStringChars(s)));
     new(mBuf) implementation_type(chars, len);
     mValid = JS_TRUE;
 }
 
-xpc_qsACString::xpc_qsACString(JSContext *cx, jsval v, jsval *pval)
+xpc_qsACString::xpc_qsACString(JSContext *cx, jsval v, jsval *pval,
+                               StringificationBehavior nullBehavior,
+                               StringificationBehavior undefinedBehavior)
 {
     typedef implementation_type::char_traits traits;
     // From the T_CSTRING case in XPCConvert::JSData2Native.
-    JSString *s = InitOrStringify<traits>(cx, v, pval, eNull, eNull);
+    JSString *s = InitOrStringify<traits>(cx, v, pval, nullBehavior,
+                                          undefinedBehavior);
     if (!s)
         return;
 
+    size_t len = JS_GetStringEncodingLength(cx, s);
+    if(len == size_t(-1))
+    {
+        mValid = JS_FALSE;
+        return;
+    }
+
     JSAutoByteString bytes(cx, s);
     if(!bytes)
     {
         mValid = JS_FALSE;
         return;
     }
 
-    new(mBuf) implementation_type(bytes.ptr(), strlen(bytes.ptr()));
+    new(mBuf) implementation_type(bytes.ptr(), len);
     mValid = JS_TRUE;
 }
 
 xpc_qsAUTF8String::xpc_qsAUTF8String(JSContext *cx, jsval v, jsval *pval)
 {
     typedef nsCharTraits<PRUnichar> traits;
     // From the T_UTF8STRING  case in XPCConvert::JSData2Native.
     JSString *s = InitOrStringify<traits>(cx, v, pval, eNull, eNull);
--- a/js/src/xpconnect/src/xpcquickstubs.h
+++ b/js/src/xpconnect/src/xpcquickstubs.h
@@ -434,17 +434,19 @@ public:
 
 /**
  * Like xpc_qsDOMString and xpc_qsAString, but for XPIDL native types annotated
  * with [cstring] rather than [domstring] or [astring].
  */
 class xpc_qsACString : public xpc_qsBasicString<nsACString, nsCString>
 {
 public:
-    xpc_qsACString(JSContext *cx, jsval v, jsval *pval);
+    xpc_qsACString(JSContext *cx, jsval v, jsval *pval,
+                   StringificationBehavior nullBehavior = eNull,
+                   StringificationBehavior undefinedBehavior = eNull);
 };
 
 /**
  * And similar for AUTF8String.
  */
 class xpc_qsAUTF8String :
   public xpc_qsBasicString<nsACString, NS_ConvertUTF16toUTF8>
 {