Merge TM -> JM
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 20 Dec 2010 09:06:43 -0800
changeset 74672 221ad532f54f28577d57126bcdf767a3de80c04b
parent 74671 15b2220f3ea981c3ecabac354e1de6babc3a7245 (current diff)
parent 59899 35697ebafa6cfed3459f58a7e9da2ae29be0482b (diff)
child 74673 626c8496435ce5776b0c527063af536edac8e7cc
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone2.0b8pre
Merge TM -> JM
dom/base/nsDOMClassInfo.cpp
js/ipc/ObjectWrapperParent.cpp
js/src/configure.in
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsapi-tests/Makefile.in
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsatom.cpp
js/src/jsbuiltins.cpp
js/src/jsbuiltins.h
js/src/jsclone.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdate.cpp
js/src/jsdbgapi.cpp
js/src/jsemit.cpp
js/src/jsemit.h
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsgcinlines.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsparse.cpp
js/src/jsparse.h
js/src/jsproxy.cpp
js/src/jsreflect.cpp
js/src/jsregexp.cpp
js/src/jsregexpinlines.h
js/src/jsscope.cpp
js/src/jsscopeinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jstypedarray.cpp
js/src/jsval.h
js/src/jsxml.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastOps.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/MonoIC.cpp
js/src/methodjit/MonoIC.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/methodjit/StubCalls.h
js/src/shell/js.cpp
js/src/tests/js1_5/extensions/regress-313500.js
js/src/tests/js1_5/extensions/regress-325269.js
js/src/trace-test/tests/basic/testArrayIn.js
js/src/trace-test/tests/basic/testArrayInWithIndexedProto.js
js/src/tracejit/Writer.cpp
js/src/tracejit/Writer.h
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -109,26 +109,26 @@ PRBool nsScriptSecurityManager::sStrictF
 ///////////////////////////
 // Convenience Functions //
 ///////////////////////////
 // Result of this function should not be freed.
 static inline const PRUnichar *
 IDToString(JSContext *cx, jsid id)
 {
     if (JSID_IS_STRING(id))
-        return reinterpret_cast<PRUnichar*>(JS_GetStringChars(JSID_TO_STRING(id)));
+        return JS_GetInternedStringChars(JSID_TO_STRING(id));
 
     JSAutoRequest ar(cx);
     jsval idval;
     if (!JS_IdToValue(cx, id, &idval))
         return nsnull;
     JSString *str = JS_ValueToString(cx, idval);
     if(!str)
         return nsnull;
-    return reinterpret_cast<PRUnichar*>(JS_GetStringChars(str));
+    return JS_GetStringCharsZ(cx, str);
 }
 
 class nsAutoInPrincipalDomainOriginSetter {
 public:
     nsAutoInPrincipalDomainOriginSetter() {
         ++sInPrincipalDomainOrigin;
     }
     ~nsAutoInPrincipalDomainOriginSetter() {
--- a/caps/src/nsSecurityManagerFactory.cpp
+++ b/caps/src/nsSecurityManagerFactory.cpp
@@ -90,33 +90,35 @@ getBytesArgument(JSContext *cx, JSObject
     JSString *str = getStringArgument(cx, obj, argNum, argc, argv);
     return str && bytes->encode(cx, str);
 }
 
 static void
 getUTF8StringArgument(JSContext *cx, JSObject *obj, PRUint16 argNum,
                       uintN argc, jsval *argv, nsCString& aRetval)
 {
+    aRetval.Truncate();
+
     if (argc <= argNum || !JSVAL_IS_STRING(argv[argNum])) {
         JS_ReportError(cx, "String argument expected");
-        aRetval.Truncate();
         return;
     }
 
     /*
      * We don't want to use JS_ValueToString because we want to be able
      * to have an object to represent a target in subsequent versions.
      */
     JSString *str = JSVAL_TO_STRING(argv[argNum]);
-    if (!str) {
-        aRetval.Truncate();
+    if (!str)
         return;
-    }
 
-    PRUnichar *data = (PRUnichar*)JS_GetStringChars(str);
+    const PRUnichar *data = JS_GetStringCharsZ(cx, str);
+    if (!data)
+        return;
+
     CopyUTF16toUTF8(data, aRetval);
 }
 
 static JSBool
 netscape_security_isPrivilegeEnabled(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj = JS_THIS_OBJECT(cx, vp);
     if (!obj)
--- a/configure.in
+++ b/configure.in
@@ -1910,23 +1910,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/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -203,17 +203,21 @@ nsFrameMessageManager::GetParamsForMessa
   PRUint32 argc;
   jsval* argv = nsnull;
   ncc->GetArgc(&argc);
   ncc->GetArgvPtr(&argv);
 
   JSAutoRequest ar(ctx);
   JSString* str;
   if (argc && (str = JS_ValueToString(ctx, argv[0])) && str) {
-    aMessageName.Assign(nsDependentJSString(str));
+    nsDependentJSString depStr;
+    if (!depStr.init(ctx, str)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    aMessageName.Assign(depStr);
   }
 
   if (argc >= 2) {
     jsval v = argv[1];
     if (JS_TryJSON(ctx, &v)) {
       JS_Stringify(ctx, &v, nsnull, JSVAL_NULL, JSONCreator, &aJSON);
     }
   }
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -2935,27 +2935,37 @@ nsWebSocket::Initialize(nsISupports* aOw
   }
 
   JSAutoRequest ar(aContext);
 
   JSString* jsstr = JS_ValueToString(aContext, aArgv[0]);
   if (!jsstr) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
-  urlParam.Assign(reinterpret_cast<const PRUnichar*>(JS_GetStringChars(jsstr)),
-                  JS_GetStringLength(jsstr));
+
+  size_t length;
+  const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
+  if (!chars) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  urlParam.Assign(chars, length);
 
   if (aArgc == 2) {
     jsstr = JS_ValueToString(aContext, aArgv[1]);
     if (!jsstr) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
-    protocolParam.
-      Assign(reinterpret_cast<const PRUnichar*>(JS_GetStringChars(jsstr)),
-             JS_GetStringLength(jsstr));
+
+    chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
+    if (!chars) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    protocolParam.Assign(chars, length);
     if (protocolParam.IsEmpty()) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
   }
 
   nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aOwner);
   NS_ENSURE_STATE(ownerWindow);
 
--- a/content/canvas/src/CustomQS_WebGL.h
+++ b/content/canvas/src/CustomQS_WebGL.h
@@ -890,33 +890,37 @@ nsIDOMWebGLRenderingContext_VertexAttrib
 static inline void FASTCALL
 helper_nsIDOMWebGLRenderingContext_Uniform_x_iv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj,
                                                       JSObject *arg, int nElements)
 {
     XPC_QS_ASSERT_CONTEXT_OK(cx);
 
     nsIDOMWebGLRenderingContext *self;
     xpc_qsSelfRef selfref;
-    xpc_qsArgValArray<3> vp(cx);
-    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, &vp.array[0], nsnull)) {
+    js::Anchor<jsval> self_anchor;
+    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr,
+                          &self_anchor.get(), nsnull)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     if (!arg) {
         xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformNiv");
         js_SetTraceableNativeFailed(cx);
     }
 
     js::AutoValueRooter obj_tvr(cx);
 
     nsIWebGLUniformLocation *location;
     xpc_qsSelfRef location_selfref;
+    js::Anchor<jsval> location_anchor;
     nsresult rv_convert_arg0
-        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location, &location_selfref.ptr, &vp.array[1], nsnull);
+        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location,
+                           &location_selfref.ptr, &location_anchor.get(),
+                           nsnull);
     if (NS_FAILED(rv_convert_arg0)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     js::TypedArray *wa = 0;
 
     if (helper_isInt32Array(arg)) {
@@ -958,33 +962,37 @@ helper_nsIDOMWebGLRenderingContext_Unifo
 static inline void FASTCALL
 helper_nsIDOMWebGLRenderingContext_Uniform_x_fv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj,
                                                       JSObject *arg, int nElements)
 {
     XPC_QS_ASSERT_CONTEXT_OK(cx);
 
     nsIDOMWebGLRenderingContext *self;
     xpc_qsSelfRef selfref;
-    xpc_qsArgValArray<3> vp(cx);
-    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, &vp.array[0], nsnull)) {
+    js::Anchor<jsval> self_anchor;
+    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr,
+                          &self_anchor.get(), nsnull)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     if (!arg) {
         xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformNfv");
         js_SetTraceableNativeFailed(cx);
     }
 
     js::AutoValueRooter obj_tvr(cx);
 
     nsIWebGLUniformLocation *location;
     xpc_qsSelfRef location_selfref;
+    js::Anchor<jsval> location_anchor;
     nsresult rv_convert_arg0
-        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location, &location_selfref.ptr, &vp.array[1], nsnull);
+        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location,
+                           &location_selfref.ptr, &location_anchor.get(),
+                           nsnull);
     if (NS_FAILED(rv_convert_arg0)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     js::TypedArray *wa = 0;
 
     if (helper_isFloat32Array(arg)) {
@@ -1028,33 +1036,37 @@ helper_nsIDOMWebGLRenderingContext_Unifo
 static inline void FASTCALL
 helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj,
                                                             JSBool transpose, JSObject *arg, int nElements)
 {
     XPC_QS_ASSERT_CONTEXT_OK(cx);
 
     nsIDOMWebGLRenderingContext *self;
     xpc_qsSelfRef selfref;
-    xpc_qsArgValArray<4> vp(cx);
-    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, &vp.array[0], nsnull)) {
+    js::Anchor<jsval> self_anchor;
+    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr,
+                          &self_anchor.get(), nsnull)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     if (!arg) {
         xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformMatrixNfv");
         js_SetTraceableNativeFailed(cx);
     }
 
     js::AutoValueRooter obj_tvr(cx);
 
     nsIWebGLUniformLocation *location;
     xpc_qsSelfRef location_selfref;
+    js::Anchor<jsval> location_anchor;
     nsresult rv_convert_arg0
-        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location, &location_selfref.ptr, &vp.array[1], nsnull);
+        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location,
+                           &location_selfref.ptr, &location_anchor.get(),
+                           nsnull);
     if (NS_FAILED(rv_convert_arg0)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     js::TypedArray *wa = 0;
 
     if (helper_isFloat32Array(arg)) {
--- a/content/events/src/nsEventListenerService.cpp
+++ b/content/events/src/nsEventListenerService.cpp
@@ -135,17 +135,20 @@ nsEventListenerInfo::ToSource(nsAString&
     if (cx && NS_SUCCEEDED(stack->Push(cx))) {
       {
         // Extra block to finish the auto request before calling pop
         JSAutoRequest ar(cx);
         jsval v = JSVAL_NULL;
         if (GetJSVal(&v)) {
           JSString* str = JS_ValueToSource(cx, v);
           if (str) {
-            aResult.Assign(nsDependentJSString(str));
+            nsDependentJSString depStr;
+            if (depStr.init(cx, str)) {
+              aResult.Assign(depStr);
+            }
           }
         }
       }
       stack->Pop(&cx);
     }
   }
   
   return NS_OK;
--- a/content/html/content/src/nsHTMLAudioElement.cpp
+++ b/content/html/content/src/nsHTMLAudioElement.cpp
@@ -135,17 +135,20 @@ nsHTMLAudioElement::Initialize(nsISuppor
     return NS_OK;
   }
 
   // The only (optional) argument is the url of the audio
   JSString* jsstr = JS_ValueToString(aContext, argv[0]);
   if (!jsstr)
     return NS_ERROR_FAILURE;
 
-  nsDependentJSString str(jsstr);
+  nsDependentJSString str;
+  if (!str.init(aContext, jsstr))
+    return NS_ERROR_FAILURE;
+
   rv = SetAttr(kNameSpaceID_None, nsGkAtoms::src, str, PR_TRUE);
   if (NS_FAILED(rv))
     return rv;
 
   // We have been specified with a src URL. Begin a load.
   QueueSelectResourceTask();
 
   return NS_OK;
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -41,16 +41,17 @@
 #include "nsNetUtil.h"
 #include "prmem.h"
 #include "nsDOMFile.h"
 #include "CheckedInt.h"
 
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
+#include "nsJSUtils.h"
 
 #include "nsFrameManager.h"
 #include "nsDisplayList.h"
 #include "ImageLayers.h"
 #include "BasicLayers.h"
 #include "imgIEncoder.h"
 
 #include "nsIWritablePropertyBag2.h"
@@ -497,28 +498,37 @@ nsHTMLCanvasElement::GetContext(const ns
           jsval propname, propval;
           if (!JS_IdToValue(cx, propid, &propname) ||
               !JS_GetPropertyById(cx, opts, propid, &propval))
           {
             continue;
           }
 
           JSString *propnameString = JS_ValueToString(cx, propname);
-
-          nsDependentString pstr(JS_GetStringChars(propnameString), JS_GetStringLength(propnameString));
+          nsDependentJSString pstr;
+          if (!propnameString || !pstr.init(cx, propnameString)) {
+            mCurrentContext = nsnull;
+            return NS_ERROR_FAILURE;
+          }
 
           if (JSVAL_IS_BOOLEAN(propval)) {
             newProps->SetPropertyAsBool(pstr, propval == JSVAL_TRUE ? PR_TRUE : PR_FALSE);
           } else if (JSVAL_IS_INT(propval)) {
             newProps->SetPropertyAsInt32(pstr, JSVAL_TO_INT(propval));
           } else if (JSVAL_IS_DOUBLE(propval)) {
             newProps->SetPropertyAsDouble(pstr, JSVAL_TO_DOUBLE(propval));
           } else if (JSVAL_IS_STRING(propval)) {
-            newProps->SetPropertyAsAString(pstr, nsDependentString(JS_GetStringChars(JS_ValueToString(cx, propval)),
-                                                                   JS_GetStringLength(JS_ValueToString(cx, propval))));
+            JSString *propvalString = JS_ValueToString(cx, propval);
+            nsDependentJSString vstr;
+            if (!propvalString || !vstr.init(cx, propvalString)) {
+              mCurrentContext = nsnull;
+              return NS_ERROR_FAILURE;
+            }
+
+            newProps->SetPropertyAsAString(pstr, vstr);
           }
         }
       }
 
       contextProps = newProps;
     }
 
     rv = UpdateContext(contextProps);
--- a/content/html/content/src/nsHTMLOptionElement.cpp
+++ b/content/html/content/src/nsHTMLOptionElement.cpp
@@ -407,36 +407,44 @@ nsHTMLOptionElement::Initialize(nsISuppo
     // Create a new text node and append it to the option
     nsCOMPtr<nsIContent> textContent;
     result = NS_NewTextNode(getter_AddRefs(textContent),
                             mNodeInfo->NodeInfoManager());
     if (NS_FAILED(result)) {
       return result;
     }
 
-    textContent->SetText(reinterpret_cast<const PRUnichar*>
-                                         (JS_GetStringChars(jsstr)),
-                         JS_GetStringLength(jsstr),
-                         PR_FALSE);
+    size_t length;
+    const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
+    if (!chars) {
+      return NS_ERROR_FAILURE;
+    }
+
+    textContent->SetText(chars, length, PR_FALSE);
     
     result = AppendChildTo(textContent, PR_FALSE);
     if (NS_FAILED(result)) {
       return result;
     }
 
     if (argc > 1) {
       // The second (optional) parameter is the value of the option
       jsstr = JS_ValueToString(aContext, argv[1]);
       if (!jsstr) {
         return NS_ERROR_FAILURE;
       }
 
+      size_t length;
+      const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
+      if (!chars) {
+        return NS_ERROR_FAILURE;
+      }
+
       // Set the value attribute for this element
-      nsAutoString value(reinterpret_cast<const PRUnichar*>
-                                         (JS_GetStringChars(jsstr)));
+      nsAutoString value(chars, length);
 
       result = SetAttr(kNameSpaceID_None, nsGkAtoms::value, value,
                        PR_FALSE);
       if (NS_FAILED(result)) {
         return result;
       }
 
       if (argc > 2) {
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -1089,88 +1089,95 @@ nsXBLBinding::ChangeDocument(nsIDocument
 
           nsCOMPtr<nsIScriptContext> context = global->GetContext();
           if (context && scope) {
             JSContext *cx = (JSContext *)context->GetNativeContext();
  
             nsCxPusher pusher;
             pusher.Push(cx);
 
-            nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
-            jsval v;
+            nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
+            nsIXPConnect *xpc = nsContentUtils::XPConnect();
             nsresult rv =
-              nsContentUtils::WrapNative(cx, scope, mBoundElement, &v,
-                                         getter_AddRefs(wrapper));
+              xpc->GetWrappedNativeOfNativeObject(cx, scope, mBoundElement,
+                                                  NS_GET_IID(nsISupports),
+                                                  getter_AddRefs(wrapper));
             if (NS_FAILED(rv))
               return;
 
-            JSObject* scriptObject = JSVAL_TO_OBJECT(v);
+            JSObject* scriptObject;
+            if (wrapper)
+                wrapper->GetJSObject(&scriptObject);
+            else
+                scriptObject = nsnull;
+
+            if (scriptObject) {
+              // XXX Stay in sync! What if a layered binding has an
+              // <interface>?!
+              // XXXbz what does that comment mean, really?  It seems to date
+              // back to when there was such a thing as an <interface>, whever
+              // that was...
 
-            // XXX Stay in sync! What if a layered binding has an
-            // <interface>?!
-            // XXXbz what does that comment mean, really?  It seems to date
-            // back to when there was such a thing as an <interface>, whever
-            // that was...
+              // Find the right prototype.
+              JSObject* base = scriptObject;
+              JSObject* proto;
+              JSAutoRequest ar(cx);
+              JSAutoEnterCompartment ac;
+              if (!ac.enter(cx, scriptObject)) {
+                return;
+              }
+
+              for ( ; true; base = proto) { // Will break out on null proto
+                proto = ::JS_GetPrototype(cx, base);
+                if (!proto) {
+                  break;
+                }
 
-            // Find the right prototype.
-            JSObject* base = scriptObject;
-            JSObject* proto;
-            JSAutoRequest ar(cx);
-            JSAutoEnterCompartment ac;
-            if (!ac.enter(cx, scriptObject)) {
-              return;
-            }
+                JSClass* clazz = ::JS_GET_CLASS(cx, proto);
+                if (!clazz ||
+                    (~clazz->flags &
+                     (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS)) ||
+                    JSCLASS_RESERVED_SLOTS(clazz) != 1 ||
+                    clazz->resolve != (JSResolveOp)XBLResolve ||
+                    clazz->finalize != XBLFinalize) {
+                  // Clearly not the right class
+                  continue;
+                }
 
-            for ( ; true; base = proto) { // Will break out on null proto
-              proto = ::JS_GetPrototype(cx, base);
-              if (!proto) {
+                nsRefPtr<nsXBLDocumentInfo> docInfo =
+                  static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(cx, proto));
+                if (!docInfo) {
+                  // Not the proto we seek
+                  continue;
+                }
+
+                jsval protoBinding;
+                if (!::JS_GetReservedSlot(cx, proto, 0, &protoBinding)) {
+                  NS_ERROR("Really shouldn't happen");
+                  continue;
+                }
+
+                if (JSVAL_TO_PRIVATE(protoBinding) != mPrototypeBinding) {
+                  // Not the right binding
+                  continue;
+                }
+
+                // Alright!  This is the right prototype.  Pull it out of the
+                // proto chain.
+                JSObject* grandProto = ::JS_GetPrototype(cx, proto);
+                ::JS_SetPrototype(cx, base, grandProto);
                 break;
               }
 
-              JSClass* clazz = ::JS_GET_CLASS(cx, proto);
-              if (!clazz ||
-                  (~clazz->flags &
-                   (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS)) ||
-                  JSCLASS_RESERVED_SLOTS(clazz) != 1 ||
-                  clazz->resolve != (JSResolveOp)XBLResolve ||
-                  clazz->finalize != XBLFinalize) {
-                // Clearly not the right class
-                continue;
-              }
+              mPrototypeBinding->UndefineFields(cx, scriptObject);
 
-              nsRefPtr<nsXBLDocumentInfo> docInfo =
-                static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(cx, proto));
-              if (!docInfo) {
-                // Not the proto we seek
-                continue;
-              }
-              
-              jsval protoBinding;
-              if (!::JS_GetReservedSlot(cx, proto, 0, &protoBinding)) {
-                NS_ERROR("Really shouldn't happen");
-                continue;
-              }
-
-              if (JSVAL_TO_PRIVATE(protoBinding) != mPrototypeBinding) {
-                // Not the right binding
-                continue;
-              }
-
-              // Alright!  This is the right prototype.  Pull it out of the
-              // proto chain.
-              JSObject* grandProto = ::JS_GetPrototype(cx, proto);
-              ::JS_SetPrototype(cx, base, grandProto);
-              break;
+              // Don't remove the reference from the document to the
+              // wrapper here since it'll be removed by the element
+              // itself when that's taken out of the document.
             }
-
-            mPrototypeBinding->UndefineFields(cx, scriptObject);
-
-            // Don't remove the reference from the document to the
-            // wrapper here since it'll be removed by the element
-            // itself when that's taken out of the document.
           }
         }
       }
 
       // Remove our event handlers
       UnhookEventHandlers();
     }
 
--- a/content/xslt/src/xslt/Makefile.in
+++ b/content/xslt/src/xslt/Makefile.in
@@ -84,16 +84,21 @@ CPPSRCS += txHTMLOutput.cpp	\
            txXMLOutput.cpp
 else
 CPPSRCS += txMozillaStylesheetCompiler.cpp \
            txMozillaTextOutput.cpp \
            txMozillaXMLOutput.cpp \
            txMozillaXSLTProcessor.cpp
 endif
 
+# For nsDependentJSString
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/dom/base \
+  $(NULL)
+
 # we don't want the shared lib, but we want to force the creation of a
 # static lib.
 FORCE_STATIC_LIB = 1
 
 EXTRA_COMPONENTS = \
   txEXSLTRegExFunctions.js \
   txEXSLTRegExFunctions.manifest \
   $(NULL)
--- a/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp
+++ b/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp
@@ -62,16 +62,17 @@
 #include "txUnknownHandler.h"
 #include "txXSLTProcessor.h"
 #include "nsIPrincipal.h"
 #include "nsThreadUtils.h"
 #include "jsapi.h"
 #include "txExprParser.h"
 #include "nsIErrorService.h"
 #include "nsIScriptSecurityManager.h"
+#include "nsJSUtils.h"
 
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kXMLDocumentCID, NS_XMLDOCUMENT_CID);
 
 /**
  * Output Handler Factories
  */
@@ -1460,20 +1461,18 @@ txVariable::Convert(nsIVariant *aValue, 
 
                 JSObject *jsobj;
                 rv = holder->GetJSObject(&jsobj);
                 NS_ENSURE_SUCCESS(rv, rv);
 
                 JSString *str = JS_ValueToString(cx, OBJECT_TO_JSVAL(jsobj));
                 NS_ENSURE_TRUE(str, NS_ERROR_FAILURE);
 
-                const PRUnichar *strChars =
-                    reinterpret_cast<const PRUnichar*>
-                                    (::JS_GetStringChars(str));
-                nsDependentString value(strChars, ::JS_GetStringLength(str));
+                nsDependentJSString value;
+                NS_ENSURE_TRUE(value.init(cx, str), NS_ERROR_FAILURE);
 
                 *aResult = new StringResult(value, nsnull);
                 if (!*aResult) {
                     return NS_ERROR_OUT_OF_MEMORY;
                 }
 
                 NS_ADDREF(*aResult);
 
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -5059,28 +5059,27 @@ nsWindowSH::GlobalScopePolluterNewResolv
       document->GetCompatibilityMode() != eCompatibility_NavQuirks) {
     // If we don't have a document, or if the document is not in
     // quirks mode, return early.
 
     return JS_TRUE;
   }
 
   JSObject *proto = ::JS_GetPrototype(cx, obj);
-  JSString *jsstr = JSID_TO_STRING(id);
   JSBool hasProp;
 
   if (!proto || !::JS_HasPropertyById(cx, proto, id, &hasProp) ||
       hasProp) {
     // No prototype, or the property exists on the prototype. Do
     // nothing.
 
     return JS_TRUE;
   }
 
-  nsDependentJSString str(jsstr);
+  nsDependentJSString str(id);
   nsCOMPtr<nsISupports> result;
   nsWrapperCache *cache;
   {
     Element *element = document->GetElementById(str);
     result = element;
     cache = element;
   }
 
@@ -5409,17 +5408,20 @@ nsWindowSH::SetProperty(nsIXPConnectWrap
     nsresult rv = window->GetLocation(getter_AddRefs(location));
     NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && location, rv);
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     rv = WrapNative(cx, obj, location, &NS_GET_IID(nsIDOMLocation), PR_TRUE,
                     vp, getter_AddRefs(holder));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = location->SetHref(nsDependentJSString(val));
+    nsDependentJSString depStr;
+    NS_ENSURE_TRUE(depStr.init(cx, val), NS_ERROR_UNEXPECTED);
+
+    rv = location->SetHref(depStr);
 
     return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING;
   }
 
   return nsEventReceiverSH::SetProperty(wrapper, cx, obj, id, vp, _retval);
 }
 
 NS_IMETHODIMP
@@ -6264,24 +6266,24 @@ ResolvePrototype(nsIXPConnect *aXPConnec
 
   return NS_OK;
 }
 
 
 // static
 nsresult
 nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
-                          JSObject *obj, JSString *str, PRBool *did_resolve)
+                          JSObject *obj, jsid id, PRBool *did_resolve)
 {
   *did_resolve = PR_FALSE;
 
   nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
 
-  nsDependentJSString name(str);
+  nsDependentJSString name(id);
 
   const nsGlobalNameStruct *name_struct = nsnull;
   const PRUnichar *class_name = nsnull;
 
   nameSpaceManager->LookupName(name, &name_struct, &class_name);
 
   if (!name_struct) {
     return NS_OK;
@@ -6481,20 +6483,18 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
       }
 
       rv = WrapNative(cx, scope, native, PR_TRUE, &prop_val,
                       getter_AddRefs(holder));
     }
 
     NS_ENSURE_SUCCESS(rv, rv);
 
-    JSBool ok = ::JS_DefineUCProperty(cx, obj, ::JS_GetStringChars(str),
-                                      ::JS_GetStringLength(str),
-                                      prop_val, nsnull, nsnull,
-                                      JSPROP_ENUMERATE);
+    JSBool ok = ::JS_DefinePropertyById(cx, obj, id, prop_val, nsnull, nsnull,
+                                        JSPROP_ENUMERATE);
 
     *did_resolve = PR_TRUE;
 
     return ok ? NS_OK : NS_ERROR_FAILURE;
   }
 
   if (name_struct->mType == nsGlobalNameStruct::eTypeDynamicNameSet) {
     nsCOMPtr<nsIScriptExternalNameSet> nameset =
@@ -6643,32 +6643,30 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     return NS_OK;
   }
 
 
   // Hmm, we do an awful lot of QIs here; maybe we should add a
   // method on an interface that would let us just call into the
   // window code directly...
 
-  JSString *str = JSID_TO_STRING(id);
-
   if (!xpc::WrapperFactory::IsXrayWrapper(obj) ||
       xpc::WrapperFactory::IsPartiallyTransparent(obj)) {
     nsCOMPtr<nsIDocShellTreeNode> dsn(do_QueryInterface(win->GetDocShell()));
 
     PRInt32 count = 0;
 
     if (dsn) {
       dsn->GetChildCount(&count);
     }
 
     if (count > 0) {
       nsCOMPtr<nsIDocShellTreeItem> child;
 
-      const jschar *chars = ::JS_GetStringChars(str);
+      const jschar *chars = ::JS_GetInternedStringChars(JSID_TO_STRING(id));
 
       dsn->FindChildWithName(reinterpret_cast<const PRUnichar*>(chars),
                              PR_FALSE, PR_TRUE, nsnull, nsnull,
                              getter_AddRefs(child));
 
       nsCOMPtr<nsIDOMWindow> child_win(do_GetInterface(child));
 
       if (child_win) {
@@ -6707,17 +6705,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
   if (!(flags & JSRESOLVE_ASSIGNING)) {
     JSAutoRequest ar(cx);
 
     // Call GlobalResolve() after we call FindChildWithName() so
     // that named child frames will override external properties
     // which have been registered with the script namespace manager.
 
     JSBool did_resolve = JS_FALSE;
-    rv = GlobalResolve(win, cx, obj, str, &did_resolve);
+    rv = GlobalResolve(win, cx, obj, id, &did_resolve);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (did_resolve) {
       // GlobalResolve() resolved something, so we're done here.
       *objp = obj;
 
       return NS_OK;
     }
@@ -8377,17 +8375,20 @@ nsDocumentSH::SetProperty(nsIXPConnectWr
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (location) {
       JSAutoRequest ar(cx);
 
       JSString *val = ::JS_ValueToString(cx, *vp);
       NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED);
 
-      rv = location->SetHref(nsDependentJSString(val));
+      nsDependentJSString depStr;
+      NS_ENSURE_TRUE(depStr.init(cx, val), NS_ERROR_UNEXPECTED);
+
+      rv = location->SetHref(depStr);
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
       rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), location,
                       &NS_GET_IID(nsIDOMLocation), PR_TRUE, vp,
                       getter_AddRefs(holder));
       return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING;
     }
@@ -8466,17 +8467,20 @@ ResolveImpl(JSContext *cx, nsIXPConnectW
     static_cast<nsHTMLDocument*>(static_cast<nsINode*>(wrapper->Native()));
 
   // 'id' is not always a string, it can be a number since document.1
   // should map to <input name="1">. Thus we can't use
   // JSVAL_TO_STRING() here.
   JSString *str = IdToString(cx, id);
   NS_ENSURE_TRUE(str, NS_ERROR_UNEXPECTED);
 
-  return doc->ResolveName(nsDependentJSString(str), nsnull, result, aCache);
+  nsDependentJSString depStr;
+  NS_ENSURE_TRUE(depStr.init(cx, str), NS_ERROR_UNEXPECTED);
+
+  return doc->ResolveName(depStr, nsnull, result, aCache);
 }
 
 // static
 JSBool
 nsHTMLDocumentSH::DocumentOpen(JSContext *cx, uintN argc, jsval *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
@@ -8504,18 +8508,23 @@ nsHTMLDocumentSH::DocumentOpen(JSContext
 
   nsCAutoString contentType("text/html");
   if (argc > 0) {
     JSString* jsstr = JS_ValueToString(cx, argv[0]);
     if (!jsstr) {
       nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_OUT_OF_MEMORY);
       return JS_FALSE;
     }
+    nsDependentJSString depStr;
+    if (!depStr.init(cx, jsstr)) {
+      nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_OUT_OF_MEMORY);
+      return JS_FALSE;
+    }
     nsAutoString type;
-    type.Assign(nsDependentJSString(jsstr));
+    type.Assign(depStr);
     ToLowerCase(type);
     nsCAutoString actualType, dummy;
     NS_ParseContentType(NS_ConvertUTF16toUTF8(type), actualType, dummy);
     if (!actualType.EqualsLiteral("text/html") &&
         !type.EqualsLiteral("replace")) {
       contentType = "text/plain";
     }
   }
@@ -8523,19 +8532,23 @@ nsHTMLDocumentSH::DocumentOpen(JSContext
   PRBool replace = PR_FALSE;
   if (argc > 1) {
     JSString* jsstr = JS_ValueToString(cx, argv[1]);
     if (!jsstr) {
       nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_OUT_OF_MEMORY);
       return JS_FALSE;
     }
 
-    replace = NS_LITERAL_STRING("replace").
-      Equals(reinterpret_cast<const PRUnichar*>
-                             (::JS_GetStringChars(jsstr)));
+    const jschar *chars = ::JS_GetStringCharsZ(cx, jsstr);
+    if (!chars) {
+      nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_OUT_OF_MEMORY);
+      return JS_FALSE;
+    }
+
+    replace = NS_LITERAL_STRING("replace").Equals(chars);
   }
 
   nsCOMPtr<nsIDOMDocument> retval;
   nsresult rv = doc->Open(contentType, replace, getter_AddRefs(retval));
   if (NS_FAILED(rv)) {
     nsDOMClassInfo::ThrowJSException(cx, rv);
 
     return JS_FALSE;
@@ -8844,18 +8857,23 @@ nsHTMLDocumentSH::CallToGetPropMapper(JS
       return JS_FALSE;
   } else {
     // In other cases (i.e. document.all("foo")), self is passed as
     // the callee
 
     self = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
   }
 
-  return ::JS_GetUCProperty(cx, self, ::JS_GetStringChars(str),
-                            ::JS_GetStringLength(str), vp);
+  size_t length;
+  const jschar *chars = ::JS_GetStringCharsAndLength(cx, str, &length);
+  if (!chars) {
+    return JS_FALSE;
+  }
+
+  return ::JS_GetUCProperty(cx, self, chars, length, vp);
 }
 
 
 static inline JSObject *
 GetDocumentAllHelper(JSContext *cx, JSObject *obj)
 {
   while (obj && JS_GET_CLASS(cx, obj) != &sHTMLDocumentAllHelperClass) {
     obj = ::JS_GetPrototype(cx, obj);
@@ -8965,34 +8983,32 @@ nsHTMLDocumentSH::DocumentAllHelperNewRe
 JSBool
 nsHTMLDocumentSH::DocumentAllTagsNewResolve(JSContext *cx, JSObject *obj,
                                             jsid id, uintN flags,
                                             JSObject **objp)
 {
   if (JSID_IS_STRING(id)) {
     nsDocument *doc = GetDocument(cx, obj);
 
-    JSString *str = JSID_TO_STRING(id);
-
     JSObject *proto = ::JS_GetPrototype(cx, obj);
     if (NS_UNLIKELY(!proto)) {
       return JS_TRUE;
     }
 
     JSBool found;
     if (!::JS_HasPropertyById(cx, proto, id, &found)) {
       return JS_FALSE;
     }
 
     if (found) {
       return JS_TRUE;
     }
 
     nsRefPtr<nsContentList> tags =
-      doc->GetElementsByTagName(nsDependentJSString(str));
+      doc->GetElementsByTagName(nsDependentJSString(id));
 
     if (tags) {
       jsval v;
       nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
       nsresult rv = nsDOMClassInfo::WrapNative(cx, JS_GetGlobalForScopeChain(cx),
                                                static_cast<nsINodeList*>(tags),
                                                tags, PR_TRUE, &v,
                                                getter_AddRefs(holder));
@@ -9154,21 +9170,21 @@ nsHTMLDocumentSH::GetProperty(nsIXPConne
 
   return nsDocumentSH::GetProperty(wrapper, cx, obj, id, vp, _retval);
 }
 
 // HTMLFormElement helper
 
 // static
 nsresult
-nsHTMLFormElementSH::FindNamedItem(nsIForm *aForm, JSString *str,
+nsHTMLFormElementSH::FindNamedItem(nsIForm *aForm, jsid id,
                                    nsISupports **aResult,
                                    nsWrapperCache **aCache)
 {
-  nsDependentJSString name(str);
+  nsDependentJSString name(id);
 
   *aResult = aForm->ResolveName(name).get();
   // FIXME Get the wrapper cache from nsIForm::ResolveName
   *aCache = nsnull;
 
   if (!*aResult) {
     nsCOMPtr<nsIContent> content(do_QueryInterface(aForm));
     nsCOMPtr<nsIDOMHTMLFormElement> form_element(do_QueryInterface(aForm));
@@ -9192,18 +9208,17 @@ nsHTMLFormElementSH::NewResolve(nsIXPCon
 {
   // For native wrappers, do not resolve random names on form
   if ((!(JSRESOLVE_ASSIGNING & flags)) && JSID_IS_STRING(id) &&
       !ObjectIsNativeWrapper(cx, obj)) {
     nsCOMPtr<nsIForm> form(do_QueryWrappedNative(wrapper, obj));
     nsCOMPtr<nsISupports> result;
     nsWrapperCache *cache;
 
-    JSString *str = JSID_TO_STRING(id);
-    FindNamedItem(form, str, getter_AddRefs(result), &cache);
+    FindNamedItem(form, id, getter_AddRefs(result), &cache);
 
     if (result) {
       JSAutoRequest ar(cx);
       *_retval = ::JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, nsnull,
                                          nsnull, JSPROP_ENUMERATE);
 
       *objp = obj;
 
@@ -9223,18 +9238,17 @@ nsHTMLFormElementSH::GetProperty(nsIXPCo
   nsCOMPtr<nsIForm> form(do_QueryWrappedNative(wrapper, obj));
 
   if (JSID_IS_STRING(id)) {
     // For native wrappers, do not get random names on form
     if (!ObjectIsNativeWrapper(cx, obj)) {
       nsCOMPtr<nsISupports> result;
       nsWrapperCache *cache;
 
-      JSString *str = JSID_TO_STRING(id);
-      FindNamedItem(form, str, getter_AddRefs(result), &cache);
+      FindNamedItem(form, id, getter_AddRefs(result), &cache);
 
       if (result) {
         // Wrap result, result can be either an element or a list of
         // elements
         nsresult rv = WrapNative(cx, obj, result, cache, PR_TRUE, vp);
         return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING;
       }
     }
@@ -10168,21 +10182,24 @@ nsStorageSH::NewResolve(nsIXPConnectWrap
   // check if the key exists in the storage object.
 
   nsCOMPtr<nsIDOMStorageObsolete> storage(do_QueryWrappedNative(wrapper));
 
   JSString *jsstr = IdToString(cx, id);
   if (!jsstr)
     return JS_FALSE;
 
+  nsDependentJSString depStr;
+  if (!depStr.init(cx, jsstr))
+    return JS_FALSE;
+
   // GetItem() will return null if the caller can't access the session
   // storage item.
   nsCOMPtr<nsIDOMStorageItem> item;
-  nsresult rv = storage->GetItem(nsDependentJSString(jsstr),
-                                 getter_AddRefs(item));
+  nsresult rv = storage->GetItem(depStr, getter_AddRefs(item));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (item) {
     if (!::JS_DefinePropertyById(cx, realObj, id, JSVAL_VOID, nsnull, nsnull,
                                  JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
 
@@ -10207,21 +10224,26 @@ nsStorageSH::SetProperty(nsIXPConnectWra
                          jsval *vp, PRBool *_retval)
 {
   nsCOMPtr<nsIDOMStorageObsolete> storage(do_QueryWrappedNative(wrapper));
   NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED);
 
   JSString *key = IdToString(cx, id);
   NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED);
 
+  nsDependentJSString keyStr;
+  NS_ENSURE_TRUE(keyStr.init(cx, key), NS_ERROR_UNEXPECTED);
+
   JSString *value = ::JS_ValueToString(cx, *vp);
   NS_ENSURE_TRUE(value, NS_ERROR_UNEXPECTED);
 
-  nsresult rv = storage->SetItem(nsDependentJSString(key),
-                                 nsDependentJSString(value));
+  nsDependentJSString valueStr;
+  NS_ENSURE_TRUE(valueStr.init(cx, value), NS_ERROR_UNEXPECTED);
+
+  nsresult rv = storage->SetItem(keyStr, valueStr);
   if (NS_SUCCEEDED(rv)) {
     rv = NS_SUCCESS_I_DID_SOMETHING;
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
@@ -10230,17 +10252,20 @@ nsStorageSH::DelProperty(nsIXPConnectWra
                          jsval *vp, PRBool *_retval)
 {
   nsCOMPtr<nsIDOMStorageObsolete> storage(do_QueryWrappedNative(wrapper));
   NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED);
 
   JSString *key = IdToString(cx, id);
   NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED);
 
-  nsresult rv = storage->RemoveItem(nsDependentJSString(key));
+  nsDependentJSString keyStr;
+  NS_ENSURE_TRUE(keyStr.init(cx, key), NS_ERROR_UNEXPECTED);
+
+  nsresult rv = storage->RemoveItem(keyStr);
   if (NS_SUCCEEDED(rv)) {
     rv = NS_SUCCESS_I_DID_SOMETHING;
   }
 
   return rv;
 }
 
 
@@ -10329,20 +10354,23 @@ nsStorage2SH::NewResolve(nsIXPConnectWra
     return NS_OK;
   }
 
   // We're resolving property that doesn't exist on the prototype,
   // check if the key exists in the storage object.
 
   nsCOMPtr<nsIDOMStorage> storage(do_QueryWrappedNative(wrapper));
 
+  nsDependentJSString depStr;
+  NS_ENSURE_TRUE(depStr.init(cx, jsstr), NS_ERROR_UNEXPECTED);
+
   // GetItem() will return null if the caller can't access the session
   // storage item.
   nsAutoString data;
-  nsresult rv = storage->GetItem(nsDependentJSString(jsstr), data);
+  nsresult rv = storage->GetItem(depStr, data);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!DOMStringIsNull(data)) {
     if (!::JS_DefinePropertyById(cx, realObj, id, JSVAL_VOID, nsnull,
                                  nsnull, JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
 
@@ -10401,21 +10429,26 @@ nsStorage2SH::SetProperty(nsIXPConnectWr
                           jsval *vp, PRBool *_retval)
 {
   nsCOMPtr<nsIDOMStorage> storage(do_QueryWrappedNative(wrapper));
   NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED);
 
   JSString *key = IdToString(cx, id);
   NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED);
 
+  nsDependentJSString keyStr;
+  NS_ENSURE_TRUE(keyStr.init(cx, key), NS_ERROR_UNEXPECTED);
+
   JSString *value = ::JS_ValueToString(cx, *vp);
   NS_ENSURE_TRUE(value, NS_ERROR_UNEXPECTED);
 
-  nsresult rv = storage->SetItem(nsDependentJSString(key),
-                                 nsDependentJSString(value));
+  nsDependentJSString valueStr;
+  NS_ENSURE_TRUE(valueStr.init(cx, value), NS_ERROR_UNEXPECTED);
+
+  nsresult rv = storage->SetItem(keyStr, valueStr);
   if (NS_SUCCEEDED(rv)) {
     rv = NS_SUCCESS_I_DID_SOMETHING;
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
@@ -10424,17 +10457,20 @@ nsStorage2SH::DelProperty(nsIXPConnectWr
                           jsval *vp, PRBool *_retval)
 {
   nsCOMPtr<nsIDOMStorage> storage(do_QueryWrappedNative(wrapper));
   NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED);
 
   JSString *key = IdToString(cx, id);
   NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED);
 
-  nsresult rv = storage->RemoveItem(nsDependentJSString(key));
+  nsDependentJSString keyStr;
+  NS_ENSURE_TRUE(keyStr.init(cx, key), NS_ERROR_UNEXPECTED);
+
+  nsresult rv = storage->RemoveItem(keyStr);
   if (NS_SUCCEEDED(rv)) {
     rv = NS_SUCCESS_I_DID_SOMETHING;
   }
 
   return rv;
 }
 
 
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -449,17 +449,17 @@ protected:
   }
 
   static PRBool ReallyIsEventName(jsid id, jschar aFirstChar);
 
   static inline PRBool IsEventName(jsid id)
   {
     NS_ASSERTION(JSID_IS_STRING(id), "Don't pass non-string jsid's here!");
 
-    jschar *str = ::JS_GetStringChars(JSID_TO_STRING(id));
+    const jschar *str = ::JS_GetInternedStringChars(JSID_TO_STRING(id));
 
     if (str[0] == 'o' && str[1] == 'n') {
       return ReallyIsEventName(id, str[2]);
     }
 
     return PR_FALSE;
   }
 
@@ -514,18 +514,17 @@ protected:
   {
   }
 
   virtual ~nsWindowSH()
   {
   }
 
   static nsresult GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
-                                JSObject *obj, JSString *str,
-                                PRBool *did_resolve);
+                                JSObject *obj, jsid id, PRBool *did_resolve);
 
 public:
   NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
                        JSObject *globalObj, JSObject **parentObj);
 #ifdef DEBUG
   NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj)
   {
@@ -1032,17 +1031,17 @@ protected:
   nsHTMLFormElementSH(nsDOMClassInfoData* aData) : nsElementSH(aData)
   {
   }
 
   virtual ~nsHTMLFormElementSH()
   {
   }
 
-  static nsresult FindNamedItem(nsIForm *aForm, JSString *str,
+  static nsresult FindNamedItem(nsIForm *aForm, jsid id,
                                 nsISupports **aResult, nsWrapperCache **aCache);
 
 public:
   NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, jsid id, PRUint32 flags,
                         JSObject **objp, PRBool *_retval);
   NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsid id, jsval *vp,
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -719,18 +719,23 @@ LocaleToUnicode(JSContext *cx, char *src
   return JS_TRUE;
 }
 
 
 static JSBool
 ChangeCase(JSContext *cx, JSString *src, jsval *rval,
            void(* changeCaseFnc)(const nsAString&, nsAString&))
 {
+  nsDependentJSString depStr;
+  if (!depStr.init(cx, src)) {
+    return JS_FALSE;
+  }
+
   nsAutoString result;
-  changeCaseFnc(nsDependentJSString(src), result);
+  changeCaseFnc(depStr, result);
 
   JSString *ucstr = JS_NewUCStringCopyN(cx, (jschar*)result.get(), result.Length());
   if (!ucstr) {
     return JS_FALSE;
   }
 
   *rval = STRING_TO_JSVAL(ucstr);
 
@@ -774,21 +779,24 @@ LocaleCompare(JSContext *cx, JSString *s
 
     if (NS_FAILED(rv)) {
       nsDOMClassInfo::ThrowJSException(cx, rv);
 
       return JS_FALSE;
     }
   }
 
+  nsDependentJSString depStr1, depStr2;
+  if (!depStr1.init(cx, src1) || !depStr2.init(cx, src2)) {
+    return JS_FALSE;
+  }
+
   PRInt32 result;
   rv = gCollation->CompareString(nsICollation::kCollationStrengthDefault,
-                                 nsDependentJSString(src1),
-                                 nsDependentJSString(src2),
-                                 &result);
+                                 depStr1, depStr2, &result);
 
   if (NS_FAILED(rv)) {
     nsDOMClassInfo::ThrowJSException(cx, rv);
 
     return JS_FALSE;
   }
 
   *rval = INT_TO_JSVAL(result);
@@ -1586,37 +1594,46 @@ JSValueToAString(JSContext *cx, jsval va
     *isUndefined = JSVAL_IS_VOID(val);
   }
 
   if (!result) {
     return NS_OK;
   }
 
   JSString* jsstring = ::JS_ValueToString(cx, val);
-  if (jsstring) {
-    result->Assign(reinterpret_cast<const PRUnichar*>
-                                   (::JS_GetStringChars(jsstring)),
-                   ::JS_GetStringLength(jsstring));
-  } else {
-    result->Truncate();
-
-    // We failed to convert val to a string. We're either OOM, or the
-    // security manager denied access to .toString(), or somesuch, on
-    // an object. Treat this case as if the result were undefined.
-
-    if (isUndefined) {
-      *isUndefined = PR_TRUE;
-    }
-
-    if (!::JS_IsExceptionPending(cx)) {
-      // JS_ValueToString() returned null w/o an exception
-      // pending. That means we're OOM.
-
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
+  if (!jsstring) {
+    goto error;
+  }
+
+  size_t length;
+  const jschar *chars;
+  chars = ::JS_GetStringCharsAndLength(cx, jsstring, &length);
+  if (!chars) {
+    goto error;
+  }
+
+  result->Assign(chars, length);
+  return NS_OK;
+
+error:
+  // We failed to convert val to a string. We're either OOM, or the
+  // security manager denied access to .toString(), or somesuch, on
+  // an object. Treat this case as if the result were undefined.
+
+  result->Truncate();
+
+  if (isUndefined) {
+    *isUndefined = PR_TRUE;
+  }
+
+  if (!::JS_IsExceptionPending(cx)) {
+    // JS_ValueToString()/JS_GetStringCharsAndLength returned null w/o an
+    // exception pending. That means we're OOM.
+
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_OK;
 }
 
 nsIScriptObjectPrincipal*
 nsJSContext::GetObjectPrincipal()
 {
@@ -2543,47 +2560,24 @@ nsJSContext::CreateNativeGlobalForInner(
 
 nsresult
 nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal)
 {
   NS_ENSURE_ARG(aNewInner);
   JSObject *newInnerJSObject = (JSObject *)aNewInner->GetScriptGlobal(JAVASCRIPT);
   JSObject *outerGlobal = (JSObject *)aOuterGlobal;
 
-  // Make the inner and outer window both share the same
-  // prototype. The prototype we share is the outer window's
-  // prototype, this way XPConnect can still find the wrapper to
-  // use when making a call like alert() (w/o qualifying it with
-  // "window."). XPConnect looks up the wrapper based on the
-  // function object's parent, which is the object the function
-  // was called on, and when calling alert() we'll be calling the
-  // alert() function from the outer window's prototype off of the
-  // inner window. In this case XPConnect is able to find the
-  // outer (through the JSExtendedClass hook outerObject), so this
-  // prototype sharing works.
-
   // Now that we're connecting the outer global to the inner one,
   // we must have transplanted it. The JS engine tries to maintain
   // the global object's compartment as its default compartment,
   // so update that now since it might have changed.
   JS_SetGlobalObject(mContext, outerGlobal);
-
-  // We do *not* want to use anything else out of the outer
-  // object's prototype chain than the first prototype, which is
-  // the XPConnect prototype. The rest we want from the inner
-  // window's prototype, i.e. the global scope polluter and
-  // Object.prototype. This way the outer also gets the benefits
-  // of the global scope polluter, and the inner window's
-  // Object.prototype.
-  JSObject *proto = JS_GetPrototype(mContext, outerGlobal);
-  JSObject *innerProto = JS_GetPrototype(mContext, newInnerJSObject);
-  JSObject *innerProtoProto = JS_GetPrototype(mContext, innerProto);
-
-  JS_SetPrototype(mContext, newInnerJSObject, proto);
-  JS_SetPrototype(mContext, proto, innerProtoProto);
+  NS_ASSERTION(JS_GetPrototype(mContext, outerGlobal) ==
+               JS_GetPrototype(mContext, newInnerJSObject),
+               "outer and inner globals should have the same prototype");
 
   return NS_OK;
 }
 
 void *
 nsJSContext::GetNativeContext()
 {
   return mContext;
@@ -2621,45 +2615,46 @@ nsJSContext::CreateOuterObject(nsIScript
 
     // Always enable E4X for XUL and other chrome content -- there is no
     // need to preserve the <!-- script hiding hack from JS-in-HTML daze
     // (introduced in 1995 for graceful script degradation in Netscape 1,
     // Mosaic, and other pre-JS browsers).
     JS_SetOptions(mContext, JS_GetOptions(mContext) | JSOPTION_XML);
   }
 
-  nsIXPConnect *xpc = nsContentUtils::XPConnect();
-  nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-
-  nsresult rv = xpc->WrapNative(mContext, aCurrentInner->GetGlobalJSObject(),
-                                aCurrentInner, NS_GET_IID(nsISupports),
-                                getter_AddRefs(holder));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIXPConnectWrappedNative> wrapper(do_QueryInterface(holder));
-  NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
-
-  wrapper->RefreshPrototype();
-
   JSObject *outer =
     NS_NewOuterWindowProxy(mContext, aCurrentInner->GetGlobalJSObject());
   if (!outer) {
     return NS_ERROR_FAILURE;
   }
 
   return SetOuterObject(outer);
 }
 
 nsresult
 nsJSContext::SetOuterObject(void *aOuterObject)
 {
   JSObject *outer = static_cast<JSObject *>(aOuterObject);
 
   // Force our context's global object to be the outer.
   JS_SetGlobalObject(mContext, outer);
+
+  // NB: JS_SetGlobalObject sets mContext->compartment.
+  JSObject *inner = JS_GetParent(mContext, outer);
+
+  nsIXPConnect *xpc = nsContentUtils::XPConnect();
+  nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
+  nsresult rv = xpc->GetWrappedNativeOfJSObject(mContext, inner,
+                                                getter_AddRefs(wrapper));
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
+
+  wrapper->RefreshPrototype();
+  JS_SetPrototype(mContext, outer, JS_GetPrototype(mContext, inner));
+
   return NS_OK;
 }
 
 nsresult
 nsJSContext::InitOuterWindow()
 {
   JSObject *global = JS_GetGlobalObject(mContext);
   OBJ_TO_INNER_OBJECT(mContext, global);
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -104,17 +104,17 @@ private:
   // filename, line number and JS language version string of the
   // caller of setTimeout()
   nsCString mFileName;
   PRUint32 mLineNo;
   PRUint32 mVersion;
   nsCOMPtr<nsIArray> mArgv;
 
   // The JS expression to evaluate or function to call, if !mExpr
-  JSString *mExpr;
+  JSFlatString *mExpr;
   JSObject *mFunObj;
 };
 
 
 // nsJSScriptTimeoutHandler
 // QueryInterface implementation for nsJSScriptTimeoutHandler
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
 NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSScriptTimeoutHandler)
@@ -129,20 +129,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
       foo.Append(tmp->mFileName);
       foo.AppendLiteral(":");
       foo.AppendInt(tmp->mLineNo);
       foo.AppendLiteral("]");
     }
     else if (tmp->mFunObj) {
       JSFunction* fun = (JSFunction*)tmp->mFunObj->getPrivate();
       if (fun->atom) {
-        size_t size = 1 + JS_PutEscapedString(NULL, 0, ATOM_TO_STRING(fun->atom), 0);
+        size_t size = 1 + JS_PutEscapedFlatString(NULL, 0, ATOM_TO_STRING(fun->atom), 0);
         char *name = new char[size];
         if (name) {
-          JS_PutEscapedString(name, size, ATOM_TO_STRING(fun->atom), 0);
+          JS_PutEscapedFlatString(name, size, ATOM_TO_STRING(fun->atom), 0);
           foo.AppendLiteral(" [");
           foo.Append(name);
           delete[] name;
           foo.AppendLiteral("]");
         }
       }
     }
     cb.DescribeNode(RefCounted, tmp->mRefCnt.get(),
@@ -227,17 +227,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRUint32 argc;
   jsval *argv = nsnull;
 
   ncc->GetArgc(&argc);
   ncc->GetArgvPtr(&argv);
 
-  JSString *expr = nsnull;
+  JSFlatString *expr = nsnull;
   JSObject *funobj = nsnull;
   int32 interval = 0;
 
   JSAutoRequest ar(cx);
 
   if (argc < 1) {
     ::JS_ReportError(cx, "Function %s requires at least 2 parameter",
                      *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
@@ -259,20 +259,27 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
 
   switch (::JS_TypeOfValue(cx, argv[0])) {
   case JSTYPE_FUNCTION:
     funobj = JSVAL_TO_OBJECT(argv[0]);
     break;
 
   case JSTYPE_STRING:
   case JSTYPE_OBJECT:
-    expr = ::JS_ValueToString(cx, argv[0]);
-    if (!expr)
-      return NS_ERROR_OUT_OF_MEMORY;
-    argv[0] = STRING_TO_JSVAL(expr);
+    {
+      JSString *str = ::JS_ValueToString(cx, argv[0]);
+      if (!str)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+      expr = ::JS_FlattenString(cx, str);
+      if (!expr)
+          return NS_ERROR_OUT_OF_MEMORY;
+
+      argv[0] = STRING_TO_JSVAL(str);
+    }
     break;
 
   default:
     ::JS_ReportError(cx, "useless %s call (missing quotes around argument?)",
                      *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
 
     // Return an error that nsGlobalWindow can recognize and turn into NS_OK.
     return NS_ERROR_DOM_TYPE_ERR;
@@ -365,18 +372,17 @@ void nsJSScriptTimeoutHandler::SetLatene
     NS_ERROR("How can our argv not handle this?");
   }
 }
 
 const PRUnichar *
 nsJSScriptTimeoutHandler::GetHandlerText()
 {
   NS_ASSERTION(mExpr, "No expression, so no handler text!");
-  return reinterpret_cast<const PRUnichar *>
-                         (::JS_GetStringChars(mExpr));
+  return ::JS_GetFlatStringChars(mExpr);
 }
 
 nsresult NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow,
                                    PRBool *aIsInterval,
                                    PRInt32 *aInterval,
                                    nsIScriptTimeoutHandler **aRet)
 {
   *aRet = nsnull;
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -70,31 +70,52 @@ public:
 
   static nsIScriptContext *GetDynamicScriptContext(JSContext *aContext);
 };
 
 
 class nsDependentJSString : public nsDependentString
 {
 public:
-  explicit nsDependentJSString(jsval v)
-    : nsDependentString((PRUnichar *)::JS_GetStringChars(JSVAL_TO_STRING(v)),
-                        ::JS_GetStringLength(JSVAL_TO_STRING(v)))
+  /**
+   * In the case of string ids, getting the string's chars is infallible, so
+   * the dependent string can be constructed directly.
+   */
+  explicit nsDependentJSString(jsid id)
+    : nsDependentString(JS_GetInternedStringChars(JSID_TO_STRING(id)),
+                        JS_GetStringLength(JSID_TO_STRING(id)))
   {
   }
 
-  explicit nsDependentJSString(jsid id)
-    : nsDependentString((PRUnichar *)::JS_GetStringChars(JSID_TO_STRING(id)),
-                        ::JS_GetStringLength(JSID_TO_STRING(id)))
+  /**
+   * For all other strings, the nsDependentJSString object should be default
+   * constructed, which leaves it empty (this->IsEmpty()), and initialized with
+   * one of the fallible init() methods below.
+   */
+
+  nsDependentJSString()
   {
   }
 
-  explicit nsDependentJSString(JSString *str)
-    : nsDependentString((PRUnichar *)::JS_GetStringChars(str), ::JS_GetStringLength(str))
+  JSBool init(JSContext* aContext, JSString* str)
   {
+      size_t length;
+      const jschar* chars = JS_GetStringCharsZAndLength(aContext, str, &length);
+      if (!chars)
+          return JS_FALSE;
+
+      NS_ASSERTION(IsEmpty(), "init() on initialized string");
+      nsDependentString* base = this;
+      new(base) nsDependentString(chars, length);
+      return JS_TRUE;
+  }
+
+  JSBool init(JSContext* aContext, const jsval &v)
+  {
+      return init(aContext, JSVAL_TO_STRING(v));
   }
 
   ~nsDependentJSString()
   {
   }
 };
 
 #endif /* nsJSUtils_h__ */
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -420,17 +420,17 @@ IDBCursor::Continue(const jsval &aKey,
   }
 
   if (mContinueCalled) {
     // XXX Update the spec for precise behavior here.
     return NS_OK;
   }
 
   Key key;
-  nsresult rv = IDBObjectStore::GetKeyFromJSVal(aKey, key);
+  nsresult rv = IDBObjectStore::GetKeyFromJSVal(aKey, aCx, key);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR);
 
   if (key.IsNull()) {
     if (aOptionalArgCount) {
       return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
     }
     else {
       key = Key::UNSETKEY;
@@ -499,17 +499,17 @@ IDBCursor::Update(const jsval& aValue,
 
       ok = JS_DefineUCProperty(aCx, JSVAL_TO_OBJECT(clone.jsval_value()),
                                keyPathChars, keyPathLen, prop.jsval_value(), nsnull,
                                nsnull, JSPROP_ENUMERATE);
       NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     }
     else {
       Key newKey;
-      rv = IDBObjectStore::GetKeyFromJSVal(prop.jsval_value(), newKey);
+      rv = IDBObjectStore::GetKeyFromJSVal(prop.jsval_value(), aCx, newKey);
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (newKey.IsUnset() || newKey.IsNull() || newKey != key) {
         return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
       }
     }
   }
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -352,17 +352,17 @@ GetKeyFromObject(JSContext* aCx,
 
   const jschar* keyPathChars = reinterpret_cast<const jschar*>(aKeyPath.get());
   const size_t keyPathLen = aKeyPath.Length();
 
   jsval key;
   JSBool ok = JS_GetUCProperty(aCx, aObj, keyPathChars, keyPathLen, &key);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
-  nsresult rv = IDBObjectStore::GetKeyFromJSVal(key, aKey);
+  nsresult rv = IDBObjectStore::GetKeyFromJSVal(key, aCx, aKey);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 inline
 already_AddRefed<IDBRequest>
 GenerateRequest(IDBObjectStore* aObjectStore)
@@ -437,26 +437,31 @@ IDBObjectStore::GetKeyFromVariant(nsIVar
   }
 
   return NS_OK;
 }
 
 // static
 nsresult
 IDBObjectStore::GetKeyFromJSVal(jsval aKeyVal,
+                                JSContext* aCx,
                                 Key& aKey)
 {
   if (JSVAL_IS_VOID(aKeyVal)) {
     aKey = Key::UNSETKEY;
   }
   else if (JSVAL_IS_NULL(aKeyVal)) {
     aKey = Key::NULLKEY;
   }
   else if (JSVAL_IS_STRING(aKeyVal)) {
-    aKey = nsDependentJSString(aKeyVal);
+    nsDependentJSString depStr;
+    if (!depStr.init(aCx, JSVAL_TO_STRING(aKeyVal))) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    aKey = depStr;
   }
   else if (JSVAL_IS_INT(aKeyVal)) {
     aKey = JSVAL_TO_INT(aKeyVal);
   }
   else if (JSVAL_IS_DOUBLE(aKeyVal)) {
     aKey = JSVAL_TO_DOUBLE(aKeyVal);
   }
   else {
@@ -581,17 +586,22 @@ IDBObjectStore::GetKeyPathValueFromJSON(
     reinterpret_cast<const jschar*>(aKeyPath.BeginReading());
   const size_t keyPathLen = aKeyPath.Length();
 
   js::AutoValueRooter value(*aCx);
   JSBool ok = JS_GetUCProperty(*aCx, obj, keyPathChars, keyPathLen,
                                value.jsval_addr());
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
-  rv = GetKeyFromJSVal(value.jsval_value(), aValue);
+  rv = GetKeyFromJSVal(value.jsval_value(), *aCx, aValue);
+  if (rv == NS_ERROR_OUT_OF_MEMORY) {
+    NS_ASSERTION(JS_IsExceptionPending(*aCx), "OOM from JS should throw");
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
   if (NS_FAILED(rv) || aValue.IsNull()) {
     // If the object doesn't have a value that we can use for our index then we
     // leave it unset.
     aValue = Key::UNSETKEY;
   }
 
   return NS_OK;
 }
@@ -622,17 +632,22 @@ IDBObjectStore::GetIndexUpdateInfo(Objec
       const size_t keyPathLen = indexInfo.keyPath.Length();
 
       jsval keyPathValue;
       JSBool ok = JS_GetUCProperty(aCx, cloneObj, keyPathChars, keyPathLen,
                                    &keyPathValue);
       NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
       Key value;
-      nsresult rv = GetKeyFromJSVal(keyPathValue, value);
+      nsresult rv = GetKeyFromJSVal(keyPathValue, aCx, value);
+      if (rv == NS_ERROR_OUT_OF_MEMORY) {
+        NS_ASSERTION(JS_IsExceptionPending(aCx), "OOM from JS should throw");
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+
       if (NS_FAILED(rv) || value.IsUnset() || value.IsNull()) {
         // Not a value we can do anything with, ignore it.
         continue;
       }
 
       IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
       updateInfo->info = indexInfo;
       updateInfo->value = value;
@@ -789,17 +804,17 @@ IDBObjectStore::GetAddInfo(JSContext* aC
                            Key& aKey,
                            nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
 {
   nsresult rv;
 
   JSAutoRequest ar(aCx);
 
   if (mKeyPath.IsEmpty()) {
-    rv = GetKeyFromJSVal(aKeyVal, aKey);
+    rv = GetKeyFromJSVal(aKeyVal, aCx, aKey);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR);
   }
   else {
     // Inline keys live on the object. Make sure it is an object.
     if (JSVAL_IS_PRIMITIVE(aValue)) {
       return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
     }
 
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -73,16 +73,17 @@ public:
          const ObjectStoreInfo* aInfo);
 
   static nsresult
   GetKeyFromVariant(nsIVariant* aKeyVariant,
                     Key& aKey);
 
   static nsresult
   GetKeyFromJSVal(jsval aKeyVal,
+                  JSContext* aCx,
                   Key& aKey);
 
   static nsresult
   GetJSValFromKey(const Key& aKey,
                   JSContext* aCx,
                   jsval* aKeyVal);
 
   static nsresult
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -330,17 +330,24 @@ nsresult nsJSThunk::EvaluateScript(nsICh
             JS_ReportPendingException(cx);
             isUndefined = PR_TRUE;
         } else {
             isUndefined = rval == JSVAL_VOID;
         }
 
         if (!isUndefined && NS_SUCCEEDED(rv)) {
             NS_ASSERTION(JSVAL_IS_STRING(rval), "evalInSandbox is broken");
-            result = nsDependentJSString(JSVAL_TO_STRING(rval));
+
+            nsDependentJSString depStr;
+            if (!depStr.init(cx, JSVAL_TO_STRING(rval))) {
+                JS_ReportPendingException(cx);
+                isUndefined = PR_TRUE;
+            } else {
+                result = depStr;
+            }
         }
 
         stack->Pop(nsnull);
     } else {
         // No need to use the sandbox, evaluate the script directly in
         // the given scope.
         rv = scriptContext->EvaluateString(NS_ConvertUTF8toUTF16(script),
                                            globalJSObject, // obj
--- a/dom/src/threads/nsDOMWorker.cpp
+++ b/dom/src/threads/nsDOMWorker.cpp
@@ -137,19 +137,21 @@ nsDOMWorkerFunctions::Dump(JSContext* aC
 {
   JS_SET_RVAL(cx, aVp, JSVAL_VOID);
   if (!nsGlobalWindow::DOMWindowDumpEnabled()) {
     return JS_TRUE;
   }
 
   JSString* str;
   if (aArgc && (str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0])) && str) {
-    nsDependentJSString string(str);
-    fputs(NS_ConvertUTF16toUTF8(nsDependentJSString(str)).get(), stderr);
-    fflush(stderr);
+    nsDependentJSString depStr;
+    if (depStr.init(aCx, str)) {
+      fputs(NS_ConvertUTF16toUTF8(depStr).get(), stderr);
+      fflush(stderr);
+    }
   }
   return JS_TRUE;
 }
 
 JSBool
 nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx,
                                   uintN aArgc,
                                   jsval* aVp,
@@ -263,17 +265,22 @@ nsDOMWorkerFunctions::LoadScripts(JSCont
     if (!str) {
       JS_ReportError(aCx, "Couldn't convert argument %d to a string", index);
       return JS_FALSE;
     }
 
     nsString* newURL = urls.AppendElement();
     NS_ASSERTION(newURL, "Shouldn't fail if SetCapacity succeeded above!");
 
-    newURL->Assign(nsDependentJSString(str));
+    nsDependentJSString depStr;
+    if (!depStr.init(aCx, str)) {
+      return JS_FALSE;
+    }
+
+    newURL->Assign(depStr);
   }
 
   nsRefPtr<nsDOMWorkerScriptLoader> loader =
     new nsDOMWorkerScriptLoader(worker);
   if (!loader) {
     JS_ReportOutOfMemory(aCx);
     return JS_FALSE;
   }
@@ -362,49 +369,18 @@ nsDOMWorkerFunctions::AtoB(JSContext* aC
     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));
@@ -414,49 +390,18 @@ nsDOMWorkerFunctions::BtoA(JSContext* aC
     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));
@@ -564,18 +509,17 @@ nsDOMWorkerFunctions::CTypesLazyGetter(J
                                        JSObject* aObj,
                                        jsid aId,
                                        jsval* aVp)
 {
 #ifdef DEBUG
   {
     NS_ASSERTION(JS_GetGlobalForObject(aCx, aObj) == aObj, "Bad object!");
     NS_ASSERTION(JSID_IS_STRING(aId), "Not a string!");
-    JSString* str = JSID_TO_STRING(aId);
-    NS_ASSERTION(nsDependentJSString(str).EqualsLiteral("ctypes"), "Bad id!");
+    NS_ASSERTION(nsDependentJSString(aId).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()) {
     return JS_FALSE;
@@ -731,24 +675,24 @@ nsDOMWorkerScope::AddProperty(nsIXPConne
   JSObject* funObj;
   if (!(JSID_IS_STRING(aId) &&
         JSVAL_IS_OBJECT(*aVp) &&
         (funObj = JSVAL_TO_OBJECT(*aVp)) &&
         JS_ObjectIsFunction(aCx, funObj))) {
     return NS_OK;
   }
 
-  JSString *str = JSID_TO_STRING(aId);
+  JSFlatString *str = JSID_TO_FLAT_STRING(aId);
 
   // Figure out which listener we're setting.
   SetListenerFunc func;
-  if (JS_MatchStringAndAscii(str, "onmessage")) {
+  if (JS_FlatStringEqualsAscii(str, "onmessage")) {
     func = &nsDOMWorkerScope::SetOnmessage;
   }
-  else if (JS_MatchStringAndAscii(str, "onerror")) {
+  else if (JS_FlatStringEqualsAscii(str, "onerror")) {
     func = &nsDOMWorkerScope::SetOnerror;
   }
   else {
     // Some other function, we don't need to do anything special after all.
     return NS_OK;
   }
 
   // Wrap the function as an nsIDOMEventListener.
@@ -1359,17 +1303,20 @@ nsDOMWorker::InitializeInternal(nsIScrip
                                 jsval* aArgv)
 {
   NS_ENSURE_TRUE(aArgc, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
   NS_ENSURE_ARG_POINTER(aArgv);
 
   JSString* str = JS_ValueToString(aCx, aArgv[0]);
   NS_ENSURE_TRUE(str, NS_ERROR_XPC_BAD_CONVERT_JS);
 
-  mScriptURL.Assign(nsDependentJSString(str));
+  nsDependentJSString depStr;
+  NS_ENSURE_TRUE(depStr.init(aCx, str), NS_ERROR_OUT_OF_MEMORY);
+
+  mScriptURL.Assign(depStr);
   NS_ENSURE_FALSE(mScriptURL.IsEmpty(), NS_ERROR_INVALID_ARG);
 
   mLock = nsAutoLock::NewLock("nsDOMWorker::mLock");
   NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
 
   NS_ASSERTION(!mGlobal, "Already got a global?!");
 
   nsCOMPtr<nsIXPConnectJSObjectHolder> thisWrapped;
--- a/dom/src/threads/nsDOMWorkerTimeout.cpp
+++ b/dom/src/threads/nsDOMWorkerTimeout.cpp
@@ -191,21 +191,20 @@ nsDOMWorkerTimeout::ExpressionCallback::
   NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
 
   JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal();
   NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
 
   JSString* expression = JS_ValueToString(aCx, mExpression);
   NS_ENSURE_TRUE(expression, NS_ERROR_FAILURE);
 
-  jschar* string = JS_GetStringChars(expression);
+  size_t stringLength;
+  const jschar* string = JS_GetStringCharsAndLength(aCx, expression, &stringLength);
   NS_ENSURE_TRUE(string, NS_ERROR_FAILURE);
 
-  size_t stringLength = JS_GetStringLength(expression);
-
   jsval rval;
   PRBool success = JS_EvaluateUCScriptForPrincipals(aCx, global, principal,
                                                     string, stringLength,
                                                     mFileName.get(),
                                                     mLineNumber, &rval);
   if (!success) {
     return NS_ERROR_FAILURE;
   }
--- 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/dom/tests/mochitest/bugs/Makefile.in
+++ b/dom/tests/mochitest/bugs/Makefile.in
@@ -121,12 +121,13 @@ include $(topsrcdir)/config/rules.mk
 		test_DOMWindowCreated_chromeonly.html \
 		test_bug581072.html \
 		test_bug583225.html \
 		test_bug585240.html \
 		test_bug585819.html \
 		test_bug369306.html \
 		test_bug61098.html \
 		test_bug597809.html \
+		test_bug612267.html \
 		$(NULL)
 
 libs:: 	$(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/test_bug612267.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=612267
+-->
+<head>
+  <title>Test for Bug 393974</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=612267">Mozilla Bug 612267</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+Window.prototype.test = 'PASS';
+is(window.test, 'PASS', "setting Window.prototype affects window.__proto__");
+is(test, 'PASS', "setting Window.prototype affects the inner window lookup for sure");
+
+</script>
+</pre>
+</body>
+</html>
--- a/extensions/jssh/nsJSSh.cpp
+++ b/extensions/jssh/nsJSSh.cpp
@@ -273,30 +273,33 @@ static JSBool
 SetProtocol(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
   if (argc!=1) return JS_FALSE;
   nsJSSh* shell;
   if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
 
   JSAutoRequest ar(cx);
 
-  JSString *protocol = JS_ValueToString(cx, argv[0]);
+  JSString *str = JS_ValueToString(cx, argv[0]);
+  if (!str) return JS_FALSE;
+
+  JSFlatString *protocol = JS_FlattenString(cx, str);
   if (!protocol) return JS_FALSE;
   
-  if (JS_MatchStringAndAscii(protocol, "interactive")) {
+  if (JS_FlatStringEqualsAscii(protocol, "interactive")) {
     shell->mEmitHeader = PR_FALSE;
     shell->mPrompt = NS_LITERAL_CSTRING("\n> ");
     shell->mProtocol = protocol;
   }
-  else if (JS_MatchStringAndAscii(protocol, "synchronous")) {
+  else if (JS_FlatStringEqualsAscii(protocol, "synchronous")) {
     shell->mEmitHeader = PR_TRUE;
     shell->mPrompt = NS_LITERAL_CSTRING("\n> ");
     shell->mProtocol = protocol;
   }
-  else if (JS_MatchStringAndAscii(protocol, "plain")) {
+  else if (JS_FlatStringEqualsAscii(protocol, "plain")) {
     shell->mEmitHeader = PR_FALSE;
     shell->mPrompt = NS_LITERAL_CSTRING("\n");
     shell->mProtocol = protocol;
   }
   else return JS_FALSE;
   
   return JS_TRUE;
 }
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -1248,20 +1248,23 @@ XPCShellEnvironment::EvaluateString(cons
           aResult->Truncate();
       }
 
       jsval result;
       JSBool ok = JS_ExecuteScript(mCx, global, script, &result);
       if (ok && result != JSVAL_VOID) {
           JSErrorReporter old = JS_SetErrorReporter(mCx, NULL);
           JSString* str = JS_ValueToString(mCx, result);
+          nsDependentJSString depStr;
+          if (str)
+              depStr.init(mCx, str);
           JS_SetErrorReporter(mCx, old);
 
-          if (str && aResult) {
-              aResult->Assign(nsDependentJSString(str));
+          if (!depStr.IsEmpty() && aResult) {
+              aResult->Assign(depStr);
           }
       }
   }
 
   JS_DestroyScript(mCx, script);
 
   return true;
 }
--- a/js/ipc/Makefile.in
+++ b/js/ipc/Makefile.in
@@ -58,13 +58,18 @@ EXPORTS_mozilla/jsipc = \
   ObjectWrapperChild.h \
   $(NULL)
 
 CPPSRCS = \
   ObjectWrapperParent.cpp \
   ObjectWrapperChild.cpp \
   $(NULL)
 
+# For nsDependentJSString
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/dom/base \
+  $(NULL)
+
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 DEFINES += -DBIN_SUFFIX='"$(BIN_SUFFIX)"'
--- a/js/ipc/ObjectWrapperChild.cpp
+++ b/js/ipc/ObjectWrapperChild.cpp
@@ -45,16 +45,17 @@
 #include "mozilla/jsipc/ObjectWrapperChild.h"
 #include "mozilla/jsipc/CPOWTypes.h"
 
 #include "jsapi.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "nsContentUtils.h"
 #include "nsIJSContextStack.h"
+#include "nsJSUtils.h"
 
 using namespace mozilla::jsipc;
 using namespace js;
 
 namespace {
 
     class AutoContextPusher {
 
@@ -183,18 +184,22 @@ ObjectWrapperChild::jsval_to_JSVariant(J
         if (from != JSVAL_NULL)
             return false;
         // fall through
     case JSTYPE_FUNCTION:
         // fall through
     case JSTYPE_OBJECT:
         return JSObject_to_JSVariant(cx, JSVAL_TO_OBJECT(from), to);
     case JSTYPE_STRING:
-        *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(from)),
-                                JS_GetStringLength(JSVAL_TO_STRING(from)));
+        {
+            nsDependentJSString depStr;
+            if (!depStr.init(cx, from))
+                return false;
+            *to = depStr;
+        }
         return true;
     case JSTYPE_NUMBER:
         if (JSVAL_IS_INT(from))
             *to = JSVAL_TO_INT(from);
         else if (JSVAL_IS_DOUBLE(from))
             *to = JSVAL_TO_DOUBLE(from);
         else return false;
         return true;
@@ -277,20 +282,20 @@ ObjectWrapperChild::Manager()
 {
     PContextWrapperChild* pcwc = PObjectWrapperChild::Manager();
     return static_cast<ContextWrapperChild*>(pcwc);
 }
 
 static bool
 jsid_to_nsString(JSContext* cx, jsid from, nsString* to)
 {
-    jsval v;
-    if (JS_IdToValue(cx, from, &v) && JSVAL_IS_STRING(v)) {
-        *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(v)),
-                                JS_GetStringLength(JSVAL_TO_STRING(v)));
+    if (JSID_IS_STRING(from)) {
+        size_t length;
+        const jschar* chars = JS_GetInternedStringCharsAndLength(JSID_TO_STRING(from), &length);
+        *to = nsDependentString(chars, length);
         return true;
     }
     return false;
 }
     
 static bool
 jsid_from_nsString(JSContext* cx, const nsString& from, jsid* to)
 {
--- a/js/ipc/ObjectWrapperParent.cpp
+++ b/js/ipc/ObjectWrapperParent.cpp
@@ -37,16 +37,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/jsipc/ObjectWrapperParent.h"
 #include "mozilla/jsipc/ContextWrapperParent.h"
 #include "mozilla/jsipc/CPOWTypes.h"
 #include "mozilla/unused.h"
+#include "nsJSUtils.h"
 
 #include "jsobj.h"
 #include "jsfun.h"
 #include "jsutil.h"
 
 using namespace mozilla::jsipc;
 
 namespace {
@@ -260,18 +261,22 @@ ObjectWrapperParent::jsval_to_JSVariant(
         {
             PObjectWrapperParent* powp;
             if (!JSObject_to_PObjectWrapperParent(cx, JSVAL_TO_OBJECT(from), &powp))
                 return with_error(cx, false, "Cannot pass parent-created object to child");
             *to = powp;
         }
         return true;
     case JSTYPE_STRING:
-        *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(from)),
-                                JS_GetStringLength(JSVAL_TO_STRING(from)));
+        {
+            nsDependentJSString depStr;
+            if (!depStr.init(cx, from))
+                return false;
+            *to = depStr;
+        }
         return true;
     case JSTYPE_NUMBER:
         if (JSVAL_IS_INT(from))
             *to = JSVAL_TO_INT(from);
         else if (JSVAL_IS_DOUBLE(from))
             *to = JSVAL_TO_DOUBLE(from);
         else return false;
         return true;
@@ -374,20 +379,22 @@ jsid_from_nsString(JSContext* cx, const 
         return false;
     return JS_ValueToId(cx, STRING_TO_JSVAL(str), to);
 }
 
 static bool
 jsval_to_nsString(JSContext* cx, jsid from, nsString* to)
 {
     JSString* str;
+    const jschar* chars;
     jsval idval;
     if (JS_IdToValue(cx, from, &idval) &&
-        (str = JS_ValueToString(cx, idval))) {
-        *to = JS_GetStringChars(str);
+        (str = JS_ValueToString(cx, idval)) &&
+        (chars = JS_GetStringCharsZ(cx, str))) {
+        *to = chars;
         return true;
     }
     return false;
 }
 
 /*static*/ JSBool
 ObjectWrapperParent::CPOW_AddProperty(JSContext *cx, JSObject *obj, jsid id,
                                       jsval *vp)
--- a/js/jetpack/JetpackActorCommon.cpp
+++ b/js/jetpack/JetpackActorCommon.cpp
@@ -44,16 +44,18 @@
 #include "jshashtable.h"
 
 #include "mozilla/jetpack/JetpackActorCommon.h"
 #include "mozilla/jetpack/PJetpack.h"
 #include "mozilla/jetpack/PHandleParent.h"
 #include "mozilla/jetpack/PHandleChild.h"
 #include "mozilla/jetpack/Handle.h"
 
+#include "nsJSUtils.h"
+
 using mozilla::jetpack::JetpackActorCommon;
 using mozilla::jetpack::PHandleParent;
 using mozilla::jetpack::HandleParent;
 using mozilla::jetpack::PHandleChild;
 using mozilla::jetpack::HandleChild;
 using mozilla::jetpack::KeyValue;
 using mozilla::jetpack::PrimVariant;
 using mozilla::jetpack::CompVariant;
@@ -133,18 +135,22 @@ JetpackActorCommon::jsval_to_PrimVariant
     if (hc) {
       *to = hc;
       return true;
     }
     return false;
   }
 
   case JSTYPE_STRING:
-    *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(from)),
-                            JS_GetStringLength(JSVAL_TO_STRING(from)));
+    {
+        nsDependentJSString depStr;
+        if (!depStr.init(cx, from))
+            return false;
+        *to = depStr;
+    }
     return true;
 
   case JSTYPE_NUMBER:
     if (JSVAL_IS_INT(from))
       *to = JSVAL_TO_INT(from);
     else if (JSVAL_IS_DOUBLE(from))
       *to = JSVAL_TO_DOUBLE(from);
     else
@@ -218,18 +224,20 @@ JetpackActorCommon::jsval_to_CompVariant
     JSString* idStr = JS_ValueToString(cx, val);
     if (!idStr)
       return false;
     if (!JS_GetPropertyById(cx, obj, ida[i], &val))
       return false;
     KeyValue kv;
     // Silently drop properties that can't be converted.
     if (jsval_to_Variant(cx, val, &kv.value(), seen)) {
-      kv.key() = nsDependentString((PRUnichar*)JS_GetStringChars(idStr),
-                                   JS_GetStringLength(idStr));
+      nsDependentJSString depStr;
+      if (!depStr.init(cx, idStr))
+          return false;
+      kv.key() = depStr;
       // If AppendElement fails, we lose this property, no big deal.
       kvs.AppendElement(kv);
     }
   }
   InfallibleTArray<KeyValue> outKvs;
   outKvs.SwapElements(kvs);
   *to = outKvs;
 
--- a/js/jetpack/JetpackChild.cpp
+++ b/js/jetpack/JetpackChild.cpp
@@ -91,17 +91,17 @@ JetpackChild::sGlobalClass = {
   JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 #ifdef BUILD_CTYPES
 static char*
 UnicodeToNative(JSContext *cx, const jschar *source, size_t slen)
 {
   nsCAutoString native;
-  nsDependentString unicode(reinterpret_cast<const PRUnichar*>(source), slen);
+  nsDependentString unicode(source, slen);
   nsresult rv = NS_CopyUnicodeToNative(unicode, native);
   if (NS_FAILED(rv)) {
     JS_ReportError(cx, "could not convert string to native charset");
     return NULL;
   }
 
   char* result = static_cast<char*>(JS_malloc(cx, native.Length() + 1));
   if (!result)
@@ -246,18 +246,22 @@ MessageCommon(JSContext* cx, uintN argc,
   jsval* argv = JS_ARGV(cx, vp);
 
   JSString* msgNameStr = JS_ValueToString(cx, argv[0]);
   if (!msgNameStr) {
     JS_ReportError(cx, "Could not convert value to string");
     return JS_FALSE;
   }
 
-  result->msgName.Assign((PRUnichar*)JS_GetStringChars(msgNameStr),
-                         JS_GetStringLength(msgNameStr));
+  size_t length;
+  const jschar* chars = JS_GetStringCharsAndLength(cx, msgNameStr, &length);
+  if (!chars)
+      return JS_FALSE;
+
+  result->msgName.Assign(chars, length);
 
   result->data.Clear();
 
   if (!result->data.SetCapacity(argc)) {
     JS_ReportOutOfMemory(cx);
     return JS_FALSE;
   }
 
@@ -350,18 +354,22 @@ ReceiverCommon(JSContext* cx, uintN argc
 
   JSString* str = JS_ValueToString(cx, argv[0]);
   if (!str) {
     JS_ReportError(cx, "%s expects a stringifiable value as its first argument",
                    methodName);
     return JS_FALSE;
   }
 
-  result->msgName.Assign((PRUnichar*)JS_GetStringChars(str),
-                         JS_GetStringLength(str));
+  size_t length;
+  const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
+  if (!chars)
+      return JS_FALSE;
+
+  result->msgName.Assign(chars, length);
 
   if (arity < 2)
     return JS_TRUE;
 
   if (JS_TypeOfValue(cx, argv[1]) != JSTYPE_FUNCTION) {
     JS_ReportError(cx, "%s expects a function as its second argument",
                    methodName);
     return JS_FALSE;
@@ -492,19 +500,23 @@ JetpackChild::EvalInSandbox(JSContext* c
 
   if (&sGlobalClass != JS_GetClass(cx, obj) ||
       obj == JS_GetGlobalObject(cx)) {
     JS_ReportError(cx, "The first argument to evalInSandbox must be a global object created using createSandbox.");
     JS_ASSERT(JS_FALSE);
     return JS_FALSE;
   }
 
+  size_t length;
+  const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
+  if (!chars)
+      return JS_FALSE;
+
   js::AutoValueRooter ignored(cx);
-  return JS_EvaluateUCScript(cx, obj, JS_GetStringChars(str), JS_GetStringLength(str), "", 1,
-                             ignored.jsval_addr());
+  return JS_EvaluateUCScript(cx, obj, chars, length, "", 1, ignored.jsval_addr());
 }
 
 bool JetpackChild::sReportingError;
 
 /* static */ void
 JetpackChild::ReportError(JSContext* cx, const char* message,
                           JSErrorReport* report)
 {
--- a/js/jetpack/Makefile.in
+++ b/js/jetpack/Makefile.in
@@ -69,15 +69,20 @@ CPPSRCS = \
   JetpackParent.cpp \
   JetpackChild.cpp \
   JetpackProcessChild.cpp \
   JetpackProcessParent.cpp \
   JetpackService.cpp \
   JetpackActorCommon.cpp \
   $(NULL)
 
+# For nsDependentJSString
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/dom/base \
+  $(NULL)
+
 ifdef ENABLE_TESTS
 TOOL_DIRS += tests
 endif
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
--- a/js/jsd/idl/jsdIDebuggerService.idl
+++ b/js/jsd/idl/jsdIDebuggerService.idl
@@ -73,17 +73,17 @@ interface jsdIValue;
 interface jsdIObject;
 interface jsdIProperty;
 interface jsdIActivationCallback;
 
 /**
  * Debugger service.  It's not a good idea to have more than one active client of
  * the debugger service.
  */
-[scriptable, uuid(01769775-c77c-47f9-8848-0abbab404215)]
+[scriptable, uuid(1ad86ef3-5eca-4ed7-81c5-a757d1957dff)]
 interface jsdIDebuggerService : nsISupports
 {
     /** Internal use only. */
     [noscript] readonly attribute JSDContext        JSDContext;
 
     /**
      * Called when an error or warning occurs.
      */
@@ -507,43 +507,43 @@ interface jsdIFilterEnumerator : nsISupp
      * debugger knows about.
      */
     void enumerateFilter (in jsdIFilter filter);
 };
 
 /**
  * Pass an instance of one of these to jsdIDebuggerService::enumerateScripts.
  */
-[scriptable, uuid(5ba76b99-acb1-4ed8-a4e4-a716a7d9097e)]
+[scriptable, uuid(4eef60c2-9bbc-48fa-b196-646a832c6c81)]
 interface jsdIScriptEnumerator : nsISupports
 {
     /**
      * The enumerateScript method will be called once for every script the
      * debugger knows about.
      */
     void enumerateScript (in jsdIScript script);
 };
 
 /**
  * Pass an instance of one of these to jsdIDebuggerService::enumerateContexts.
  */
-[scriptable, uuid(d96af02e-3379-4db5-885d-fee28d178701)]
+[scriptable, uuid(57d18286-550c-4ca9-ac33-56f12ebba91e)]
 interface jsdIContextEnumerator : nsISupports
 {
     /**
      * The enumerateContext method will be called once for every context
      * currently in use.
      */
     void enumerateContext (in jsdIContext executionContext);
 };
 
 /**
  * Set jsdIDebuggerService::scriptHook to an instance of one of these.
  */
-[scriptable, uuid(cf7ecc3f-361b-44af-84a7-4b0d6cdca204)]
+[scriptable, uuid(bb722893-0f63-45c5-b547-7a0947c7b6b6)]
 interface jsdIScriptHook : nsISupports
 {
     /**
      * Called when scripts are created.
      */
     void onScriptCreated (in jsdIScript script);
     /**
      * Called when the JavaScript engine destroys a script.  The jsdIScript
@@ -551,17 +551,17 @@ interface jsdIScriptHook : nsISupports
      */
     void onScriptDestroyed (in jsdIScript script);
 };
 
 /**
  * Hook instances of this interface up to the
  * jsdIDebuggerService::functionHook and toplevelHook properties.
  */
-[scriptable, uuid(191d2738-22e8-4756-b366-6c878c87d73b)]
+[scriptable, uuid(3eff1314-7ae3-4cf8-833b-c33c24a55633)]
 interface jsdICallHook : nsISupports
 {
     /**
      * TYPE_* values must be kept in sync with the JSD_HOOK_* #defines
      * in jsdebug.h.
      */
 
     /**
@@ -583,17 +583,17 @@ interface jsdICallHook : nsISupports
     
     /**
      * Called before the JavaScript engine executes a top level script or calls
      * a function.
      */
     void onCall (in jsdIStackFrame frame, in unsigned long type);
 };
 
-[scriptable, uuid(cea9ab1a-4b5d-416f-a197-9ffa7046f2ce)]
+[scriptable, uuid(e6b45eee-d974-4d85-9d9e-f5a67218deb4)]
 interface jsdIErrorHook : nsISupports
 {
     /**
      * REPORT_* values must be kept in sync with JSREPORT_* #defines in
      * jsapi.h
      */
     
     /**
@@ -875,17 +875,17 @@ interface jsdIStackFrame : jsdIEphemeral
                   in unsigned long line, out jsdIValue result);
     
 };
 
 /**
  * Script object.  In JavaScript engine terms, there's a single script for each
  * function, and one for the top level script.
  */
-[scriptable, uuid(7e6fb9ed-4382-421d-9a14-c80a486e983b)]
+[scriptable, uuid(e7935220-7def-4c8e-832f-fbc948a97490)]
 interface jsdIScript : jsdIEphemeral
 {
     /** Internal use only. */
     [noscript] readonly attribute JSDContext JSDContext;
     /** Internal use only. */
     [noscript] readonly attribute JSDScript  JSDScript;
     
     /**
@@ -1033,25 +1033,29 @@ interface jsdIScript : jsdIEphemeral
     /**
      * Clear a breakpoint at a PC in this script.
      */
     void clearBreakpoint (in unsigned long pc);
     /**
      * Clear all breakpoints set in this script.
      */
     void clearAllBreakpoints ();
+    /**
+     * Call interrupt hook at least once per source line
+     */
+    void enableSingleStepInterrupts (in PRBool mode);
 };
 
 /**
  * Value objects.  Represents typeless JavaScript values (jsval in SpiderMonkey
  * terminology.)  These are valid until the debugger is turned off.  Holding a
  * jsdIValue adds a root for the underlying JavaScript value, so don't keep it
  * if you don't need to.
  */
-[scriptable, uuid(9cab158f-dc78-41dd-9d11-79e05cb3f2bd)]
+[scriptable, uuid(fd1311f7-096c-44a3-847b-9d478c8176c3)]
 interface jsdIValue : jsdIEphemeral
 {
     /** Internal use only. */
     [noscript] readonly attribute JSDContext JSDContext;
     /** Internal use only. */
     [noscript] readonly attribute JSDValue   JSDValue;
 
     /**
@@ -1187,17 +1191,17 @@ interface jsdIValue : jsdIEphemeral
  * Properties specific to values which are also objects.
  * XXX We don't add roots for these yet, so make sure you hold on to the
  * jsdIValue from whence your jsdIObject instance came for at least as long as
  * you hold the jsdIObject.
  * XXX Maybe the jsClassName, jsConstructorName, and property related attribute/
  * functions from jsdIValue should move to this interface.  We could inherit from
  * jsdIValue or use interface flattening or something.
  */
-[scriptable, uuid(a735a94c-9d41-4997-8fcb-cfa8b649a5b7)]
+[scriptable, uuid(87d86308-7a27-4255-b23c-ce2394f02473)]
 interface jsdIObject : nsISupports
 {
     /** Internal use only. */
     [noscript] readonly attribute JSDContext JSDContext;
     /** Internal use only. */
     [noscript] readonly attribute JSDObject  JSDObject;
 
     /**
@@ -1223,17 +1227,17 @@ interface jsdIObject : nsISupports
      */
     readonly attribute jsdIValue     value;
 };
 
 /**
  * Representation of a property of an object.  When an instance is invalid, all
  * method and property access will result in a NS_UNAVAILABLE error.
  */
-[scriptable, uuid(4491ecd4-fb6b-43fb-bd6f-5d1473f1df24)]
+[scriptable, uuid(09332485-1419-42bc-ba1f-070815ed4b82)]
 interface jsdIProperty : jsdIEphemeral
 {
     /** Internal use only. */
     [noscript] readonly attribute JSDContext  JSDContext;
     /** Internal use only. */
     [noscript] readonly attribute JSDProperty JSDProperty;
 
     /**
--- a/js/jsd/jsd_scpt.c
+++ b/js/jsd/jsd_scpt.c
@@ -209,17 +209,18 @@ static void
     extent = jsd_GetScriptLineExtent(jsdc, jsdscript);
     n = size_t(snprintf(Buf, sizeof(Buf), "%sscript=%08X, %s, ",
                         leadingtext, (unsigned) jsdscript->script,
                         name ? name : "no URL"));
     if (n + 1 < sizeof(Buf)) {
         if (fun) {
             n += size_t(snprintf(Buf + n, sizeof(Buf) - n, "%s", "no fun"));
         } else {
-            n += JS_PutEscapedString(Buf + n, sizeof(Buf) - n, fun, 0);
+            n += JS_PutEscapedFlatString(Buf + n, sizeof(Buf) - n,
+                                         JS_ASSERT_STRING_IS_FLAT(fun), 0);
             Buf[sizeof(Buf) - 1] = '\0';
         }
         if (n + 1 < sizeof(Buf))
             snprintf(Buf + n, sizeof(Buf) - n, ", %d-%d\n", base, base + extent - 1);
     }
     OutputDebugString( Buf );
 }
 
@@ -582,16 +583,27 @@ jsd_GetScriptHook(JSDContext* jsdc, JSD_
     if( hook )
         *hook = jsdc->scriptHook;
     if( callerdata )
         *callerdata = jsdc->scriptHookData;
     JSD_UNLOCK();
     return JS_TRUE;
 }    
 
+JSBool
+jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable)
+{
+    JSBool rv;
+    JSD_LOCK();
+    rv = JS_SetSingleStepMode(jsdc->dumbContext, jsdscript->script, enable);
+    JSD_UNLOCK();
+    return rv;
+}
+
+
 /***************************************************************************/
 
 void
 jsd_NewScriptHookProc( 
                 JSContext   *cx,
                 const char  *filename,      /* URL this script loads from */
                 uintN       lineno,         /* line where this script starts */
                 JSScript    *script,
@@ -746,17 +758,17 @@ jsd_TrapHandler(JSContext *cx, JSScript 
     if( NULL == (jsdc = jsd_JSDContextForJSContext(cx)) ||
         ! _isActiveHook(jsdc, script, jsdhook) )
     {
         JSD_UNLOCK();
         return JSTRAP_CONTINUE;
     }
 
     JSD_ASSERT_VALID_EXEC_HOOK(jsdhook);
-    JS_ASSERT(jsdhook->pc == (jsuword)pc);
+    JS_ASSERT(!jsdhook->pc || jsdhook->pc == (jsuword)pc);
     JS_ASSERT(jsdhook->jsdscript->script == script);
     JS_ASSERT(jsdhook->jsdscript->jsdc == jsdc);
 
     hook = jsdhook->hook;
     hookData = jsdhook->callerdata;
     jsdscript = jsdhook->jsdscript;
 
     /* do not use jsdhook-> after this point */
--- a/js/jsd/jsd_val.c
+++ b/js/jsd/jsd_val.c
@@ -557,27 +557,30 @@ jsd_GetValueProperty(JSDContext* jsdc, J
 
     if(!jsd_IsValueObject(jsdc, jsdval))
         return NULL;
 
     /* If we already have the prop, then return it */
     while(NULL != (jsdprop = jsd_IterateProperties(jsdc, jsdval, &iter)))
     {
         JSString* propName = jsd_GetValueString(jsdc, jsdprop->name);
-        if(propName && !JS_CompareStrings(propName, name))
-            return jsdprop;
+        if(propName) {
+            intN result;
+            if (JS_CompareStrings(cx, propName, name, &result) && !result)
+                return jsdprop;
+        }
         JSD_DropProperty(jsdc, jsdprop);
     }
     /* Not found in property list, look it up explicitly */
 
     if(!(obj = JSVAL_TO_OBJECT(jsdval->val)))
         return NULL;
 
-    nameChars = JS_GetStringChars(name);
-    nameLen   = JS_GetStringLength(name);
+    if (!(nameChars = JS_GetStringCharsZAndLength(cx, name, &nameLen)))
+        return NULL;
 
     JS_BeginRequest(cx);
     call = JS_EnterCrossCompartmentCall(cx, obj);
     if(!call) {
         JS_EndRequest(cx);
 
         return NULL;
     }
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -1028,46 +1028,71 @@ jsdScript::~jsdScript ()
 PCMapEntry *
 jsdScript::CreatePPLineMap()
 {    
     JSContext  *cx  = JSD_GetDefaultJSContext (mCx);
     JSAutoRequest ar(cx);
     JSObject   *obj = JS_NewObject(cx, NULL, NULL, NULL);
     JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
     JSScript   *script;
+    JSString   *jsstr;
     PRUint32    baseLine;
     PRBool      scriptOwner = PR_FALSE;
     
     if (fun) {
-        uintN nargs = JS_GetFunctionArgumentCount(cx, fun);
-        if (nargs > 12)
-            return nsnull;
-        JSString *jsstr = JS_DecompileFunctionBody (cx, fun, 4);
-        if (!jsstr)
+        uintN nargs;
+
+        /* Enter a new block so we can leave before the end of this block */
+        do {
+            JSAutoEnterCompartment ac;
+            if (!ac.enter(cx, JS_GetFunctionObject(fun)))
+                return nsnull;
+
+            nargs = JS_GetFunctionArgumentCount(cx, fun);
+            if (nargs > 12)
+                return nsnull;
+            jsstr = JS_DecompileFunctionBody (cx, fun, 4);
+            if (!jsstr)
+                return nsnull;
+        } while(false);
+
+        size_t length;
+        const jschar *chars = JS_GetStringCharsAndLength(cx, jsstr, &length);
+        if (!chars)
             return nsnull;
     
         const char *argnames[] = {"arg1", "arg2", "arg3", "arg4", 
                                   "arg5", "arg6", "arg7", "arg8",
                                   "arg9", "arg10", "arg11", "arg12" };
-        fun = JS_CompileUCFunction (cx, obj, "ppfun", nargs, argnames,
-                                    JS_GetStringChars(jsstr),
-                                    JS_GetStringLength(jsstr),
-                                    "x-jsd:ppbuffer?type=function", 3);
+        fun = JS_CompileUCFunction (cx, obj, "ppfun", nargs, argnames, chars,
+                                    length, "x-jsd:ppbuffer?type=function", 3);
         if (!fun || !(script = JS_GetFunctionScript(cx, fun)))
             return nsnull;
         baseLine = 3;
     } else {
-        JSString *jsstr = JS_DecompileScript (cx, JSD_GetJSScript(mCx, mScript),
-                                              "ppscript", 4);
-        if (!jsstr)
+        /* Enter a new block so we can leave before the end of this block */
+        do {
+            script = JSD_GetJSScript(mCx, mScript);
+
+            JSAutoEnterCompartment ac;
+            if (!ac.enter(cx, script))
+                return nsnull;
+
+            jsstr = JS_DecompileScript (cx, JSD_GetJSScript(mCx, mScript),
+                                        "ppscript", 4);
+            if (!jsstr)
+                return nsnull;
+        } while(false);
+
+        size_t length;
+        const jschar *chars = JS_GetStringCharsAndLength(cx, jsstr, &length);
+        if (!chars)
             return nsnull;
 
-        script = JS_CompileUCScript (cx, obj,
-                                     JS_GetStringChars(jsstr),
-                                     JS_GetStringLength(jsstr),
+        script = JS_CompileUCScript (cx, obj, chars, length,
                                      "x-jsd:ppbuffer?type=script", 1);
         if (!script)
             return nsnull;
         scriptOwner = PR_TRUE;
         baseLine = 1;
     }
 
     PRUint32 scriptExtent = JS_GetScriptLineExtent (cx, script);
@@ -1279,18 +1304,17 @@ jsdScript::GetParameterNames(PRUint32* c
 
     nsresult rv = NS_OK;
     for (uintN i = 0; i < nargs; ++i) {
         JSAtom *atom = JS_LocalNameToAtom(names[i]);
         if (!atom) {
             ret[i] = 0;
         } else {
             JSString *str = JS_AtomKey(atom);
-            ret[i] = NS_strndup(reinterpret_cast<PRUnichar*>(JS_GetStringChars(str)),
-                                JS_GetStringLength(str));
+            ret[i] = NS_strndup(JS_GetInternedStringChars(str), JS_GetStringLength(str));
             if (!ret[i]) {
                 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret);
                 rv = NS_ERROR_OUT_OF_MEMORY;
                 break;
             }
         }
     }
     JS_ReleaseFunctionLocalNameArray(cx, mark);
@@ -1338,33 +1362,36 @@ jsdScript::GetFunctionSource(nsAString &
         NS_WARNING("No default context !?");
         return NS_ERROR_FAILURE;
     }
     JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
 
     JSAutoRequest ar(cx);
 
     JSString *jsstr;
+    JSAutoEnterCompartment ac;
     if (fun) {
-        JSAutoEnterCompartment ac;
         if (!ac.enter(cx, JS_GetFunctionObject(fun)))
             return NS_ERROR_FAILURE;
         jsstr = JS_DecompileFunction (cx, fun, 4);
     } else {
         JSScript *script = JSD_GetJSScript (mCx, mScript);
-        js::SwitchToCompartment sc(cx, script->compartment);
+        if (!ac.enter(cx, script))
+            return NS_ERROR_FAILURE;
         jsstr = JS_DecompileScript (cx, script, "ppscript", 4);
     }
     if (!jsstr)
         return NS_ERROR_FAILURE;
 
-    aFunctionSource =
-        nsDependentString(
-            reinterpret_cast<PRUnichar*>(JS_GetStringChars(jsstr)),
-            JS_GetStringLength(jsstr));
+    size_t length;
+    const jschar *chars = JS_GetStringCharsZAndLength(cx, jsstr, &length);
+    if (!chars)
+        return NS_ERROR_FAILURE;
+
+    aFunctionSource = nsDependentString(chars, length);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdScript::GetBaseLineNumber(PRUint32 *_rval)
 {
     *_rval = mBaseLineNumber;
     return NS_OK;
@@ -1476,16 +1503,30 @@ jsdScript::LineToPc(PRUint32 aLine, PRUi
     } else {
         return NS_ERROR_INVALID_ARG;
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
+jsdScript::EnableSingleStepInterrupts(PRBool enable)
+{
+    ASSERT_VALID_EPHEMERAL;
+
+    /* Must have set interrupt hook before enabling */
+    if (enable && !jsdService::GetService()->CheckInterruptHook())
+        return NS_ERROR_NOT_INITIALIZED;
+
+    JSD_EnableSingleStepInterrupts(mCx, mScript, enable);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval)
 {
     ASSERT_VALID_EPHEMERAL;
     if (aPcmap == PCMAP_SOURCETEXT) {    
         jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine);
         *_rval = (aLine == JSD_GetClosestLine (mCx, mScript, pc));
     } else if (aPcmap == PCMAP_PRETTYPRINT) {
         if (!mPPLineMap && !CreatePPLineMap())
@@ -2235,22 +2276,29 @@ jsdValue::GetObjectValue(jsdIObject **_r
         return NS_ERROR_FAILURE;
     return NS_OK;
 }
     
 NS_IMETHODIMP
 jsdValue::GetStringValue(nsACString &_rval)
 {
     ASSERT_VALID_EPHEMERAL;
+    JSContext *cx = JSD_GetDefaultJSContext (mCx);
+    if (!cx) {
+        NS_WARNING("No default context !?");
+        return NS_ERROR_FAILURE;
+    }
     JSString *jstr_val = JSD_GetValueString(mCx, mValue);
     if (jstr_val) {
-        nsDependentString chars(
-            reinterpret_cast<PRUnichar*>(JS_GetStringChars(jstr_val)),
-            JS_GetStringLength(jstr_val));
-        CopyUTF16toUTF8(chars, _rval);
+        size_t length;
+        const jschar *chars = JS_GetStringCharsZAndLength(cx, jstr_val, &length);
+        if (!chars)
+            return NS_ERROR_FAILURE;
+        nsDependentString depStr(chars, length);
+        CopyUTF16toUTF8(depStr, _rval);
     } else {
         _rval.Truncate();
     }
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdValue::GetPropertyCount (PRInt32 *_rval)
--- a/js/jsd/jsd_xpc.h
+++ b/js/jsd/jsd_xpc.h
@@ -283,16 +283,18 @@ class jsdService : public jsdIDebuggerSe
                    mInterruptHook(0), mScriptHook(0), mThrowHook(0),
                    mTopLevelHook(0), mFunctionHook(0)
     {
     }
 
     virtual ~jsdService();
     
     static jsdService *GetService ();
+
+    PRBool CheckInterruptHook() { return !!mInterruptHook; }
     
   private:
     PRBool      mOn;
     PRUint32    mPauseLevel;
     PRUint32    mNestedLoopLevel;
     JSDContext *mCx;
     JSRuntime  *mRuntime;
 
--- a/js/jsd/jsdebug.c
+++ b/js/jsd/jsdebug.c
@@ -572,16 +572,24 @@ JSD_SetInterruptHook(JSDContext*        
                      JSD_ExecutionHookProc hook,
                      void*                 callerdata)
 {
     JSD_ASSERT_VALID_CONTEXT(jsdc);
     return jsd_SetInterruptHook(jsdc, hook, callerdata);
 }
 
 JSD_PUBLIC_API(JSBool)
+JSD_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable)
+{
+    JSD_ASSERT_VALID_CONTEXT(jsdc);
+    JSD_ASSERT_VALID_SCRIPT(jsdscript);
+    return jsd_EnableSingleStepInterrupts(jsdc, jsdscript, enable);
+}
+
+JSD_PUBLIC_API(JSBool)
 JSD_ClearInterruptHook(JSDContext* jsdc)
 {
     JSD_ASSERT_VALID_CONTEXT(jsdc);
     return jsd_ClearInterruptHook(jsdc);
 }
 
 JSD_PUBLIC_API(JSBool)
 JSD_SetDebugBreakHook(JSDContext*           jsdc,
--- a/js/jsd/jsdebug.h
+++ b/js/jsd/jsdebug.h
@@ -799,16 +799,22 @@ JSD_ClearAllExecutionHooks(JSDContext* j
 * executes until cleared.
 */
 extern JSD_PUBLIC_API(JSBool)
 JSD_SetInterruptHook(JSDContext*           jsdc,
                      JSD_ExecutionHookProc hook,
                      void*                 callerdata);
 
 /*
+* Call the interrupt hook at least once per source line
+*/
+extern JSD_PUBLIC_API(JSBool)
+JSD_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript *jsdscript, JSBool enable);
+
+/*
 * Clear the current interrupt hook.
 */
 extern JSD_PUBLIC_API(JSBool)
 JSD_ClearInterruptHook(JSDContext* jsdc);
 
 /*
 * Set the hook that should be called whenever a JSD_ErrorReporter hook
 * returns JSD_ERROR_REPORTER_DEBUG.
--- 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/assembler/jit/ExecutableAllocator.h
+++ b/js/src/assembler/jit/ExecutableAllocator.h
@@ -165,16 +165,23 @@ public:
     {
         Allocation* end = m_pools.end();
         for (Allocation* ptr = m_pools.begin(); ptr != end; ++ptr)
             ExecutablePool::systemRelease(*ptr);
     }
 
     size_t available() const { return (m_pools.length() > 1) ? 0 : m_end - m_freePtr; }
 
+    // Flag for downstream use, whether to try to release references to this pool.
+    bool m_destroy;
+
+    // GC number in which the m_destroy flag was most recently set. Used downstream to
+    // remember whether m_destroy was computed for the currently active GC.
+    size_t m_gcNumber;
+
 private:
     // On OOM, this will return an Allocation where pages is NULL.
     static Allocation systemAlloc(size_t n);
     static void systemRelease(const Allocation& alloc);
 
     ExecutablePool(size_t n);
 
     void* poolAllocate(size_t n);
@@ -388,17 +395,17 @@ private:
     static const size_t maxSmallPools = 4;
     typedef js::Vector<ExecutablePool *, maxSmallPools, js::SystemAllocPolicy > SmallExecPoolVector;
     SmallExecPoolVector m_smallAllocationPools;
     static void intializePageSize();
 };
 
 // This constructor can fail due to OOM. If it does, m_freePtr will be
 // set to NULL. 
-inline ExecutablePool::ExecutablePool(size_t n) : m_refCount(1)
+inline ExecutablePool::ExecutablePool(size_t n) : m_refCount(1), m_destroy(false), m_gcNumber(0)
 {
     size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
     if (allocSize == OVERSIZE_ALLOCATION) {
         m_freePtr = NULL;
         return;
     }
 #ifdef DEBUG_STRESS_JSC_ALLOCATOR
     Allocation mem = systemAlloc(size_t(4294967291));
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -985,17 +985,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)
@@ -1899,23 +1899,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
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -1287,17 +1287,20 @@ jsvalToFloat(JSContext *cx, jsval val, F
 }
 
 template<class IntegerType>
 static bool
 StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
 {
   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
 
-  const jschar* cp = string->chars();
+  const jschar* cp = string->getChars(NULL);
+  if (!cp)
+    return false;
+
   const jschar* end = cp + string->length();
   if (cp == end)
     return false;
 
   IntegerType sign = 1;
   if (cp[0] == '-') {
     if (!numeric_limits<IntegerType>::is_signed)
       return false;
@@ -1775,19 +1778,20 @@ ImplicitConvert(JSContext* cx,
   case TYPE_##name: {                                                          \
     /* Convert from a 1-character string, regardless of encoding, */           \
     /* or from an integer, provided the result fits in 'type'. */              \
     type result;                                                               \
     if (JSVAL_IS_STRING(val)) {                                                \
       JSString* str = JSVAL_TO_STRING(val);                                    \
       if (str->length() != 1)                                                  \
         return TypeError(cx, #name, val);                                      \
-                                                                               \
-      result = str->chars()[0];                                                \
-                                                                               \
+      const jschar *chars = str->getChars(cx);                                 \
+      if (!chars)                                                              \
+        return false;                                                          \
+      result = chars[0];                                                       \
     } else if (!jsvalToInteger(cx, val, &result)) {                            \
       return TypeError(cx, #name, val);                                        \
     }                                                                          \
     *static_cast<type*>(buffer) = result;                                      \
     break;                                                                     \
   }
 #include "typedefs.h"
   case TYPE_pointer: {
@@ -1819,18 +1823,20 @@ ImplicitConvert(JSContext* cx,
         }
       }
 
     } else if (isArgument && JSVAL_IS_STRING(val)) {
       // Convert the string for the ffi call. This requires allocating space
       // which the caller assumes ownership of.
       // TODO: Extend this so we can safely convert strings at other times also.
       JSString* sourceString = JSVAL_TO_STRING(val);
-      const jschar* sourceChars = sourceString->chars();
       size_t sourceLength = sourceString->length();
+      const jschar* sourceChars = sourceString->getChars(cx);
+      if (!sourceChars)
+        return false;
 
       switch (CType::GetTypeCode(cx, baseType)) {
       case TYPE_char:
       case TYPE_signed_char:
       case TYPE_unsigned_char: {
         // Convert from UTF-16 to UTF-8.
         size_t nbytes =
           js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
@@ -1874,18 +1880,20 @@ ImplicitConvert(JSContext* cx,
     return TypeError(cx, "pointer", val);
   }
   case TYPE_array: {
     JSObject* baseType = ArrayType::GetBaseType(cx, targetType);
     size_t targetLength = ArrayType::GetLength(cx, targetType);
 
     if (JSVAL_IS_STRING(val)) {
       JSString* sourceString = JSVAL_TO_STRING(val);
-      const jschar* sourceChars = sourceString->chars();
       size_t sourceLength = sourceString->length();
+      const jschar* sourceChars = sourceString->getChars(cx);
+      if (!sourceChars)
+        return false;
 
       switch (CType::GetTypeCode(cx, baseType)) {
       case TYPE_char:
       case TYPE_signed_char:
       case TYPE_unsigned_char: {
         // Convert from UTF-16 to UTF-8.
         size_t nbytes =
           js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
@@ -1984,31 +1992,28 @@ ImplicitConvert(JSContext* cx,
       jsid id;
       size_t i = 0;
       while (1) {
         if (!JS_NextProperty(cx, iter, &id))
           return false;
         if (JSID_IS_VOID(id))
           break;
 
-        js::AutoValueRooter fieldVal(cx);
-        JS_IdToValue(cx, id, fieldVal.jsval_addr());
-        if (!JSVAL_IS_STRING(fieldVal.jsval_value())) {
+        if (!JSID_IS_STRING(id)) {
           JS_ReportError(cx, "property name is not a string");
           return false;
         }
 
-        const FieldInfo* field = StructType::LookupField(cx, targetType,
-                                                         JSVAL_TO_STRING(fieldVal.jsval_value()));
+        JSFlatString *name = JSID_TO_FLAT_STRING(id);
+        const FieldInfo* field = StructType::LookupField(cx, targetType, name);
         if (!field)
           return false;
 
-        JSString* name = JSVAL_TO_STRING(fieldVal.jsval_value());
         js::AutoValueRooter prop(cx);
-        if (!JS_GetUCProperty(cx, obj, name->chars(), name->length(), prop.jsval_addr()))
+        if (!JS_GetPropertyById(cx, obj, id, prop.jsval_addr()))
           return false;
 
         // Convert the field via ImplicitConvert().
         char* fieldData = intermediate.get() + field->mOffset;
         if (!ImplicitConvert(cx, prop.jsval_value(), field->mType, fieldData, false, NULL))
           return false;
 
         ++i;
@@ -3562,18 +3567,20 @@ ArrayType::ConstructData(JSContext* cx,
         JS_ReportError(cx, "argument must be an array object or length");
         return JS_FALSE;
       }
 
     } else if (JSVAL_IS_STRING(argv[0])) {
       // We were given a string. Size the array to the appropriate length,
       // including space for the terminator.
       JSString* sourceString = JSVAL_TO_STRING(argv[0]);
-      const jschar* sourceChars = sourceString->chars();
       size_t sourceLength = sourceString->length();
+      const jschar* sourceChars = sourceString->getChars(cx);
+      if (!sourceChars)
+        return false;
 
       switch (CType::GetTypeCode(cx, baseType)) {
       case TYPE_char:
       case TYPE_signed_char:
       case TYPE_unsigned_char: {
         // Determine the UTF-8 length.
         length = js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
         if (length == (size_t) -1)
@@ -3866,56 +3873,54 @@ ArrayType::AddressOfElement(JSContext* c
 }
 
 /*******************************************************************************
 ** StructType implementation
 *******************************************************************************/
 
 // For a struct field descriptor 'val' of the form { name : type }, extract
 // 'name' and 'type'.
-static JSString*
+static JSFlatString*
 ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj)
 {
   if (JSVAL_IS_PRIMITIVE(val)) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
     return NULL;
   }
 
   JSObject* obj = JSVAL_TO_OBJECT(val);
   JSObject* iter = JS_NewPropertyIterator(cx, obj);
   if (!iter)
     return NULL;
   js::AutoObjectRooter iterroot(cx, iter);
 
-  jsid id;
-  if (!JS_NextProperty(cx, iter, &id))
+  jsid nameid;
+  if (!JS_NextProperty(cx, iter, &nameid))
     return NULL;
-  if (JSID_IS_VOID(id)) {
+  if (JSID_IS_VOID(nameid)) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
     return NULL;
   }
 
-  js::AutoValueRooter nameVal(cx);
-  JS_IdToValue(cx, id, nameVal.jsval_addr());
-  if (!JSVAL_IS_STRING(nameVal.jsval_value())) {
+  if (!JSID_IS_STRING(nameid)) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
     return NULL;
   }
-  JSString* name = JSVAL_TO_STRING(nameVal.jsval_value());
 
   // make sure we have one, and only one, property
+  jsid id;
   if (!JS_NextProperty(cx, iter, &id))
     return NULL;
   if (!JSID_IS_VOID(id)) {
     JS_ReportError(cx, "struct field descriptors must contain one property");
     return NULL;
   }
 
   js::AutoValueRooter propVal(cx);
-  if (!JS_GetUCProperty(cx, obj, name->chars(), name->length(), propVal.jsval_addr()))
+  if (!JS_GetPropertyById(cx, obj, nameid, propVal.jsval_addr()))
     return NULL;
 
   if (propVal.value().isPrimitive() ||
       !CType::IsCType(cx, JSVAL_TO_OBJECT(propVal.jsval_value()))) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
     return NULL;
   }
 
@@ -3924,25 +3929,25 @@ ExtractStructField(JSContext* cx, jsval 
   // choke on a zero-size struct, so we disallow them.)
   *typeObj = JSVAL_TO_OBJECT(propVal.jsval_value());
   size_t size;
   if (!CType::GetSafeSize(cx, *typeObj, &size) || size == 0) {
     JS_ReportError(cx, "struct field types must have defined and nonzero size");
     return NULL;
   }
 
-  return name;
+  return JSID_TO_FLAT_STRING(nameid);
 }
 
 // For a struct field with 'name' and 'type', add an element of the form
 // { name : type }.
 static JSBool
 AddFieldToArray(JSContext* cx,
                 jsval* element,
-                JSString* name,
+                JSFlatString* name,
                 JSObject* typeObj)
 {
   JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL);
   if (!fieldObj)
     return false;
 
   *element = OBJECT_TO_JSVAL(fieldObj);
 
@@ -4043,17 +4048,17 @@ StructType::DefineInternal(JSContext* cx
     structAlign = 0;
 
     for (jsuint i = 0; i < len; ++i) {
       js::AutoValueRooter item(cx);
       if (!JS_GetElement(cx, fieldsObj, i, item.jsval_addr()))
         return JS_FALSE;
 
       JSObject* fieldType = NULL;
-      JSString* name = ExtractStructField(cx, item.jsval_value(), &fieldType);
+      JSFlatString* name = ExtractStructField(cx, item.jsval_value(), &fieldType);
       if (!name)
         return JS_FALSE;
       fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType);
 
       // Make sure each field name is unique, and add it to the hash.
       FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
       if (entryPtr) {
         JS_ReportError(cx, "struct fields must have unique names");
@@ -4316,17 +4321,17 @@ StructType::GetFieldInfo(JSContext* cx, 
   jsval slot;
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot));
   JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
 
   return static_cast<const FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
 }
 
 const FieldInfo*
-StructType::LookupField(JSContext* cx, JSObject* obj, JSString *name)
+StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name)
 {
   JS_ASSERT(CType::IsCType(cx, obj));
   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
 
   FieldInfoHash::Ptr ptr = GetFieldInfo(cx, obj)->lookup(name);
   if (ptr)
     return &ptr->value;
 
@@ -4412,17 +4417,17 @@ StructType::FieldGetter(JSContext* cx, J
   }
 
   JSObject* typeObj = CData::GetCType(cx, obj);
   if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
     JS_ReportError(cx, "not a StructType");
     return JS_FALSE;
   }
 
-  const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_STRING(idval));
+  const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
   if (!field)
     return JS_FALSE;
 
   char* data = static_cast<char*>(CData::GetData(cx, obj)) + field->mOffset;
   return ConvertToJS(cx, field->mType, obj, data, false, false, vp);
 }
 
 JSBool
@@ -4434,17 +4439,17 @@ StructType::FieldSetter(JSContext* cx, J
   }
 
   JSObject* typeObj = CData::GetCType(cx, obj);
   if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
     JS_ReportError(cx, "not a StructType");
     return JS_FALSE;
   }
 
-  const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_STRING(idval));
+  const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
   if (!field)
     return JS_FALSE;
 
   char* data = static_cast<char*>(CData::GetData(cx, obj)) + field->mOffset;
   return ImplicitConvert(cx, *vp, field->mType, data, false, NULL);
 }
 
 JSBool
@@ -4462,18 +4467,21 @@ StructType::AddressOfField(JSContext* cx
     return JS_FALSE;
   }
 
   if (argc != 1) {
     JS_ReportError(cx, "addressOfField takes one argument");
     return JS_FALSE;
   }
 
-  const FieldInfo* field = LookupField(cx, typeObj,
-                                       JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]));
+  JSFlatString *str = JS_FlattenString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]));
+  if (!str)
+    return JS_FALSE;
+
+  const FieldInfo* field = LookupField(cx, typeObj, str);
   if (!field)
     return JS_FALSE;
 
   JSObject* baseType = field->mType;
   JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
   if (!pointerType)
     return JS_FALSE;
   js::AutoObjectRooter root(cx, pointerType);
@@ -4606,28 +4614,33 @@ PrepareReturnType(JSContext* cx, jsval t
   }
 
   // libffi cannot pass types of zero size by value.
   JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(cx, result) != 0);
 
   return result;
 }
 
-static JS_ALWAYS_INLINE bool
-IsEllipsis(jsval v)
+static JS_ALWAYS_INLINE JSBool
+IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis)
 {
+  *isEllipsis = false;
   if (!JSVAL_IS_STRING(v))
-    return false;
+    return true;
   JSString* str = JSVAL_TO_STRING(v);
   if (str->length() != 3)
+    return true;
+  const jschar* chars = str->getChars(cx);
+  if (!chars)
     return false;
-  const jschar* chars = str->chars(), dot('.');
-  return (chars[0] == dot &&
-          chars[1] == dot &&
-          chars[2] == dot);
+  jschar dot = '.';
+  *isEllipsis = (chars[0] == dot &&
+                 chars[1] == dot &&
+                 chars[2] == dot);
+  return true;
 }
 
 static JSBool
 PrepareCIF(JSContext* cx,
            FunctionInfo* fninfo)
 {
   ffi_abi abi;
   if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) {
@@ -4732,17 +4745,20 @@ NewFunctionInfo(JSContext* cx,
       !fninfo->mFFITypes.reserve(argLength)) {
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
 
   fninfo->mIsVariadic = false;
 
   for (JSUint32 i = 0; i < argLength; ++i) {
-    if (IsEllipsis(argTypes[i])) {
+    bool isEllipsis;
+    if (!IsEllipsis(cx, argTypes[i], &isEllipsis))
+      return false;
+    if (isEllipsis) {
       fninfo->mIsVariadic = true;
       if (i < 1) {
         JS_ReportError(cx, "\"...\" may not be the first and only parameter "
                        "type of a variadic function declaration");
         return NULL;
       }
       if (i < argLength - 1) {
         JS_ReportError(cx, "\"...\" must be the last parameter type of a "
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -136,31 +136,38 @@ AppendString(Vector<T, N, AP> &v, Vector
   v.append(w.begin(), w.length());
 }
 
 template <size_t N, class AP>
 void
 AppendString(Vector<jschar, N, AP> &v, JSString* str)
 {
   JS_ASSERT(str);
-  v.append(str->chars(), str->length());
+  const jschar *chars = str->getChars(NULL);
+  if (!chars)
+    return;
+  v.append(chars, str->length());
 }
 
 template <size_t N, class AP>
 void
 AppendString(Vector<char, N, AP> &v, JSString* str)
 {
   JS_ASSERT(str);
   size_t vlen = v.length();
   size_t alen = str->length();
   if (!v.resize(vlen + alen))
     return;
 
+  const jschar *chars = str->getChars(NULL);
+  if (!chars)
+    return;
+
   for (size_t i = 0; i < alen; ++i)
-    v[i + vlen] = char(str->chars()[i]);
+    v[i + vlen] = char(chars[i]);
 }
 
 template <class T, size_t N, class AP, size_t ArrayLength>
 void
 PrependString(Vector<T, N, AP> &v, const char (&array)[ArrayLength])
 {
   // Don't include the trailing '\0'.
   size_t alen = ArrayLength - 1;
@@ -181,43 +188,25 @@ void
 PrependString(Vector<jschar, N, AP> &v, JSString* str)
 {
   JS_ASSERT(str);
   size_t vlen = v.length();
   size_t alen = str->length();
   if (!v.resize(vlen + alen))
     return;
 
+  const jschar *chars = str->getChars(NULL);
+  if (!chars)
+    return;
+
   // Move vector data forward. This is safe since we've already resized.
   memmove(v.begin() + alen, v.begin(), vlen * sizeof(jschar));
 
   // Copy data to insert.
-  memcpy(v.begin(), str->chars(), alen * sizeof(jschar));
-}
-
-template <class T, size_t N, size_t M, class AP>
-bool
-StringsEqual(Vector<T, N, AP> &v, Vector<T, M, AP> &w)
-{
-  if (v.length() != w.length())
-    return false;
-
-  return memcmp(v.begin(), w.begin(), v.length() * sizeof(T)) == 0;
-}
-
-template <size_t N, class AP>
-bool
-StringsEqual(Vector<jschar, N, AP> &v, JSString* str)
-{
-  JS_ASSERT(str);
-  size_t length = str->length();
-  if (v.length() != length)
-    return false;
-
-  return memcmp(v.begin(), str->chars(), length * sizeof(jschar)) == 0;
+  memcpy(v.begin(), chars, alen * sizeof(jschar));
 }
 
 /*******************************************************************************
 ** Function and struct API definitions
 *******************************************************************************/
 
 JS_ALWAYS_INLINE void
 ASSERT_OK(JSBool ok)
@@ -269,17 +258,17 @@ struct FieldInfo
   JSObject* mType;    // CType of the field
   size_t    mIndex;   // index of the field in the struct (first is 0)
   size_t    mOffset;  // offset of the field in the struct, in bytes
 };
 
 // Hash policy for FieldInfos.
 struct FieldHashPolicy
 {
-  typedef JSString* Key;
+  typedef JSFlatString* Key;
   typedef Key Lookup;
 
   static uint32 hash(const Lookup &l) {
     const jschar* s = l->chars();
     size_t n = l->length();
     uint32 hash = 0;
     for (; n > 0; s++, n--)
       hash = hash * 33 + *s;
@@ -292,17 +281,17 @@ struct FieldHashPolicy
 
     if (k->length() != l->length())
       return false;
 
     return memcmp(k->chars(), l->chars(), k->length() * sizeof(jschar)) == 0;
   }
 };
 
-typedef HashMap<JSString*, FieldInfo, FieldHashPolicy, SystemAllocPolicy> FieldInfoHash;
+typedef HashMap<JSFlatString*, FieldInfo, FieldHashPolicy, SystemAllocPolicy> FieldInfoHash;
 
 // Descriptor of ABI, return type, argument types, and variadicity for a
 // FunctionType.
 struct FunctionInfo
 {
   // Initialized in NewFunctionInfo when !mIsVariadic, but only later, in
   // FunctionType::Call, when mIsVariadic. Not always consistent with
   // mFFITypes, due to lazy initialization when mIsVariadic.
@@ -477,17 +466,17 @@ namespace ArrayType {
   bool GetSafeLength(JSContext* cx, JSObject* obj, size_t* result);
   ffi_type* BuildFFIType(JSContext* cx, JSObject* obj);
 }
 
 namespace StructType {
   JSBool DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj);
 
   const FieldInfoHash* GetFieldInfo(JSContext* cx, JSObject* obj);
-  const FieldInfo* LookupField(JSContext* cx, JSObject* obj, JSString *name);
+  const FieldInfo* LookupField(JSContext* cx, JSObject* obj, JSFlatString *name);
   JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj);
   ffi_type* BuildFFIType(JSContext* cx, JSObject* obj);
 }
 
 namespace FunctionType {
   JSObject* CreateInternal(JSContext* cx, jsval abi, jsval rtype,
     jsval* argtypes, jsuint arglen);
 
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -128,22 +128,23 @@ Library::Create(JSContext* cx, jsval pat
     return NULL;
 
   if (!JSVAL_IS_STRING(path)) {
     JS_ReportError(cx, "open takes a string argument");
     return NULL;
   }
 
   PRLibSpec libSpec;
-  JSString* pathStr = JSVAL_TO_STRING(path);
+  JSFlatString* pathStr = JS_FlattenString(cx, JSVAL_TO_STRING(path));
+  if (!pathStr)
+    return NULL;
 #ifdef XP_WIN
   // On Windows, converting to native charset may corrupt path string.
   // So, we have to use Unicode path directly.
-  const PRUnichar* pathChars = reinterpret_cast<const PRUnichar*>(
-    JS_GetStringCharsZ(cx, pathStr));
+  const PRUnichar* pathChars = JS_GetFlatStringChars(pathStr);
   if (!pathChars)
     return NULL;
 
   libSpec.value.pathname_u = pathChars;
   libSpec.type = PR_LibSpec_PathnameU;
 #else
   // Convert to platform native charset if the appropriate callback has been
   // provided.
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
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug616762.js
@@ -0,0 +1,26 @@
+// vim: set ts=4 sw=4 tw=99 et:
+document = {
+    ready: function (x) {
+        this.exec = x;
+    }
+};
+
+var $ = function (x) {
+    return document;
+};
+
+(function ($) {
+    eval("(function(){\n" +
+         "  var Private={};\n" +
+         "  $(document).ready(function(){\n" +
+         "      init()\n" +
+         "  });\n" +
+         "  function init(){\n" +
+         "      $(Private)\n" +
+         "  };\n" +
+         "})();");
+})($);
+document.exec();
+
+// Don't crash or assert.
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug618350.js
@@ -0,0 +1,24 @@
+
+var global = 0;
+
+Object.defineProperty(Object.prototype, 0, {set: function() { global++; }});
+
+for (var x = 0; x < 20; ++x)
+  [1,2];
+assertEq(global, 0);
+
+Object.defineProperty(Object.prototype, 1, {set: function() { global++; }});
+
+for (var x = 0; x < 20; ++x)
+  [1,2];
+assertEq(global, 0);
+
+Object.defineProperty(Object.prototype, "b", {set: function() { global++; }});
+
+for (var x = 0; x < 20; ++x) {
+  var s = { a:0, b:1, 0: 2, 1: 3 };
+}
+assertEq(global, 0);
+
+assertEq([42][0], 42);
+assertEq([42].length, 1);
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();
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testBug616119.js
@@ -0,0 +1,6 @@
+// don't assert
+for (a = 0; a < 9; ++a) {
+    M: for (let c in <x>></x>) {
+      break M
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testChangingTypeDuringRecording.js
@@ -0,0 +1,19 @@
+/* Touch/init early so global shape doesn't change in loop */
+var SetOnIter = HOTLOOP - 1;
+var x = 3;
+var i = 0;
+assertEq(true, true);
+
+for (i = 0; i < SetOnIter + 10; ++i) {
+    x = 3;
+    setGlobalPropIf(i == SetOnIter, 'x', 'pretty');
+    assertEq(x == 'pretty', i == SetOnIter);
+    x = 3;
+}
+
+for (i = 0; i < SetOnIter + 10; ++i) {
+    x = 3;
+    defGlobalPropIf(i == SetOnIter, 'x', { value:'pretty' });
+    assertEq(x == 'pretty', i == SetOnIter);
+    x = 3;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-2.js
@@ -0,0 +1,8 @@
+function f() {
+    var a = [], i, N = HOTLOOP + 2;
+    for (i = 0; i < N; i++)
+        a[i] = {m: i, m: function() { return 0; }};
+    assertEq(a[N - 2].m === a[N - 1].m, false);
+}
+f();
+f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-3.js
@@ -0,0 +1,15 @@
+function f() {
+    var a = [], i, N = HOTLOOP + 2;
+    for (i = 0; i < N; i++) {
+        a[i] = {};
+        a[i].m = function() { return 0; };
+        a[i].m = function() { return 1; };
+    }
+    assertEq(a[N - 2].m === a[N - 1].m, false);
+    for (i = 0; i < N; i++) {
+        var f = a[i].m;
+        assertEq(f(), 1);
+    }
+}
+f();
+f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt-4.js
@@ -0,0 +1,12 @@
+function f() {
+    var a = [], i, N = HOTLOOP + 2;
+    for (i = 0; i < N; i++)
+        a[i] = {m: function() { return 0; }, m: function() { return 1; }};
+    assertEq(a[N - 2].m === a[N - 1].m, false);
+    for (i = 0; i < N; i++) {
+        var f = a[i].m;
+        assertEq(f(), 1);
+    }
+}
+f();
+f();
--- a/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt.js
+++ b/js/src/jit-test/tests/basic/testExistingPropToJoinedMethodAttempt.js
@@ -1,17 +1,10 @@
 function f() {
-  var a = [{m: 0}, {m: 1}, {m: 2}, {m: 3}, {m: 4}];
-  var b = [{}, {}, {}, {}, {}];
-  for (var i = 0; i < a.length; i++) {
-    a[i].m = function() { return 0; };
-    b[i].m = function() { return 1; };
-  }
-  assertEq(false, a[0].m === a[1].m);
-  assertEq(false, a[0].m === a[2].m);
-  assertEq(false, a[0].m === a[3].m);
-  assertEq(false, a[0].m === a[4].m);
-  assertEq(false, b[0].m === b[1].m);
-  assertEq(false, b[0].m === b[2].m);
-  assertEq(false, b[0].m === b[3].m);
-  assertEq(false, b[0].m === b[4].m);
+    var a = [], i, N = HOTLOOP + 2;
+    for (i = 0; i < N; i++)
+        a[i] = {m: i};
+    for (i = 0; i < N; i++)
+        a[i].m = function() { return 0; };
+    assertEq(a[N - 2].m === a[N - 1].m, false);
 }
 f();
+f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/pic/to-dictionary.js
@@ -0,0 +1,9 @@
+function f() {
+    var MAX_HEIGHT = 64;
+    var obj = {};
+    for (var i = 0; i < MAX_HEIGHT; i++)
+        obj['a' + i] = i;
+    obj.m = function () { return 0; };
+}
+f();
+f();
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -110,17 +110,17 @@ TypeIdStringImpl(jsid id)
 {
     if (JSID_IS_VOID(id))
         return "(index)";
     if (JSID_IS_EMPTY(id))
         return "(new)";
     static char bufs[4][100];
     static unsigned which = 0;
     which = (which + 1) & 3;
-    PutEscapedString(bufs[which], 100, JSID_TO_STRING(id), 0);
+    PutEscapedString(bufs[which], 100, JSID_TO_FLAT_STRING(id), 0);
     return bufs[which];
 }
 
 void
 TypeObject::splicePrototype(JSObject *proto)
 {
     JS_ASSERT(!this->proto);
     this->proto = proto;
@@ -629,16 +629,19 @@ Script::analyze(JSContext *cx)
             continue;
         }
 
         code->analyzed = true;
 
         if (forwardCatch)
             code->inTryBlock = true;
 
+        if (untrap.trap)
+            code->safePoint = true;
+
         unsigned stackDepth = code->stackDepth;
         uint32 *defineArray = code->defineArray;
         unsigned defineCount = code->defineCount;
 
         if (!forwardJump) {
             /*
              * There is no jump over this bytecode, nor a containing try block.
              * Either this bytecode will definitely be executed, or an exception
@@ -688,18 +691,20 @@ Script::analyze(JSContext *cx)
                              id, offset, i, stack->types.id());
         }
 
         /* Track the initializer stack and compute new objects for encountered initializers. */
         if (op == JSOP_NEWINIT || op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT) {
             bool newArray = (op == JSOP_NEWARRAY) || (op == JSOP_NEWINIT && pc[1] == JSProto_Array);
 
             types::TypeObject *object;
-            if (initializerStack && initializerStack->initObject &&
-                initializerStack->initArray == newArray) {
+            if (!script->compileAndGo) {
+                object = NULL;
+            } else if (initializerStack && initializerStack->initObject &&
+                       initializerStack->initArray == newArray) {
                 object = initializerStack->initObject;
                 if (newArray)
                     code->initArray = object;
                 else
                     code->initObject = object;
             } else {
                 object = code->getInitObject(cx, newArray);
 
@@ -757,26 +762,28 @@ Script::analyze(JSContext *cx)
             jsint high = GET_JUMP_OFFSET(pc2);
             pc2 += JUMP_OFFSET_LEN;
 
             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump,
                          stackDepth, stack, defineArray, defineCount)) {
                 return;
             }
             getCode(defaultOffset).switchTarget = true;
+            getCode(defaultOffset).safePoint = true;
 
             for (jsint i = low; i <= high; i++) {
                 unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
                 if (targetOffset != offset) {
                     if (!addJump(cx, targetOffset, &nextOffset, &forwardJump,
                                  stackDepth, stack, defineArray, defineCount)) {
                         return;
                     }
                 }
                 getCode(targetOffset).switchTarget = true;
+                getCode(targetOffset).safePoint = true;
                 pc2 += jmplen;
             }
             break;
           }
 
           case JSOP_LOOKUPSWITCH:
           case JSOP_LOOKUPSWITCHX: {
             jsbytecode *pc2 = pc;
@@ -786,25 +793,27 @@ Script::analyze(JSContext *cx)
             unsigned npairs = GET_UINT16(pc2);
             pc2 += UINT16_LEN;
 
             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump,
                          stackDepth, stack, defineArray, defineCount)) {
                 return;
             }
             getCode(defaultOffset).switchTarget = true;
+            getCode(defaultOffset).safePoint = true;
 
             while (npairs) {
                 pc2 += INDEX_LEN;
                 unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
                 if (!addJump(cx, targetOffset, &nextOffset, &forwardJump,
                              stackDepth, stack, defineArray, defineCount)) {
                     return;
                 }
                 getCode(targetOffset).switchTarget = true;
+                getCode(targetOffset).safePoint = true;
                 pc2 += jmplen;
                 npairs--;
             }
             break;
           }
 
           case JSOP_TRY: {
             /*
@@ -825,16 +834,17 @@ Script::analyze(JSContext *cx)
                         forwardCatch = catchOffset;
 
                     if (tn->kind != JSTRY_ITER) {
                         if (!addJump(cx, catchOffset, &nextOffset, &forwardJump,
                                      stackDepth, stack, defineArray, defineCount)) {
                             return;
                         }
                         getCode(catchOffset).exceptionEntry = true;
+                        getCode(catchOffset).safePoint = true;
                     }
                 }
             }
             break;
           }
 
           case JSOP_GETLOCAL:
             /*
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -77,16 +77,19 @@ struct Bytecode
     bool analyzed : 1;
 
     /* Whether this is a catch/finally entry point. */
     bool exceptionEntry : 1;
 
     /* Whether this is in a try block. */
     bool inTryBlock : 1;
 
+    /* Method JIT safe point. */
+    bool safePoint : 1;
+
     /*
      * For type inference, whether this bytecode needs to have its effects monitored dynamically.
      * This is limited to property/name sets and calls.
      */
     bool monitorNeeded : 1;
 
     /* Stack depth before this opcode. */
     uint32 stackDepth;
--- a/js/src/jsapi-tests/Makefile.in
+++ b/js/src/jsapi-tests/Makefile.in
@@ -71,16 +71,17 @@ CPPSRCS = \
   testScriptObject.cpp \
   testSetPropertyWithNativeGetterStubSetter.cpp \
   testThreadGC.cpp \
   testThreads.cpp \
   testTrap.cpp \
   testUTF8.cpp \
   testVersion.cpp \
   testXDR.cpp \
+  testCustomIterator.cpp \
   $(NULL)
 
 DEFINES         += -DEXPORT_JS_API
 
 LIBS      = $(NSPR_LIBS) $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX)
 
 ifdef JS_TYPE_INFERENCE
 LIBS += -lz
--- a/js/src/jsapi-tests/testConservativeGC.cpp
+++ b/js/src/jsapi-tests/testConservativeGC.cpp
@@ -53,8 +53,28 @@ bool checkObjectFields(JSObject *savedCo
      */
     savedCopy->objShape = obj->objShape;
     savedCopy->slots = obj->slots;
     CHECK(!memcmp(savedCopy, obj, sizeof(*obj)));
     return true;
 }
 
 END_TEST(testConservativeGC)
+
+BEGIN_TEST(testDerivedValues)
+{
+  JSString *str = JS_NewStringCopyZ(cx, "once upon a midnight dreary");
+  js::Anchor<JSString *> str_anchor(str);
+  static const jschar expected[] = { 'o', 'n', 'c', 'e' };
+  const jschar *ch = JS_GetStringCharsZ(cx, str);
+  str = NULL;
+
+  /* Do a lot of allocation and collection. */
+  for (int i = 0; i < 3; i++) {
+    for (int j = 0; j < 1000; j++)
+      JS_NewStringCopyZ(cx, "as I pondered weak and weary");
+    JS_GC(cx);
+  }
+
+  CHECK(!memcmp(ch, expected, sizeof(expected)));
+  return true;
+}
+END_TEST(testDerivedValues)
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testCustomIterator.cpp
@@ -0,0 +1,81 @@
+#include "tests.h"
+
+#include "jsvalue.h"
+
+int count = 0;
+
+static JSBool
+IterNext(JSContext *cx, uintN argc, jsval *vp)
+{
+    if (count++ == 100)
+        return JS_ThrowStopIteration(cx);
+    JS_SET_RVAL(cx, vp, INT_TO_JSVAL(count));
+    return true;
+}
+
+static JSObject *
+IterHook(JSContext *cx, JSObject *obj, JSBool keysonly)
+{
+    JSObject *iterObj = JS_NewObject(cx, NULL, NULL, NULL);
+    if (!iterObj)
+        return NULL;
+    if (!JS_DefineFunction(cx, iterObj, "next", IterNext, 0, 0))
+        return NULL;
+    return iterObj;
+}
+
+js::Class HasCustomIterClass = {
+    "HasCustomIter",
+    0,
+    js::PropertyStub,
+    js::PropertyStub,
+    js::PropertyStub,
+    js::PropertyStub,
+    js::EnumerateStub,
+    js::ResolveStub,
+    js::ConvertStub,
+    NULL,
+    NULL, /* reserved0 */
+    NULL, /* checkAccess */
+    NULL, /* call */
+    NULL, /* construct */
+    NULL, /* xdrObject */
+    NULL, /* hasInstance */
+    NULL, /* mark */
+    {
+        NULL,
+        NULL,
+        NULL,
+        IterHook,
+        NULL
+    }
+};
+
+JSBool
+IterClassConstructor(JSContext *cx, uintN argc, jsval *vp)
+{
+    JSObject *obj = JS_NewObjectForConstructor(cx, vp);
+    if (!obj)
+        return false;
+    JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
+    return true;
+}
+
+BEGIN_TEST(testCustomIterator_bug612523)
+{
+    CHECK(JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, Jsvalify(&HasCustomIterClass),
+                       IterClassConstructor, 0, NULL, NULL, NULL, NULL));
+
+    jsval result;
+    EVAL("var o = new HasCustomIter(); \n"
+         "var j = 0; \n"
+         "for (var i in o) { ++j; }; \n"
+         "j;", &result);
+
+    CHECK(JSVAL_IS_INT(result));
+    CHECK(JSVAL_TO_INT(result) == 100);
+    CHECK(count == 101);
+
+    return true;
+}
+END_TEST(testCustomIterator_bug612523)
--- a/js/src/jsapi-tests/testGetPropertyDefault.cpp
+++ b/js/src/jsapi-tests/testGetPropertyDefault.cpp
@@ -5,21 +5,17 @@
 #include "tests.h"
 
 #define JSVAL_IS_FALSE(x) ((JSVAL_IS_BOOLEAN(x)) && !(JSVAL_TO_BOOLEAN(x)))
 #define JSVAL_IS_TRUE(x)  ((JSVAL_IS_BOOLEAN(x)) && (JSVAL_TO_BOOLEAN(x)))
 
 static JSBool
 stringToId(JSContext *cx, const char *s, jsid *idp)
 {
-    char *buf = JS_strdup(cx, s);
-    if (!buf)
-        return false;
-
-    JSString *str = JS_NewString(cx, buf, strlen(s));
+    JSString *str = JS_NewStringCopyZ(cx, s);
     if (!str)
         return false;
 
     return JS_ValueToId(cx, STRING_TO_JSVAL(str), idp);
 }
 
 BEGIN_TEST(testGetPropertyDefault_bug594060)
 {
--- a/js/src/jsapi-tests/testIntString.cpp
+++ b/js/src/jsapi-tests/testIntString.cpp
@@ -7,34 +7,34 @@
 
 BEGIN_TEST(testIntString_bug515273)
 {
     jsvalRoot v(cx);
 
     EVAL("'1';", v.addr());
     JSString *str = JSVAL_TO_STRING(v.value());
     CHECK(JSString::isStatic(str));
-    CHECK(JS_MatchStringAndAscii(str, "1"));
+    CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "1"));
 
     EVAL("'42';", v.addr());
     str = JSVAL_TO_STRING(v.value());
     CHECK(JSString::isStatic(str));
-    CHECK(JS_MatchStringAndAscii(str, "42"));
+    CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "42"));
 
     EVAL("'111';", v.addr());
     str = JSVAL_TO_STRING(v.value());
     CHECK(JSString::isStatic(str));
-    CHECK(JS_MatchStringAndAscii(str, "111"));
+    CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "111"));
 
     /* Test other types of static strings. */
     EVAL("'a';", v.addr());
     str = JSVAL_TO_STRING(v.value());
     CHECK(JSString::isStatic(str));
-    CHECK(JS_MatchStringAndAscii(str, "a"));
+    CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "a"));
 
     EVAL("'bc';", v.addr());
     str = JSVAL_TO_STRING(v.value());
     CHECK(JSString::isStatic(str));
-    CHECK(JS_MatchStringAndAscii(str, "bc"));
+    CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "bc"));
 
     return true;
 }
 END_TEST(testIntString_bug515273)
--- a/js/src/jsapi-tests/testLookup.cpp
+++ b/js/src/jsapi-tests/testLookup.cpp
@@ -36,17 +36,20 @@ JSBool
 document_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
 {
     // If id is "all", and we're not detecting, resolve document.all=true.
     jsvalRoot v(cx);
     if (!JS_IdToValue(cx, id, v.addr()))
         return false;
     if (JSVAL_IS_STRING(v.value())) {
         JSString *str = JSVAL_TO_STRING(v.value());
-        if (JS_MatchStringAndAscii(str, "all") && !(flags & JSRESOLVE_DETECTING)) {
+        JSFlatString *flatStr = JS_FlattenString(cx, str);
+        if (!flatStr)
+            return false;
+        if (JS_FlatStringEqualsAscii(flatStr, "all") && !(flags & JSRESOLVE_DETECTING)) {
             JSBool ok = JS_DefinePropertyById(cx, obj, id, JSVAL_TRUE, NULL, NULL, 0);
             *objp = ok ? obj : NULL;
             return ok;
         }
     }
     *objp = NULL;
     return true;
 }
--- a/js/src/jsapi-tests/testSameValue.cpp
+++ b/js/src/jsapi-tests/testSameValue.cpp
@@ -11,12 +11,14 @@ BEGIN_TEST(testSameValue)
      * NB: passing a double that fits in an integer jsval is API misuse.  As a
      * matter of defense in depth, however, JS_SameValue should return the
      * correct result comparing a positive-zero double to a negative-zero
      * double, and this is believed to be the only way to make such a
      * comparison possible.
      */
     jsval v1 = DOUBLE_TO_JSVAL(0.0);
     jsval v2 = DOUBLE_TO_JSVAL(-0.0);
-    CHECK(!JS_SameValue(cx, v1, v2));
+    JSBool same;
+    CHECK(JS_SameValue(cx, v1, v2, &same));
+    CHECK(!same);
     return true;
 }
 END_TEST(testSameValue)
--- a/js/src/jsapi-tests/testTrap.cpp
+++ b/js/src/jsapi-tests/testTrap.cpp
@@ -57,22 +57,22 @@ BEGIN_TEST(testTrap_gc)
     static const char trapClosureText[] = "some trap closure";
     JSString *trapClosure = JS_NewStringCopyZ(cx, trapClosureText);
     CHECK(trapClosure);
     JS_SetTrap(cx, script, line2, EmptyTrapHandler, STRING_TO_JSVAL(trapClosure));
     JS_SetTrap(cx, script, line6, EmptyTrapHandler, STRING_TO_JSVAL(trapClosure));
 
     JS_GC(cx);
 
-    CHECK(JS_MatchStringAndAscii(trapClosure, trapClosureText));
+    CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(trapClosure), trapClosureText));
 
     // execute
     CHECK(JS_ExecuteScript(cx, global, script, v2.addr()));
     CHECK(emptyTrapCallCount == 11);
 
     JS_GC(cx);
 
-    CHECK(JS_MatchStringAndAscii(trapClosure, trapClosureText));
+    CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(trapClosure), trapClosureText));
 
     return true;
 }
 END_TEST(testTrap_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-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -187,17 +187,18 @@ class JSAPITest
     do { \
         if (!checkSame(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
             return false; \
     } while (false)
 
     bool checkSame(jsval actual, jsval expected,
                    const char *actualExpr, const char *expectedExpr,
                    const char *filename, int lineno) {
-        return JS_SameValue(cx, actual, expected) ||
+        JSBool same;
+        return (JS_SameValue(cx, actual, expected, &same) && same) ||
                fail(JSAPITestString("CHECK_SAME failed: expected JS_SameValue(cx, ") +
                     actualExpr + ", " + expectedExpr + "), got !JS_SameValue(cx, " +
                     toSource(actual) + ", " + toSource(expected) + ")", filename, lineno);
     }
 
 #define CHECK(expr) \
     do { \
         if (!(expr)) \
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -108,16 +108,26 @@
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
+static JSClass dummy_class = {
+    "jdummy",
+    JSCLASS_GLOBAL_FLAGS,
+    JS_PropertyStub,  JS_PropertyStub,
+    JS_PropertyStub,  JS_PropertyStub,
+    JS_EnumerateStub, JS_ResolveStub,
+    JS_ConvertStub,   NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
 class AutoVersionAPI
 {
     JSContext   * const cx;
     JSVersion   oldVersion;
     bool        oldVersionWasOverride;
     uint32      oldOptions;
 
   public:
@@ -568,27 +578,27 @@ JS_PUBLIC_API(const char *)
 JS_GetTypeName(JSContext *cx, JSType type)
 {
     if ((uintN)type >= (uintN)JSTYPE_LIMIT)
         return NULL;
     return JS_TYPE_STR(type);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2)
+JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal)
 {
     assertSameCompartment(cx, v1, v2);
-    return StrictlyEqual(cx, Valueify(v1), Valueify(v2));
-}
-
-JS_PUBLIC_API(JSBool)
-JS_SameValue(JSContext *cx, jsval v1, jsval v2)
+    return StrictlyEqual(cx, Valueify(v1), Valueify(v2), equal);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same)
 {
     assertSameCompartment(cx, v1, v2);
-    return SameValue(Valueify(v1), Valueify(v2), cx);
+    return SameValue(cx, Valueify(v1), Valueify(v2), same);
 }
 
 /************************************************************************/
 
 /*
  * Has a new runtime ever been created?  This flag is used to detect unsafe
  * changes to js_CStringsAreUTF8 after a runtime has been created, and to
  * ensure that "first checks" on runtime creation are run only once.
@@ -1166,16 +1176,32 @@ JS_EnterCrossCompartmentCall(JSContext *
         return NULL;
     if (!call->enter()) {
         delete call;
         return NULL;
     }
     return reinterpret_cast<JSCrossCompartmentCall *>(call);
 }
 
+JS_PUBLIC_API(JSCrossCompartmentCall *)
+JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target)
+{
+    CHECK_REQUEST(cx);
+
+    JS_ASSERT(target);
+    JSObject *scriptObject = target->u.object;
+    if (!scriptObject) {
+        SwitchToCompartment sc(cx, target->compartment);
+        scriptObject = JS_NewGlobalObject(cx, &dummy_class);
+        if (!scriptObject)
+            return NULL;        
+    }
+    return JS_EnterCrossCompartmentCall(cx, scriptObject);
+}
+
 JS_PUBLIC_API(void)
 JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call)
 {
     AutoCompartment *realcall = reinterpret_cast<AutoCompartment *>(call);
     CHECK_REQUEST(realcall->context);
     realcall->leave();
     delete realcall;
 }
@@ -1187,16 +1213,28 @@ JSAutoEnterCompartment::enter(JSContext 
     if (cx->compartment == target->getCompartment()) {
         call = reinterpret_cast<JSCrossCompartmentCall*>(1);
         return true;
     }
     call = JS_EnterCrossCompartmentCall(cx, target);
     return call != NULL;
 }
 
+bool
+JSAutoEnterCompartment::enter(JSContext *cx, JSScript *target)
+{
+    JS_ASSERT(!call);
+    if (cx->compartment == target->compartment) {
+        call = reinterpret_cast<JSCrossCompartmentCall*>(1);
+        return true;
+    }
+    call = JS_EnterCrossCompartmentCallScript(cx, target);
+    return call != NULL;    
+}
+
 void
 JSAutoEnterCompartment::enterAndIgnoreErrors(JSContext *cx, JSObject *target)
 {
     (void) enter(cx, target);
 }
 
 JS_PUBLIC_API(void *)
 JS_SetCompartmentPrivate(JSContext *cx, JSCompartment *compartment, void *data)
@@ -2196,18 +2234,24 @@ JS_PrintTraceThingInfo(char *buf, size_t
                 JS_snprintf(buf, bufsize, "%p", obj->getPrivate());
             } else {
                 JS_snprintf(buf, bufsize, "<no private>");
             }
             break;
           }
 
           case JSTRACE_STRING:
-            PutEscapedString(buf, bufsize, (JSString *)thing, 0);
+          {
+            JSString *str = (JSString *)thing;
+            if (str->isLinear())
+                PutEscapedString(buf, bufsize, str->assertIsLinear(), 0);
+            else
+                JS_snprintf(buf, bufsize, "<rope: length %d>", (int)str->length());
             break;
+          }
 
 #if JS_HAS_XML_SUPPORT
           case JSTRACE_XML:
           {
             extern const char *js_xml_class_str[];
             JSXML *xml = (JSXML *)thing;
 
             JS_snprintf(buf, bufsize, "%s", js_xml_class_str[xml->xml_class]);
@@ -3319,16 +3363,17 @@ DefinePropertyById(JSContext *cx, JSObje
                             ? JS_FUNC_TO_DATA_PTR(JSObject *, setter)
                             : NULL);
 
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING);
     if (flags != 0 && obj->isNative()) {
         return !!js_DefineNativeProperty(cx, obj, id, value, getter, setter,
                                          attrs, flags, tinyid, NULL);
     }
+
     return obj->defineProperty(cx, id, value, getter, setter, attrs);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value,
                       JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
 {
     return DefinePropertyById(cx, obj, id, Valueify(value), Valueify(getter),
@@ -3789,16 +3834,18 @@ JS_GetMethod(JSContext *cx, JSObject *ob
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
+
+    cx->addTypePropertyId(obj->getType(), id, Valueify(*vp));
     return obj->setProperty(cx, id, Valueify(vp), false);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp)
 {
     return JS_SetPropertyById(cx, obj, INT_TO_JSID(index), vp);
 }
@@ -4064,17 +4111,23 @@ 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));
+
+#ifdef DEBUG
+    for (int i = 0; i < length; i++)
+        JS_ASSERT(!Valueify(vector[i]).isMagic(JS_ARRAY_HOLE));
+#endif
+
+    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());
@@ -4848,17 +4901,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
@@ -5065,17 +5117,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)
@@ -5348,52 +5400,21 @@ 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 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);
-    if (!js)
-        return NULL;
-    str = js_NewString(cx, js, n);
-    if (!str)
-        cx->free(js);
-    return str;
+    return js_NewStringCopyN(cx, s, n);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_NewStringCopyZ(JSContext *cx, const char *s)
 {
     size_t n;
     jschar *js;
     JSString *str;
@@ -5413,16 +5434,26 @@ JS_NewStringCopyZ(JSContext *cx, const c
 
 JS_PUBLIC_API(JSBool)
 JS_StringHasBeenInterned(JSString *str)
 {
     return str->isAtomized();
 }
 
 JS_PUBLIC_API(JSString *)
+JS_InternJSString(JSContext *cx, JSString *str)
+{
+    CHECK_REQUEST(cx);
+    JSAtom *atom = js_AtomizeString(cx, str, 0);
+    if (!atom)
+        return NULL;
+    return ATOM_TO_STRING(atom);
+}
+
+JS_PUBLIC_API(JSString *)
 JS_InternString(JSContext *cx, const char *s)
 {
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     atom = js_Atomize(cx, s, strlen(s), ATOM_INTERNED);
     if (!atom)
         return NULL;
@@ -5465,97 +5496,119 @@ JS_InternUCStringN(JSContext *cx, const 
 }
 
 JS_PUBLIC_API(JSString *)
 JS_InternUCString(JSContext *cx, const jschar *s)
 {
     return JS_InternUCStringN(cx, s, js_strlen(s));
 }
 
-JS_PUBLIC_API(jschar *)
-JS_GetStringChars(JSString *str)
-{
-    size_t n, size;
-    jschar *s;
-
-    str->ensureNotRope();
-
-    /*
-     * API botch: we have no cx to report out-of-memory when undepending
-     * strings, so we replace JSString::undepend with explicit malloc call and
-     * ignore its errors.
-     *
-     * If we fail to convert a dependent string into an independent one, our
-     * caller will not be guaranteed a \u0000 terminator as a backstop.  This
-     * may break some clients who already misbehave on embedded NULs.
-     *
-     * The gain of dependent strings, which cure quadratic and cubic growth
-     * rate bugs in string concatenation, is worth this slight loss in API
-     * compatibility.
-     */
-    if (str->isDependent()) {
-        n = str->dependentLength();
-        size = (n + 1) * sizeof(jschar);
-        s = (jschar *) js_malloc(size);
-        if (s) {
-            memcpy(s, str->dependentChars(), n * sizeof *s);
-            s[n] = 0;
-            str->initFlat(s, n);
-        } else {
-            s = str->dependentChars();
-        }
-    } else {
-        str->flatClearExtensible();
-        s = str->flatChars();
-    }
-    return s;
-}
-
 JS_PUBLIC_API(size_t)
 JS_GetStringLength(JSString *str)
 {
     return str->length();
 }
 
 JS_PUBLIC_API(const jschar *)
-JS_GetStringCharsAndLength(JSString *str, size_t *lengthp)
-{
-    *lengthp = str->length();
-    return str->chars();
+JS_GetStringCharsZ(JSContext *cx, JSString *str)
+{
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, str);
+    return str->getCharsZ(cx);
+}
+
+JS_PUBLIC_API(const jschar *)
+JS_GetStringCharsZAndLength(JSContext *cx, JSString *str, size_t *plength)
+{
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, str);
+    *plength = str->length();
+    return str->getCharsZ(cx);
+}
+
+JS_PUBLIC_API(const jschar *)
+JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *plength)
+{
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, str);
+    *plength = str->length();
+    return str->getChars(cx);
+}
+
+JS_PUBLIC_API(const jschar *)
+JS_GetInternedStringChars(JSString *str)
+{
+    JS_ASSERT(str->isAtomized());
+    return str->flatChars();
 }
 
 JS_PUBLIC_API(const jschar *)
-JS_GetStringCharsZ(JSContext *cx, JSString *str)
-{
+JS_GetInternedStringCharsAndLength(JSString *str, size_t *plength)
+{
+    JS_ASSERT(str->isAtomized());
+    *plength = str->flatLength();
+    return str->flatChars();
+}
+
+extern JS_PUBLIC_API(JSFlatString *)
+JS_FlattenString(JSContext *cx, JSString *str)
+{
+    CHECK_REQUEST(cx);
     assertSameCompartment(cx, str);
-    return str->undepend(cx);
-}
-
-JS_PUBLIC_API(intN)
-JS_CompareStrings(JSString *str1, JSString *str2)
-{
-    return js_CompareStrings(str1, str2);
-}
-
-JS_PUBLIC_API(JSBool)
-JS_MatchStringAndAscii(JSString *str, const char *asciiBytes)
-{
-    return MatchStringAndAscii(str, asciiBytes);
+    return str->getCharsZ(cx) ? (JSFlatString *)str : NULL;
+}
+
+extern JS_PUBLIC_API(const jschar *)
+JS_GetFlatStringChars(JSFlatString *str)
+{
+    return str->chars();
+}
+
+JS_PUBLIC_API(JSBool)
+JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result)
+{
+    return CompareStrings(cx, str1, str2, result);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_StringEqualsAscii(JSContext *cx, JSString *str, const char *asciiBytes, JSBool *match)
+{
+    JSLinearString *linearStr = str->ensureLinear(cx);
+    if (!linearStr)
+        return false;
+    *match = StringEqualsAscii(linearStr, asciiBytes);
+    return true;
+}
+
+JS_PUBLIC_API(JSBool)
+JS_FlatStringEqualsAscii(JSFlatString *str, const char *asciiBytes)
+{
+    return StringEqualsAscii(str, asciiBytes);
 }
 
 JS_PUBLIC_API(size_t)
-JS_PutEscapedString(char *buffer, size_t size, JSString *str, char quote)
+JS_PutEscapedFlatString(char *buffer, size_t size, JSFlatString *str, char quote)
 {
     return PutEscapedString(buffer, size, str, quote);
 }
 
+JS_PUBLIC_API(size_t)
+JS_PutEscapedString(JSContext *cx, char *buffer, size_t size, JSString *str, char quote)
+{
+    JSLinearString *linearStr = str->ensureLinear(cx);
+    if (!linearStr)
+        return size_t(-1);
+    return PutEscapedString(buffer, size, linearStr, quote);
+}
+
 JS_PUBLIC_API(JSBool)
 JS_FileEscapedString(FILE *fp, JSString *str, char quote)
 {
-    return FileEscapedString(fp, str, quote);
+    JSLinearString *linearStr = str->ensureLinear(NULL);
+    return linearStr && FileEscapedString(fp, linearStr, quote);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length)
 {
     CHECK_REQUEST(cx);
     return js_NewString(cx, chars, length);
 }
@@ -5573,17 +5626,17 @@ JS_ConcatStrings(JSContext *cx, JSString
     CHECK_REQUEST(cx);
     return js_ConcatStrings(cx, left, right);
 }
 
 JS_PUBLIC_API(const jschar *)
 JS_UndependString(JSContext *cx, JSString *str)
 {
     CHECK_REQUEST(cx);
-    return str->undepend(cx);
+    return str->getCharsZ(cx);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_MakeStringImmutable(JSContext *cx, JSString *str)
 {
     CHECK_REQUEST(cx);
     return js_MakeStringImmutable(cx, str);
 }
@@ -5609,40 +5662,49 @@ JS_PUBLIC_API(JSBool)
 JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_t *dstlenp)
 {
     return js_InflateStringToBuffer(cx, src, srclen, dst, dstlenp);
 }
 
 JS_PUBLIC_API(char *)
 JS_EncodeString(JSContext *cx, JSString *str)
 {
-    return js_DeflateString(cx, str->chars(), str->length());
+    const jschar *chars = str->getChars(cx);
+    if (!chars)
+        return NULL;
+    return js_DeflateString(cx, chars, str->length());
 }
 
 JS_PUBLIC_API(size_t)
 JS_GetStringEncodingLength(JSContext *cx, JSString *str)
 {
-    return js_GetDeflatedStringLength(cx, str->chars(), str->length());
+    const jschar *chars = str->getChars(cx);
+    if (!chars)
+        return size_t(-1);
+    return js_GetDeflatedStringLength(cx, chars, str->length());
 }
 
 JS_PUBLIC_API(size_t)
 JS_EncodeStringToBuffer(JSString *str, char *buffer, size_t length)
 {
     /*
      * FIXME bug 612141 - fix js_DeflateStringToBuffer interface so the result
      * would allow to distinguish between insufficient buffer and encoding
      * error.
      */
     size_t writtenLength = length;
-    if (js_DeflateStringToBuffer(NULL, str->chars(), str->length(), buffer, &writtenLength)) {
+    const jschar *chars = str->getChars(NULL);
+    if (!chars)
+        return size_t(-1);
+    if (js_DeflateStringToBuffer(NULL, chars, str->length(), buffer, &writtenLength)) {
         JS_ASSERT(writtenLength <= length);
         return writtenLength;
     }
     JS_ASSERT(writtenLength <= length);
-    size_t necessaryLength = js_GetDeflatedStringLength(NULL, str->chars(), str->length());
+    size_t necessaryLength = js_GetDeflatedStringLength(NULL, chars, str->length());
     if (necessaryLength == size_t(-1))
         return size_t(-1);
     if (writtenLength != length) {
         /* Make sure that the buffer contains only valid UTF-8 sequences. */
         JS_ASSERT(js_CStringsAreUTF8);
         PodZero(buffer + writtenLength, length - writtenLength);
     }
     return necessaryLength;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -697,20 +697,20 @@ JS_ValueToBoolean(JSContext *cx, jsval v
 
 extern JS_PUBLIC_API(JSType)
 JS_TypeOfValue(JSContext *cx, jsval v);
 
 extern JS_PUBLIC_API(const char *)
 JS_GetTypeName(JSContext *cx, JSType type);
 
 extern JS_PUBLIC_API(JSBool)
-JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2);
+JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal);
 
 extern JS_PUBLIC_API(JSBool)
-JS_SameValue(JSContext *cx, jsval v1, jsval v2);
+JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same);
 
 /************************************************************************/
 
 /*
  * Initialization, locking, contexts, and memory allocation.
  *
  * It is important that the first runtime and first context be created in a
  * single-threaded fashion, otherwise the behavior of the library is undefined.
@@ -974,16 +974,19 @@ JS_SetCompartmentCallback(JSRuntime *rt,
 extern JS_PUBLIC_API(JSWrapObjectCallback)
 JS_SetWrapObjectCallbacks(JSRuntime *rt,
                           JSWrapObjectCallback callback,
                           JSPreWrapCallback precallback);
 
 extern JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target);
 
+extern JS_PUBLIC_API(JSCrossCompartmentCall *)
+JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target);
+
 extern JS_PUBLIC_API(void)
 JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call);
 
 extern JS_PUBLIC_API(void *)
 JS_SetCompartmentPrivate(JSContext *cx, JSCompartment *compartment, void *data);
 
 extern JS_PUBLIC_API(void *)
 JS_GetCompartmentPrivate(JSContext *cx, JSCompartment *compartment);
@@ -1004,16 +1007,18 @@ class JS_PUBLIC_API(JSAutoEnterCompartme
 {
     JSCrossCompartmentCall *call;
 
   public:
     JSAutoEnterCompartment() : call(NULL) {}
 
     bool enter(JSContext *cx, JSObject *target);
 
+    bool enter(JSContext *cx, JSScript *target);
+
     void enterAndIgnoreErrors(JSContext *cx, JSObject *target);
 
     bool entered() const { return call != NULL; }
 
     ~JSAutoEnterCompartment() {
         if (call && call != reinterpret_cast<JSCrossCompartmentCall*>(1))
             JS_LeaveCrossCompartmentCall(call);
     }
@@ -1264,16 +1269,177 @@ extern JS_FRIEND_API(JSBool)
 js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name);
 
 extern JS_FRIEND_API(JSBool)
 js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name);
 
 extern JS_FRIEND_API(JSBool)
 js_RemoveRoot(JSRuntime *rt, void *rp);
 
+#ifdef __cplusplus
+JS_END_EXTERN_C
+
+namespace js {
+
+/*
+ * Protecting non-jsval, non-JSObject *, non-JSString * values from collection
+ * 
+ * Most of the time, the garbage collector's conservative stack scanner works
+ * behind the scenes, finding all live values and protecting them from being
+ * collected. However, when JSAPI client code obtains a pointer to data the
+ * scanner does not know about, owned by an object the scanner does know about,
+ * Care Must Be Taken.
+ *
+ * The scanner recognizes only a select set of types: pointers to JSObjects and
+ * similar things (JSFunctions, and so on), pointers to JSStrings, and jsvals.
+ * So while the scanner finds all live |JSString| pointers, it does not notice
+ * |jschar| pointers.
+ *
+ * So suppose we have:
+ *
+ *   void f(JSString *str) {
+ *     const jschar *ch = JS_GetStringCharsZ(str);
+ *     ... do stuff with ch, but no uses of str ...;
+ *   }
+ *
+ * After the call to |JS_GetStringCharsZ|, there are no further uses of
+ * |str|, which means that the compiler is within its rights to not store
+ * it anywhere. But because the stack scanner will not notice |ch|, there
+ * is no longer any live value in this frame that would keep the string
+ * alive. If |str| is the last reference to that |JSString|, and the
+ * collector runs while we are using |ch|, the string's array of |jschar|s
+ * may be freed out from under us.
+ *
+ * Note that there is only an issue when 1) we extract a thing X the scanner
+ * doesn't recognize from 2) a thing Y the scanner does recognize, and 3) if Y
+ * gets garbage-collected, then X gets freed. If we have code like this:
+ *
+ *   void g(JSObject *obj) {
+ *     jsval x;
+ *     JS_GetProperty(obj, "x", &x);
+ *     ... do stuff with x ...
+ *   }
+ *
+ * there's no problem, because the value we've extracted, x, is a jsval, a
+ * type that the conservative scanner recognizes.
+ *
+ * Conservative GC frees us from the obligation to explicitly root the types it
+ * knows about, but when we work with derived values like |ch|, we must root
+ * their owners, as the derived value alone won't keep them alive.
+ *
+ * A js::Anchor is a kind of GC root that allows us to keep the owners of
+ * derived values like |ch| alive throughout the Anchor's lifetime. We could
+ * fix the above code as follows:
+ *
+ *   void f(JSString *str) {
+ *     js::Anchor<JSString *> a_str(str);
+ *     const jschar *ch = JS_GetStringCharsZ(str);
+ *     ... 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; }
+    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
+/*
+ * 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|.
+ */
+template<>
+inline Anchor<jsval>::~Anchor() {
+    volatile jsval sink;
+    sink.asBits = hold.asBits;
+}
+#endif
+#endif
+
+}  /* namespace js */
+
+JS_BEGIN_EXTERN_C
+#endif
+
 /*
  * This symbol may be used by embedders to detect the change from the old
  * JS_AddRoot(JSContext *, void *) APIs to the new ones above.
  */
 #define JS_TYPED_ROOTING_API
 
 /* Obsolete rooting APIs. */
 #define JS_ClearNewbornRoots(cx) ((void) 0)
@@ -2886,32 +3052,32 @@ 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_InternJSString(JSContext *cx, JSString *str);
+
+extern JS_PUBLIC_API(JSString *)
 JS_InternString(JSContext *cx, const char *s);
 
 extern JS_PUBLIC_API(JSString *)
 JS_NewUCString(JSContext *cx, jschar *chars, size_t length);
 
 extern JS_PUBLIC_API(JSString *)
 JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n);
 
@@ -2919,46 +3085,116 @@ extern JS_PUBLIC_API(JSString *)
 JS_NewUCStringCopyZ(JSContext *cx, const jschar *s);
 
 extern JS_PUBLIC_API(JSString *)
 JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length);
 
 extern JS_PUBLIC_API(JSString *)
 JS_InternUCString(JSContext *cx, const jschar *s);
 
+extern JS_PUBLIC_API(JSBool)
+JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result);
+
+extern JS_PUBLIC_API(JSBool)
+JS_StringEqualsAscii(JSContext *cx, JSString *str, const char *asciiBytes, JSBool *match);
+
+extern JS_PUBLIC_API(size_t)
+JS_PutEscapedString(JSContext *cx, char *buffer, size_t size, JSString *str, char quote);
+
+extern JS_PUBLIC_API(JSBool)
+JS_FileEscapedString(FILE *fp, JSString *str, char quote);
+
 /*
- * Deprecated. Use JS_GetStringCharsZ() instead.
+ * Extracting string characters and length.
+ *
+ * While getting the length of a string is infallible, getting the chars can
+ * fail. As indicated by the lack of a JSContext parameter, there are two
+ * special cases where getting the chars is infallible:
+ *
+ * The first case is interned strings, i.e., strings from JS_InternString or
+ * JSID_TO_STRING(id), using JS_GetInternedStringChars*.
+ *
+ * The second case is "flat" strings that have been explicitly prepared in a
+ * fallible context by JS_FlattenString. To catch errors, a separate opaque
+ * JSFlatString type is returned by JS_FlattenString and expected by
+ * JS_GetFlatStringChars. Note, though, that this is purely a syntactic
+ * distinction: the input and output of JS_FlattenString are the same actual
+ * GC-thing so only one needs to be rooted. If a JSString is known to be flat,
+ * JS_ASSERT_STRING_IS_FLAT can be used to make a debug-checked cast. Example:
+ *
+ *   // in a fallible context
+ *   JSFlatString *fstr = JS_FlattenString(cx, str);
+ *   if (!fstr)
+ *     return JS_FALSE;
+ *   JS_ASSERT(fstr == JS_ASSERT_STRING_IS_FLAT(str));
+ *
+ *   // in an infallible context, for the same 'str'
+ *   const jschar *chars = JS_GetFlatStringChars(fstr)
+ *   JS_ASSERT(chars);
+ *
+ * The CharsZ APIs guarantee that the returned array has a null character at
+ * chars[length]. This can require additional copying so clients should prefer
+ * APIs without CharsZ if possible. The infallible functions also return
+ * null-terminated arrays. (There is no additional cost or non-Z alternative
+ * for the infallible functions, so 'Z' is left out of the identifier.)
  */
-extern JS_PUBLIC_API(jschar *)
-JS_GetStringChars(JSString *str);
 
 extern JS_PUBLIC_API(size_t)
 JS_GetStringLength(JSString *str);
 
-/*
- * Return the char array and length for this string. The array is not
- * null-terminated.
- */
+extern JS_PUBLIC_API(const jschar *)
+JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *length);
+
 extern JS_PUBLIC_API(const jschar *)
-JS_GetStringCharsAndLength(JSString *str, size_t *lengthp);
+JS_GetInternedStringChars(JSString *str);
+
+extern JS_PUBLIC_API(const jschar *)
+JS_GetInternedStringCharsAndLength(JSString *str, size_t *length);
 
 extern JS_PUBLIC_API(const jschar *)
 JS_GetStringCharsZ(JSContext *cx, JSString *str);
 
-extern JS_PUBLIC_API(intN)
-JS_CompareStrings(JSString *str1, JSString *str2);
+extern JS_PUBLIC_API(const jschar *)
+JS_GetStringCharsZAndLength(JSContext *cx, JSString *str, size_t *length);
+
+extern JS_PUBLIC_API(JSFlatString *)
+JS_FlattenString(JSContext *cx, JSString *str);
+
+extern JS_PUBLIC_API(const jschar *)
+JS_GetFlatStringChars(JSFlatString *str);
+
+static JS_ALWAYS_INLINE JSFlatString *
+JSID_TO_FLAT_STRING(jsid id)
+{
+    JS_ASSERT(JSID_IS_STRING(id));
+    return (JSFlatString *)(JSID_BITS(id));
+}
+
+static JS_ALWAYS_INLINE JSFlatString *
+JS_ASSERT_STRING_IS_FLAT(JSString *str)
+{
+    JS_ASSERT(JS_GetFlatStringChars((JSFlatString *)str));
+    return (JSFlatString *)str;
+}
+
+static JS_ALWAYS_INLINE JSString *
+JS_FORGET_STRING_FLATNESS(JSFlatString *fstr)
+{
+    return (JSString *)fstr;
+}
+
+/*
+ * Additional APIs that avoid fallibility when given a flat string.
+ */
 
 extern JS_PUBLIC_API(JSBool)
-JS_MatchStringAndAscii(JSString *str, const char *asciiBytes);
+JS_FlatStringEqualsAscii(JSFlatString *str, const char *asciiBytes);
 
 extern JS_PUBLIC_API(size_t)
-JS_PutEscapedString(char *buffer, size_t size, JSString *str, char quote);
-
-extern JS_PUBLIC_API(JSBool)
-JS_FileEscapedString(FILE *fp, JSString *str, char quote);
+JS_PutEscapedFlatString(char *buffer, size_t size, JSFlatString *str, char quote);
 
 /*
  * This function is now obsolete and behaves the same as JS_NewUCString.  Use
  * JS_NewUCString instead.
  */
 extern JS_PUBLIC_API(JSString *)
 JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length);
 
@@ -3102,16 +3338,22 @@ class JSAutoByteString {
       : mBytes(NULL) {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     ~JSAutoByteString() {
         js_free(mBytes);
     }
 
+    /* Take ownership of the given byte array. */
+    void initBytes(char *bytes) {
+        JS_ASSERT(!mBytes);
+        mBytes = bytes;
+    }
+
     char *encode(JSContext *cx, JSString *str) {
         JS_ASSERT(!mBytes);
         JS_ASSERT(cx);
         mBytes = JS_EncodeString(cx, str);
         return mBytes;
     }
 
     void clear() {
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -48,26 +48,28 @@
  *
  * 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().
  *  - The array's initialized length, accessible with getDenseArrayInitializedLength().
  *
- * In dense mode, holes in the array are represented by (JS_ARRAY_HOLE) invalid
- * values.  Elements between the initialized length and the length property
- * are left uninitialized, but are conceptually holes.  Arrays with no holes
- * below the initialized length are "packed" arrays.
+ * In dense mode, holes in the array are represented by
+ * MagicValue(JS_ARRAY_HOLE) invalid values. Elements between the initialized
+ * length and the length property are left uninitialized, but are conceptually holes.
+ * Arrays with no holes below the initialized length are "packed" arrays.
  *
  * 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.  The initialized length is always less than or equal to both
- * the length and capacity.
+ * 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
+ * capacity must be treated as holes). See array_length_setter for another
+ * explanation of how the first case may occur. The initialized length is always
+ * less than or equal to both the length and capacity.
  *
  * 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.
  *
@@ -144,20 +146,20 @@ ENSURE_SLOW_ARRAY(JSContext *cx, JSObjec
  * except that by using signed 31-bit integers we miss the top half of the
  * valid range. This function checks the string representation itself; note
  * that calling a standard conversion routine might allow strings such as
  * "08" or "4.0" as array indices, which they are not.
  *
  * 'id' is passed as a jsboxedword since the given id need not necessarily hold
  * an atomized string.
  */
-JSBool
-js_StringIsIndex(JSString *str, jsuint *indexp)
+bool
+js_StringIsIndex(JSLinearString *str, jsuint *indexp)
 {
-    jschar *cp = str->chars();
+    const jschar *cp = str->chars();
     if (JS7_ISDEC(*cp) && str->length() < sizeof(MAXSTR)) {
         jsuint index = JS7_UNDEC(*cp++);
         jsuint oldIndex = 0;
         jsuint c = 0;
         if (index != 0) {
             while (JS7_ISDEC(*cp)) {
                 oldIndex = index;
                 c = JS7_UNDEC(*cp);
@@ -167,50 +169,54 @@ js_StringIsIndex(JSString *str, jsuint *
         }
 
         /* Ensure that all characters were consumed and we didn't overflow. */
         if (*cp == 0 &&
              (oldIndex < (MAXINDEX / 10) ||
               (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10))))
         {
             *indexp = index;
-            return JS_TRUE;
+            return true;
         }
     }
-    return JS_FALSE;
+    return 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;
@@ -570,18 +576,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.
@@ -594,37 +602,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(cx, newlen);
@@ -649,18 +650,16 @@ array_length_setter(JSContext *cx, JSObj
         do {
             --oldlen;
             if (!JS_CHECK_OPERATION_LIMIT(cx)) {
                 obj->setArrayLength(cx, oldlen + 1);
                 return false;
             }
             if (!DeleteArrayElement(cx, obj, oldlen, true)) {
                 obj->setArrayLength(cx, oldlen + 1);
-                if (strict)
-                    return false;
                 JS_ClearPendingException(cx);
                 return true;
             }
         } while (oldlen != newlen);
         obj->setArrayLength(cx, newlen);
     } else {
         /*
          * We are going to remove a lot of indexes in a presumably sparse
@@ -812,17 +811,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))
@@ -842,27 +841,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.
      */
@@ -879,31 +867,45 @@ js_PrototypeHasIndexedProperties(JSConte
     }
     return JS_FALSE;
 }
 
 static JSBool
 array_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
                      PropertyOp getter, PropertyOp setter, uintN attrs)
 {
-    uint32 i = 0;       // init to shut GCC up
-    JSBool isIndex;
-
     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
         return JS_TRUE;
 
-    isIndex = js_IdIsIndex(id, &i);
-    if (!isIndex || attrs != JSPROP_ENUMERATE) {
-        if (!ENSURE_SLOW_ARRAY(cx, obj))
-            return JS_FALSE;
+    if (!obj->isDenseArray())
         return js_DefineProperty(cx, obj, id, value, getter, setter, attrs);
-    }
-
-    Value tmp = *value;
-    return array_setProperty(cx, obj, id, &tmp, false);
+
+    do {
+        uint32 i = 0;       // init to shut GCC up
+        bool isIndex = js_IdIsIndex(id, &i);
+        if (!isIndex || attrs != JSPROP_ENUMERATE)
+            break;
+
+        JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, i, 1);
+        if (result != JSObject::ED_OK) {
+            if (result == JSObject::ED_FAILED)
+                return false;
+            JS_ASSERT(result == JSObject::ED_SPARSE);
+            break;
+        }
+
+        if (i >= obj->getArrayLength())
+            obj->setArrayLength(cx, i + 1);
+        obj->setDenseArrayElement(i, *value);
+        return true;
+    } while (false);
+
+    if (!obj->makeDenseArraySlow(cx))
+        return false;
+    return js_DefineProperty(cx, obj, id, value, getter, setter, attrs);
 }
 
 static JSBool
 array_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     *attrsp = JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)
         ? JSPROP_PERMANENT : JSPROP_ENUMERATE;
     return JS_TRUE;
@@ -1012,44 +1014,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)
 {
@@ -1071,17 +1046,17 @@ JSObject::makeDenseArraySlow(JSContext *
 
     uint32 initlen = getDenseArrayInitializedLength();
 
     /*
      * 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).
@@ -1210,22 +1185,23 @@ array_toSource(JSContext *cx, uintN argc
         if (hole) {
             str = cx->runtime->emptyString;
         } else {
             str = js_ValueToSource(cx, *vp);
             if (!str)
                 goto out;
         }
         vp->setString(str);
-        const jschar *chars;
-        size_t charlen;
-        str->getCharsAndLength(chars, charlen);
+
+        const jschar *chars = str->getChars(cx);
+        if (!chars)
+            goto out;
 
         /* Append element to buffer. */
-        if (!cb.append(chars, charlen))
+        if (!cb.append(chars, chars + str->length()))
             goto out;
         if (index + 1 != length) {
             if (!js_AppendLiteral(cb, ", "))
                 goto out;
         } else if (hole) {
             if (!cb.append(','))
                 goto out;
         }
@@ -1249,53 +1225,54 @@ array_toSource(JSContext *cx, uintN argc
 #endif
 
 static JSBool
 array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
                    JSString *sepstr, Value *rval)
 {
     JS_CHECK_RECURSION(cx, return false);
 
+    /* Get characters to use for the separator. */
+    static const jschar comma = ',';
+    const jschar *sep;
+    size_t seplen;
+    if (sepstr) {
+        seplen = sepstr->length();
+        sep = sepstr->getChars(cx);
+        if (!sep)
+            return false;
+    } else {
+        sep = &comma;
+        seplen = 1;
+    }
+
     /*
      * Use HashTable entry as the cycle indicator. On first visit, create the
      * entry, and, when leaving, remove the entry.
      */
     typedef js::HashSet<JSObject *> ObjSet;
     ObjSet::AddPtr hashp = cx->busyArrays.lookupForAdd(obj);
     uint32 genBefore;
     if (!hashp) {
         /* Not in hash table, so not a cycle. */
-        if (!cx->busyArrays.add(hashp, obj)) {
-            JS_ReportOutOfMemory(cx);
+        if (!cx->busyArrays.add(hashp, obj))
             return false;
-        }
         genBefore = cx->busyArrays.generation();
     } else {
         /* Cycle, so return empty string. */
         rval->setString(ATOM_TO_STRING(cx->runtime->atomState.emptyAtom));
         return true;
     }
 
     AutoObjectRooter tvr(cx, obj);
 
     /* After this point, all paths exit through the 'out' label. */
     MUST_FLOW_THROUGH("out");
     bool ok = false;
 
-    /* Get characters to use for the separator. */
-    static const jschar comma = ',';
-    const jschar *sep;
-    size_t seplen;
-    if (sepstr) {
-        sepstr->getCharsAndLength(sep, seplen);
-    } else {
-        sep = &comma;
-        seplen = 1;
-    }
-
     /*
      * This object will take responsibility for the jschar buffer until the
      * buffer is transferred to the returned JSString.
      */
     JSCharBuffer cb(cx);
 
     jsuint length;
     if (!js_GetLengthProperty(cx, obj, &length))
@@ -1787,25 +1764,20 @@ static inline JS_IGNORE_STACK JSComparat
 comparator_stack_cast(JSRedComparator func)
 {
     return func;
 }
 
 static int
 sort_compare_strings(void *arg, const void *a, const void *b, int *result)
 {
-    const Value *av = (const Value *)a, *bv = (const Value *)b;
-
-    JS_ASSERT(av->isString());
-    JS_ASSERT(bv->isString());
-    if (!JS_CHECK_OPERATION_LIMIT((JSContext *)arg))
-        return JS_FALSE;
-
-    *result = (int) js_CompareStrings(av->toString(), bv->toString());
-    return JS_TRUE;
+    JSContext *cx = (JSContext *)arg;
+    JSString *astr = ((const Value *)a)->toString();
+    JSString *bstr = ((const Value *)b)->toString();
+    return JS_CHECK_OPERATION_LIMIT(cx) && CompareStrings(cx, astr, bstr, result);
 }
 
 JSBool
 js::array_sort(JSContext *cx, uintN argc, Value *vp)
 {
     jsuint len, newlen, i, undefs;
     size_t elemsize;
     JSString *str;
@@ -2362,17 +2334,17 @@ array_splice(JSContext *cx, uintN argc, 
         cx->markTypeObjectUnknownProperties(type);
 
     /*
      * 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;
     obj2->setType(type);
     vp->setObject(*obj2);
 
     /* Nothing to do if no args.  Otherwise get length. */
     if (argc == 0)
         return JS_TRUE;
@@ -2416,17 +2388,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;
@@ -2530,56 +2501,60 @@ array_concat(JSContext *cx, uintN argc, 
 
     /* Create a new Array object and root it using *vp. */
     JSObject *aobj = ComputeThisFromVp(cx, vp);
     JSObject *nobj;
     jsuint length;
     if (aobj->isDenseArray()) {
         length = aobj->getArrayLength();
         jsuint initlen = aobj->getDenseArrayInitializedLength();
-        nobj = js_NewArrayObject(cx, initlen, aobj->getDenseArrayElements());
+        nobj = NewDenseCopiedArray(cx, initlen, aobj->getDenseArrayElements());
         if (!nobj)
             return JS_FALSE;
         nobj->setArrayLength(cx, 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;
     }
 
     /* Get the type object to use for the result. */
     TypeObject *ntype = cx->getTypeCallerInitObject(true);
     if (!ntype)
         return JS_FALSE;
+    nobj->setType(ntype);
     if (cx->isTypeCallerMonitored())
         cx->markTypeObjectUnknownProperties(ntype);
 
+    if (aobj->isDenseArray() && !aobj->isPackedDenseArray())
+        nobj->setDenseArrayNotPacked(cx);
+
     AutoValueRooter tvr(cx);
 
     /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
     for (uintN i = 0; i <= argc; i++) {
         if (!JS_CHECK_OPERATION_LIMIT(cx))
             return false;
         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;
                     }
 
@@ -2675,42 +2650,44 @@ array_slice(JSContext *cx, uintN argc, V
         cx->markTypeCallerUnexpected((jstype) type);
     }
 
     if (cx->isTypeCallerMonitored())
         cx->markTypeObjectUnknownProperties(type);
 
     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;
         nobj->setType(type);
+        if (!obj->isPackedDenseArray())
+            nobj->setDenseArrayNotPacked(cx);
         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;
     nobj->setType(type);
     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;
@@ -2760,21 +2737,26 @@ array_indexOfHelper(JSContext *cx, JSBoo
         direction = 1;
     }
 
     for (;;) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetElement(cx, obj, (jsuint)i, &hole, vp)) {
             return JS_FALSE;
         }
-        if (!hole && StrictlyEqual(cx, *vp, tosearch)) {
-            vp->setNumber(i);
-            if (!vp->isInt32())
-                cx->markTypeCallerOverflow();
-            return JS_TRUE;
+        if (!hole) {
+            JSBool equal;
+            if (!StrictlyEqual(cx, *vp, tosearch, &equal))
+                return JS_FALSE;
+            if (equal) {
+                vp->setNumber(i);
+                if (!vp->isInt32())
+                    cx->markTypeCallerOverflow();
+                return JS_TRUE;
+            }
         }
         if (i == stop)
             goto not_found;
         i += direction;
     }
 
   not_found:
     vp->setInt32(-1);
@@ -2865,17 +2847,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;
         newtype = cx->getTypeCallerInitObject(true);
         if (!newtype)
             return JS_FALSE;
         newarr->setType(newtype);
         vp->setObject(*newarr);
         break;
@@ -3118,16 +3100,22 @@ static void array_TypeSplice(JSContext *
 #endif
 }
 
 static void array_TypeConcat(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
 
+    if (!site->compileAndGo()) {
+        if (site->returnTypes)
+            site->returnTypes->addType(cx, TYPE_UNKNOWN);
+        return;
+    }
+
     /* Treat the returned array as a new allocation site. */
     TypeObject *object = site->getInitObject(cx, true);
 
     site->forceThisTypes(cx);
 
     if (site->returnTypes)
         site->returnTypes->addType(cx, (jstype) object);
 
@@ -3200,37 +3188,40 @@ static void array_TypeExtra(JSContext *c
             initialTypes = site->argumentTypes[1];
         else
             initialTypes = elemTypes;
         initialTypes->addSubset(cx, pool, extraSite->argumentTypes[0]);
         initialTypes->addSubset(cx, pool, site->returnTypes);
         break;
       }
 
-      case MAP: {
-        /* Makes a new array whose element type is the return value of the argument function. */
-        TypeObject *object = site->getInitObject(cx, true);
-        extraSite->returnTypes = object->getProperty(cx, JSID_VOID, true);
-
-        site->returnTypes->addType(cx, (jstype) object);
+      case MAP:
+        if (site->compileAndGo()) {
+            /* Makes a new array whose element type is the return value of the argument function. */
+            TypeObject *object = site->getInitObject(cx, true);
+            extraSite->returnTypes = object->getProperty(cx, JSID_VOID, true);
+            site->returnTypes->addType(cx, (jstype) object);
+        } else {
+            site->returnTypes->addType(cx, TYPE_UNKNOWN);
+        }
         break;
-      }
-
-      case FILTER: {
-        /*
-         * Makes a new array, whose element type is the same as the element type of the
-         * 'this' array. TODO: could use the same type information as the 'this' array,
-         * but might run into problems when we're able to handle receiver types other than arrays.
-         */
-        TypeObject *object = site->getInitObject(cx, true);
-        elemTypes->addSubset(cx, pool, object->getProperty(cx, JSID_VOID, true));
-
-        site->returnTypes->addType(cx, (jstype) object);
+
+      case FILTER:
+        if (site->compileAndGo()) {
+            /*
+             * Makes a new array, whose element type is the same as the element type
+             * of the 'this' array.
+             */
+            TypeObject *object = site->getInitObject(cx, true);
+            elemTypes->addSubset(cx, pool, object->getProperty(cx, JSID_VOID, true));
+            site->returnTypes->addType(cx, (jstype) object);
+        } else {
+            site->returnTypes->addType(cx, TYPE_UNKNOWN);
+        }
         break;
-      }
 
       case SOME:
         site->returnTypes->addType(cx, TYPE_BOOLEAN);
         break;
 
       default:
         JS_NOT_REACHED("Unexpected ArrayExtraMode");
     }
@@ -3323,121 +3314,65 @@ static JSFunctionSpec array_methods[] = 
     JS_FS_END
 };
 
 static JSFunctionSpec array_static_methods[] = {
     JS_FN_TYPE("isArray",            array_isArray,      1,0, JS_TypeHandlerBool),
     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;
 
     TypeObject *type = cx->getTypeCallerInitObject(true);
     if (!type)
         return JS_FALSE;
 
     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;
-
         /* Unexpected case for type inference. */
         cx->addTypeProperty(type, NULL, 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);
     }
 
-    if (cx->isTypeCallerMonitored() && vector)
-        cx->markTypeObjectUnknownProperties(type);
-
-    /* Whether called with 'new' or not, use a new Array object. */
-    JSObject *obj = NewDenseArrayObject(cx, length);
     if (!obj)
         return JS_FALSE;
+
     obj->setType(type);
+    if (cx->isTypeCallerMonitored())
+        cx->markTypeObjectUnknownProperties(type);
+
     vp->setObject(*obj);
-
-    return InitArrayObject(cx, obj, length, vector);
+    return JS_TRUE;
 }
 
-JSObject* JS_FASTCALL
-js_NewEmptyArray(JSContext* cx, JSObject* proto, int32 len)
-{
-    if (len < 0)
-        return NULL;
-
-    JS_ASSERT(proto->isArray());
-
-    TypeObject *type = proto->getNewType(cx);
-    if (!type)
-        return NULL;
-
-    gc::FinalizeKind kind = GuessObjectGCKind(len, true);
-    JSObject* obj = js_NewGCObject(cx, kind);
-    if (!obj)
-        return NULL;
-
-    /* Initialize all fields, calling init before setting obj->map. */
-    obj->init(cx, &js_ArrayClass, type, proto->getParent(), (void*) len, true);
-    obj->setSharedNonNativeMap();
-    return obj;
-}
-#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)
-{
-    /* :FIXME: new Arrays do not have the right type when created on trace. */
-    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;
-    ClearValueRange(obj->getDenseArrayElements(), len, true);
-    obj->setDenseArrayInitializedLength(len);
-    obj->setDenseArrayNotPacked(cx);
-    return obj;
-}
-#ifdef JS_TRACER
-JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewPreallocatedArray, CONTEXT, OBJECT, INT32,
-                     0, nanojit::ACCSET_STORE_ANY)
-#endif
-
 // specialized handler for Array() that propagates arguments into indexes
 // of the resulting array.
 static void array_TypeNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
 {
 #ifdef JS_TYPE_INFERENCE
     TypeCallsite *site = Valueify(jssite);
 
+    if (!site->compileAndGo()) {
+        if (site->returnTypes)
+            site->returnTypes->addType(cx, TYPE_UNKNOWN);
+        return;
+    }
+
     TypeObject *object = site->getInitObject(cx, true);
     if (site->returnTypes)
         site->returnTypes->addType(cx, (jstype) object);
 
     TypeSet *indexTypes = object->getProperty(cx, JSID_VOID, true);
 
     // ignore the case where the call is passed a single argument. this is
     // expected to be the array length, but if it isn't we will catch it
@@ -3462,41 +3397,108 @@ js_InitArrayClass(JSContext *cx, JSObjec
     proto->setArrayLength(cx, 0);
 
     /* The default 'new' object for Array.prototype has unknown properties. */
     cx->markTypeObjectUnknownProperties(proto->getNewType(cx));
 
     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);
+    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(cx, length);
+
+    if (allocateCapacity && !obj->ensureSlots(cx, length))
+        return 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
+NewDenseAllocatedEmptyArray(JSContext *cx, uint length, JSObject *proto)
+{
+    JSObject *obj = NewArray<true>(cx, length, proto);
     if (!obj)
         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;
+    obj->setDenseArrayInitializedLength(length);
+    obj->setDenseArrayNotPacked(cx);
+    ClearValueRange(obj->getSlots(), length, true);
+    return obj;
+}
+
+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));
+
+    obj->setDenseArrayInitializedLength(length);
+    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, NewDenseAllocatedEmptyArray, 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(cx, 0);
+    if (!obj)
+        return NULL;
+
+    obj->setArrayLength(cx, 0);
     return obj;
 }
 
+}
+
+
 #ifdef DEBUG
 JSBool
 js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp)
 {
     uintN i;
     JSObject *array;
 
     for (i = 0; i < argc; i++) {
@@ -3634,15 +3636,19 @@ js_CloneDensePrimitiveArray(JSContext *c
              */
             *clone = NULL;
             return JS_TRUE;
         }
 
         vector.append(val);
     }
 
-    *clone = js_NewArrayObject(cx, initlen, vector.begin());
+    *clone = NewDenseCopiedArray(cx, initlen, vector.begin());
     if (!*clone)
         return JS_FALSE;
+
+    if (!obj->isPackedDenseArray())
+        (*clone)->setDenseArrayNotPacked(cx);
+
+    /* The length will be set to the initlen, above, but length might be larger. */
     (*clone)->setArrayLength(cx, length);
-
     return JS_TRUE;
 }
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -40,17 +40,19 @@
 #ifndef jsarray_h___
 #define jsarray_h___
 /*
  * JS Array interface.
  */
 #include "jscntxt.h"
 #include "jsprvtd.h"
 #include "jspubtd.h"
+#include "jsatom.h"
 #include "jsobj.h"
+#include "jsstr.h"
 
 /* Small arrays are dense, no matter what. */
 const uintN MIN_SPARSE_INDEX = 256;
 
 inline uint32
 JSObject::getDenseArrayInitializedLength()
 {
     JS_ASSERT(isDenseArray());
@@ -140,54 +142,64 @@ JSObject::ensureDenseArrayElements(JSCon
         ClearValueRange(getSlots() + initLength, index - initLength, true);
         setDenseArrayNotPacked(cx);
     }
     setDenseArrayInitializedLength(requiredCapacity);
 
     return ED_OK;
 }
 
-extern JSBool
-js_StringIsIndex(JSString *str, jsuint *indexp);
+extern bool
+js_StringIsIndex(JSLinearString *str, jsuint *indexp);
 
 inline JSBool
 js_IdIsIndex(jsid id, jsuint *indexp)
 {
     if (JSID_IS_INT(id)) {
         jsint i;
         i = JSID_TO_INT(id);
         if (i < 0)
             return JS_FALSE;
         *indexp = (jsuint)i;
         return JS_TRUE;
     }
 
     if (JS_UNLIKELY(!JSID_IS_STRING(id)))
         return JS_FALSE;
 
-    return js_StringIsIndex(JSID_TO_STRING(id), indexp);
+    return js_StringIsIndex(JSID_TO_ATOM(id), indexp);
 }
 
 /* XML really wants to pretend jsvals are jsids. */
-inline JSBool
-js_IdValIsIndex(jsval id, jsuint *indexp)
+inline bool
+js_IdValIsIndex(JSContext *cx, jsval id, jsuint *indexp, bool *isIndex)
 {
     if (JSVAL_IS_INT(id)) {
         jsint i;
         i = JSVAL_TO_INT(id);
-        if (i < 0)
-            return JS_FALSE;
+        if (i < 0) {
+            *isIndex = false;
+            return true;
+        }
         *indexp = (jsuint)i;
-        return JS_TRUE;
+        *isIndex = true;
+        return true;
     }
 
-    if (!JSVAL_IS_STRING(id))
-        return JS_FALSE;
+    if (!JSVAL_IS_STRING(id)) {
+        *isIndex = false;
+        return true;
+    }
 
-    return js_StringIsIndex(JSVAL_TO_STRING(id), indexp);
+    JSLinearString *str = JSVAL_TO_STRING(id)->ensureLinear(cx);
+    if (!str)
+        return false;
+
+    *isIndex = js_StringIsIndex(str, indexp);
+    return true;
 }
 
 extern js::Class js_ArrayClass, js_SlowArrayClass;
 
 inline bool
 JSObject::isDenseArray() const
 {
     return getClass() == &js_ArrayClass;
@@ -228,22 +240,51 @@ inline JSObject *
 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', initialized length set to 0. */
+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 length, capacity and initialized length == 'length', and filled with holes.
+ * This is a kludge, as the tracer doesn't yet track/update initialized length when initializing
+ * array elements.
+ */
+extern JSObject * JS_FASTCALL
+NewDenseAllocatedEmptyArray(JSContext *cx, uint length, JSObject *proto=NULL);
+
+/*
+ * 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
@@ -335,35 +376,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/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -452,50 +452,45 @@ js_SweepAtomState(JSContext *cx)
             JS_ASSERT(!IsAboutToBeFinalized(AtomEntryToKey(entry)));
         } else if (IsAboutToBeFinalized(AtomEntryToKey(entry))) {
             e.removeFront();
         }
     }
 }
 
 JSAtom *
-js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
+js_AtomizeString(JSContext *cx, JSString *strArg, uintN flags)
 {
     JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
     JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
 
-    if (str->isAtomized()) {
-        JSAtomState *state = &cx->runtime->atomState;
-        AtomSet &atoms = state->atoms;
-
-        AutoLockDefaultCompartment lock(cx);
-        AtomSet::AddPtr p = atoms.lookupForAdd(str);
+    if (strArg->isAtomized())
+        return STRING_TO_ATOM(strArg);
 
-        AddAtomEntryFlags(*p, flags & (ATOM_PINNED | ATOM_INTERNED));
-        return STRING_TO_ATOM(str);
-    }
+    JSLinearString *str = strArg->ensureLinear(cx);
+    if (!str)
+        return NULL;
 
-    const jschar *chars;
-    size_t length;
-    str->getCharsAndLength(chars, length);
+    const jschar *chars = str->chars();
+    size_t length = str->length();
 
     JSString *staticStr = JSString::lookupStaticString(chars, length);
     if (staticStr)
         return STRING_TO_ATOM(staticStr);
 
     JSAtomState *state = &cx->runtime->atomState;
     AtomSet &atoms = state->atoms;
 
     AutoLockDefaultCompartment lock(cx);
     AtomSet::AddPtr p = atoms.lookupForAdd(str);
 
     /* Hashing the string should have flattened it if it was a rope. */
     JS_ASSERT(str->isFlat() || str->isDependent());
 
-    JSString *key;
+    JSLinearString *key;
     if (p) {
         key = AtomEntryToKey(*p);
     } else {
         /*
          * Ensure that any atomized string lives only in the default
          * compartment.
          */
         bool needNewString = !!(flags & ATOM_TMPSTR) ||
@@ -509,25 +504,24 @@ js_AtomizeString(JSContext *cx, JSString
          */
         if (!needNewString && str->isFlat()) {
             str->flatClearExtensible();
             key = str;
             atoms.add(p, StringToInitialAtomEntry(key));
         } else {
             if (needNewString) {
                 SwitchToCompartment sc(cx, cx->runtime->defaultCompartment);
-                jschar *chars = str->chars();
                 if (flags & ATOM_NOCOPY) {
-                    key = js_NewString(cx, chars, length);
+                    key = js_NewString(cx, const_cast<jschar *>(str->flatChars()), length);
                     if (!key)
                         return NULL;
 
                     /* Finish handing off chars to the GC'ed key string. */
                     JS_ASSERT(flags & ATOM_TMPSTR);
-                    str->mChars = NULL;
+                    str->u.chars = NULL;
                 } else {
                     key = js_NewStringCopyN(cx, chars, length);
                     if (!key)
                         return NULL;
                 }
             } else {
                 JS_ASSERT(str->isDependent());
                 if (!str->undepend(cx))
@@ -540,17 +534,16 @@ js_AtomizeString(JSContext *cx, JSString
                 return NULL;
             }
         }
         key->flatSetAtomized();
     }
 
     AddAtomEntryFlags(*p, flags & (ATOM_PINNED | ATOM_INTERNED));
 
-    JS_ASSERT(key->isAtomized());
     JSAtom *atom = STRING_TO_ATOM(key);
     return atom;
 }
 
 JSAtom *
 js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags)
 {
     jschar *chars;
@@ -590,37 +583,37 @@ js_Atomize(JSContext *cx, const char *by
 }
 
 JSAtom *
 js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags)
 {
     JSString str;
 
     CHECK_REQUEST(cx);
-    str.initFlat((jschar *)chars, length);
+    str.initFlatNotTerminated((jschar *)chars, length);
     return js_AtomizeString(cx, &str, ATOM_TMPSTR | flags);
 }
 
 JSAtom *
 js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
 {
     JSString str, *str2;
     JSAtomState *state;
 
     if (length == 1) {
         jschar c = *chars;
         if (c < UNIT_STRING_LIMIT)
             return STRING_TO_ATOM(JSString::unitString(c));
     }
 
-    str.initFlat((jschar *)chars, length);
+    str.initFlatNotTerminated((jschar *)chars, length);
     state = &cx->runtime->atomState;
 
     JS_LOCK(cx, &state->lock);
-    AtomSet::Ptr p = state->atoms.lookup(&str);
+    AtomSet::Ptr p = state->atoms.lookup(str.assertIsFlat());
     str2 = p ? AtomEntryToKey(*p) : NULL;
     JS_UNLOCK(cx, &state->lock);
 
     return str2 ? STRING_TO_ATOM(str2) : NULL;
 }
 
 #ifdef DEBUG
 JS_FRIEND_API(void)
@@ -631,17 +624,17 @@ js_DumpAtoms(JSContext *cx, FILE *fp)
     fprintf(fp, "atoms table contents:\n");
     unsigned number = 0;
     for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) {
         AtomEntryType entry = r.front();
         fprintf(fp, "%3u ", number++);
         if (entry == 0) {
             fputs("<uninitialized>", fp);
         } else {
-            JSString *key = AtomEntryToKey(entry);
+            JSAtom *key = AtomEntryToKey(entry);
             FileEscapedString(fp, key, '"');
             uintN flags = AtomEntryFlags(entry);
             if (flags != 0) {
                 fputs((flags & (ATOM_PINNED | ATOM_INTERNED))
                       ? " pinned | interned"
                       : (flags & ATOM_PINNED) ? " pinned" : " interned",
                       fp);
             }
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -55,17 +55,17 @@
 
 #define ATOM_PINNED     0x1       /* atom is pinned against GC */
 #define ATOM_INTERNED   0x2       /* pinned variant for JS_Intern* API */
 #define ATOM_NOCOPY     0x4       /* don't copy atom string bytes */
 #define ATOM_TMPSTR     0x8       /* internal, to avoid extra string */
 
 #define STRING_TO_ATOM(str)       (JS_ASSERT(str->isAtomized()),             \
                                    (JSAtom *)str)
-#define ATOM_TO_STRING(atom)      ((JSString *)(atom))
+#define ATOM_TO_STRING(atom)      (atom)
 #define ATOM_TO_JSVAL(atom)       STRING_TO_JSVAL(ATOM_TO_STRING(atom))
 
 /* Engine-internal extensions of jsid */
 
 static JS_ALWAYS_INLINE jsid
 JSID_FROM_BITS(size_t bits)
 {
     jsid id;
@@ -260,33 +260,33 @@ struct JSAtomMap {
 namespace js {
 
 #define ATOM_ENTRY_FLAG_MASK            ((size_t)(ATOM_PINNED | ATOM_INTERNED))
 
 JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JS_GCTHING_ALIGN);
 
 typedef uintptr_t AtomEntryType;
 
-static JS_ALWAYS_INLINE JSString *
+static JS_ALWAYS_INLINE JSAtom *
 AtomEntryToKey(AtomEntryType entry)
 {
     JS_ASSERT(entry != 0);
-    return (JSString *)(entry & ~ATOM_ENTRY_FLAG_MASK);
+    return (JSAtom *)(entry & ~ATOM_ENTRY_FLAG_MASK);
 }
 
 struct AtomHasher
 {
-    typedef JSString *Lookup;
+    typedef JSLinearString *Lookup;
 
-    static HashNumber hash(JSString *str) {
+    static HashNumber hash(JSLinearString *str) {
         return js_HashString(str);
     }
 
-    static bool match(AtomEntryType entry, JSString *lookup) {
-        return entry ? js_EqualStrings(AtomEntryToKey(entry), lookup) : false;
+    static bool match(AtomEntryType entry, JSLinearString *lookup) {
+        return entry ? EqualStrings(AtomEntryToKey(entry), lookup) : false;
     }
 };
 
 typedef HashSet<AtomEntryType, AtomHasher, SystemAllocPolicy> AtomSet;
 
 }  /* namespace js */
 
 struct JSAtomState
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -163,28 +163,32 @@ JS_DEFINE_CALLINFO_1(extern, INT32, js_D
 uint32 FASTCALL
 js_DoubleToUint32(jsdouble d)
 {
     return js_DoubleToECMAUint32(d);
 }
 JS_DEFINE_CALLINFO_1(extern, UINT32, js_DoubleToUint32, DOUBLE, 1, ACCSET_NONE)
 
 jsdouble FASTCALL
-js_StringToNumber(JSContext* cx, JSString* str)
+js_StringToNumber(JSContext* cx, JSString* str, JSBool *ok)
 {
-    return StringToNumberType<jsdouble>(cx, str);
+    double out = 0;  /* silence warnings. */
+    *ok = StringToNumberType<jsdouble>(cx, str, &out);
+    return out;
 }
-JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, 1, ACCSET_NONE)
+JS_DEFINE_CALLINFO_3(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, BOOLPTR, 1, ACCSET_NONE)
 
 int32 FASTCALL
-js_StringToInt32(JSContext* cx, JSString* str)
+js_StringToInt32(JSContext* cx, JSString* str, JSBool *ok)
 {
-    return StringToNumberType<int32>(cx, str);
+    int32 out = 0;  /* silence warnings. */
+    *ok = StringToNumberType<int32>(cx, str, &out);
+    return out;
 }
-JS_DEFINE_CALLINFO_2(extern, INT32, js_StringToInt32, CONTEXT, STRING, 1, ACCSET_NONE)
+JS_DEFINE_CALLINFO_3(extern, INT32, js_StringToInt32, CONTEXT, STRING, BOOLPTR, 1, ACCSET_NONE)
 
 /* Nb: it's always safe to set isDefinitelyAtom to false if you're unsure or don't know. */
 static inline JSBool
 AddPropertyHelper(JSContext* cx, JSObject* obj, Shape* shape, bool isDefinitelyAtom)
 {
     JS_ASSERT(shape->previous() == obj->lastProperty());
 
     if (obj->nativeEmpty()) {
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -542,17 +542,17 @@ struct ClosureVarInfo;
         { _JS_TN_INIT_HELPER_n tn2 | JSTN_MORE },                                                 \
         { _JS_TN_INIT_HELPER_n tn3 }                                                              \
     };                                                                                            \
     JSNativeTraceInfo name##_trcinfo = { JS_VALUEIFY_NATIVE(name), name##_sns };
 
 #define _JS_DEFINE_CALLINFO_n(n, args)  JS_DEFINE_CALLINFO_##n args
 
 jsdouble FASTCALL
-js_StringToNumber(JSContext* cx, JSString* str);
+js_StringToNumber(JSContext* cx, JSString* str, JSBool *ok);
 
 /* Extern version of SetBuiltinError. */
 extern JS_FRIEND_API(void)
 js_SetTraceableNativeFailed(JSContext *cx);
 
 extern jsdouble FASTCALL
 js_dmod(jsdouble a, jsdouble b);
 
@@ -570,18 +570,22 @@ 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(NewDenseAllocatedEmptyArray)
+}
 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)
@@ -615,17 +619,17 @@ JS_DECLARE_CALLINFO(js_Object_tn)
 JS_DECLARE_CALLINFO(js_CreateThisFromTrace)
 JS_DECLARE_CALLINFO(js_InitializerObject)
 
 /* Defined in jsregexp.cpp. */
 JS_DECLARE_CALLINFO(js_CloneRegExpObject)
 
 /* Defined in jsstr.cpp. */
 JS_DECLARE_CALLINFO(js_String_tn)
-JS_DECLARE_CALLINFO(js_CompareStrings)
+JS_DECLARE_CALLINFO(js_CompareStringsOnTrace)
 JS_DECLARE_CALLINFO(js_ConcatStrings)
-JS_DECLARE_CALLINFO(js_EqualStrings)
+JS_DECLARE_CALLINFO(js_EqualStringsOnTrace)
 JS_DECLARE_CALLINFO(js_Flatten)
 
 /* Defined in jstypedarray.cpp. */
 JS_DECLARE_CALLINFO(js_TypedArray_uint8_clamp_double)
 
 #endif /* jsbuiltins_h___ */
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -345,19 +345,20 @@ SCOutput::extractBuffer(uint64_t **datap
     return (*datap = buf.extractRawBuffer()) != NULL;
 }
 
 JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
 
 bool
 JSStructuredCloneWriter::writeString(uint32_t tag, JSString *str)
 {
-    const jschar *chars;
-    size_t length;
-    str->getCharsAndLength(chars, length);
+    size_t length = str->length();
+    const jschar *chars = str->getChars(context());
+    if (!chars)
+        return false;
     return out.writePair(tag, uint32_t(length)) && out.writeChars(chars, length);
 }
 
 bool
 JSStructuredCloneWriter::writeId(jsid id)
 {
     if (JSID_IS_INT(id))
         return out.writePair(SCTAG_INDEX, uint32_t(JSID_TO_INT(id)));
@@ -599,19 +600,24 @@ class Chars {
     JSContext *cx;
     jschar *p;
   public:
     Chars() : p(NULL) {}
     ~Chars() { if (p) cx->free(p); }
 
     bool allocate(JSContext *cx, size_t len) {
         JS_ASSERT(!p);
-        p = (jschar *) cx->malloc(len * sizeof(jschar));
+        // We're going to null-terminate!
+        p = (jschar *) cx->malloc((len + 1) * sizeof(jschar));
         this->cx = cx;
-        return p != NULL;
+        if (p) {
+            p[len] = jschar(0);
+            return true;
+        }
+        return false;
     }
     jschar *get() { return p; }
     void forget() { p = NULL; }
 };
 
 JSString *
 JSStructuredCloneReader::readString(uint32_t nchars)
 {
@@ -740,36 +746,32 @@ JSStructuredCloneReader::startRead(Value
         if (tag2 != SCTAG_STRING) {
             JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
                                  "regexp");
             return false;
         }
         JSString *str = readString(nchars);
         if (!str)
             return false;
-        const jschar *chars;
-        size_t length;
-        str->getCharsAndLength(chars, length);
+        size_t length = str->length();
+        const jschar *chars = str->getChars(context());
+        if (!chars)
+            return false;
         JSObject *obj = RegExp::createObjectNoStatics(context(), chars, length, data);
         if (!obj)
             return false;
         vp->setObject(*obj);
         break;
       }
 
-      case SCTAG_ARRAY_OBJECT: {
-        JSObject *obj = js_NewArrayObject(context(), 0, NULL);
-        if (!obj || !objs.append(ObjectValue(*obj)))
-            return false;
-        vp->setObject(*obj);
-        break;
-      }
-
+      case SCTAG_ARRAY_OBJECT:
       case SCTAG_OBJECT_OBJECT: {
-        JSObject *obj = NewBuiltinClassInstance(context(), &js_ObjectClass);
+        JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
+                        ? NewDenseEmptyArray(context())
+                        : NewBuiltinClassInstance(context(), &js_ObjectClass);
         if (!obj || !objs.append(ObjectValue(*obj)))
             return false;
         vp->setObject(*obj);
         break;
       }
 
       case SCTAG_ARRAY_BUFFER_OBJECT:
         return readArrayBuffer(data, vp);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1317,16 +1317,17 @@ struct JSRuntime {
     size_t              gcTriggerBytes;
     size_t              gcLastBytes;
     size_t              gcMaxBytes;
     size_t              gcMaxMallocBytes;
     uint32              gcEmptyArenaPoolLifespan;
     uint32              gcNumber;
     js::GCMarker        *gcMarkingTracer;
     uint32              gcTriggerFactor;
+    int64               gcJitReleaseTime;
     volatile JSBool     gcIsNeeded;
 
     /*
      * We can pack these flags as only the GC thread writes to them. Atomic
      * updates to packed bytes are not guaranteed, so stores issued by one
      * thread may be lost due to unsynchronized read-modify-write cycles on
      * other threads.
      */
@@ -1364,17 +1365,17 @@ struct JSRuntime {
     JSTraceDataOp       gcExtraRootsTraceOp;
     void                *gcExtraRootsData;
 
     /* Well-known numbers held for use by this runtime's contexts. */
     js::Value           NaNValue;
     js::Value           negativeInfinityValue;
     js::Value           positiveInfinityValue;
 
-    JSString            *emptyString;
+    JSFlatString        *emptyString;
 
     /* List of active contexts sharing this runtime; protected by gcLock. */
     JSCList             contextList;
 
     /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */
     JSDebugHooks        globalDebugHooks;
 
     /*
@@ -1651,16 +1652,23 @@ struct JSRuntime {
      * reporting OOM error when cx is not null.
      */
     void* calloc(size_t bytes, JSContext *cx = NULL) {
         updateMallocCounter(bytes);
         void *p = ::js_calloc(bytes);
         return JS_LIKELY(!!p) ? p : onOutOfMemory(reinterpret_cast<void *>(1), bytes, cx);
     }
 
+    void* realloc(void* p, size_t oldBytes, size_t newBytes, JSContext *cx = NULL) {
+        JS_ASSERT(oldBytes < newBytes);
+        updateMallocCounter(newBytes - oldBytes);
+        void *p2 = ::js_realloc(p, newBytes);
+        return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, newBytes, cx);
+    }
+
     void* realloc(void* p, size_t bytes, JSContext *cx = NULL) {
         /*
          * For compatibility we do not account for realloc that increases
          * previously allocated memory.
          */
         if (!p)
             updateMallocCounter(bytes);
         void *p2 = ::js_realloc(p, bytes);
@@ -2290,16 +2298,20 @@ struct JSContext
         JS_ASSERT(bytes != 0);
         return runtime->calloc(bytes, this);
     }
 
     inline void* realloc(void* p, size_t bytes) {
         return runtime->realloc(p, bytes, this);
     }
 
+    inline void* realloc(void* p, size_t oldBytes, size_t newBytes) {
+        return runtime->realloc(p, oldBytes, newBytes, this);
+    }
+
     inline void free(void* p) {
 #ifdef JS_THREADSAFE
         if (gcBackgroundFree) {
             gcBackgroundFree->freeLater(p);
             return;
         }
 #endif
         runtime->free(p);
@@ -2370,20 +2382,20 @@ struct JSContext
         DOLLAR_3,
         DOLLAR_4,
         DOLLAR_5,
         DOLLAR_OTHER
     };
 #ifdef XP_WIN
     volatile DollarPath *dollarPath;
     volatile JSSubString *sub;
-    volatile jschar *blackBox;
-    volatile jschar **repstrChars;
-    volatile jschar **repstrDollar;
-    volatile jschar **repstrDollarEnd;
+    volatile const jschar *blackBox;
+    volatile const jschar **repstrChars;
+    volatile const jschar **repstrDollar;
+    volatile const jschar **repstrDollarEnd;
     volatile size_t *peekLen;
 #endif
 
 private:
 
     /*
      * The allocation code calls the function to indicate either OOM failure
      * when p is null or that a memory pressure counter has reached some
--- 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/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -49,17 +49,17 @@
 #include "methodjit/MonoIC.h"
 
 #include "jsgcinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 JSCompartment::JSCompartment(JSRuntime *rt)
-  : rt(rt), principals(NULL), data(NULL), marked(false), debugMode(rt->debugMode),
+  : rt(rt), principals(NULL), data(NULL), marked(false), active(false), debugMode(rt->debugMode),
     anynameObject(NULL), functionNamespaceObject(NULL)
 {
     JS_INIT_CLIST(&scripts);
 }
 
 JSCompartment::~JSCompartment()
 {
 #ifdef JS_METHODJIT
@@ -200,17 +200,20 @@ JSCompartment::wrap(JSContext *cx, Value
         if (vp->isObject())
             vp->toObject().setParent(global);
         return true;
     }
 
     if (vp->isString()) {
         Value orig = *vp;
         JSString *str = vp->toString();
-        JSString *wrapped = js_NewStringCopyN(cx, str->chars(), str->length());
+        const jschar *chars = str->getChars(cx);
+        if (!chars)
+            return false;
+        JSString *wrapped = js_NewStringCopyN(cx, chars, str->length());
         if (!wrapped)
             return false;
         vp->setString(wrapped);
         return crossCompartmentWrappers.put(orig, *vp);
     }
 
     JSObject *obj = &vp->toObject();
 
@@ -323,42 +326,91 @@ JSCompartment::wrapException(JSContext *
             cx->throwing = true;
             cx->exception = tvr.value();
         }
         return false;
     }
     return true;
 }
 
+/*
+ * Check if the pool containing the code for jit should be destroyed, per the
+ * heuristics in JSCompartment::sweep.
+ */
+static inline bool
+ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit,
+                    uint32 releaseInterval, uint32 &counter)
+{
+    JSC::ExecutablePool *pool = jit->code.m_executablePool;
+    if (pool->m_gcNumber != cx->runtime->gcNumber) {
+        /*
+         * The m_destroy flag may have been set in a previous GC for a pool which had
+         * references we did not remove (e.g. from the compartment's ExecutableAllocator)
+         * and is still around. Forget we tried to destroy it in such cases.
+         */
+        pool->m_destroy = false;
+        pool->m_gcNumber = cx->runtime->gcNumber;
+        if (--counter == 0) {
+            pool->m_destroy = true;
+            counter = releaseInterval;
+        }
+    }
+    return pool->m_destroy;
+}
+
 void
-JSCompartment::sweep(JSContext *cx)
+JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
 {
     chunk = NULL;
     /* Remove dead wrappers from the table. */
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
         JS_ASSERT_IF(IsAboutToBeFinalized(e.front().key.toGCThing()) &&
                      !IsAboutToBeFinalized(e.front().value.toGCThing()),
                      e.front().key.isString());
         if (IsAboutToBeFinalized(e.front().key.toGCThing()) ||
             IsAboutToBeFinalized(e.front().value.toGCThing())) {
             e.removeFront();
         }
     }
 
+    /*
+     * The release interval is the frequency with which we should try to destroy
+     * executable pools by releasing all JIT code in them, zero to never destroy pools.
+     * Initialize counter so that the first pool will be destroyed, and eventually drive
+     * the amount of JIT code in never-used compartments to zero. Don't discard anything
+     * for compartments which currently have active stack frames.
+     */
+    uint32 counter = 1;
+    bool discardScripts = !active && releaseInterval != 0;
+
     for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
         JSScript *script = reinterpret_cast<JSScript *>(cursor);
-#if defined JS_METHODJIT && defined JS_MONOIC
-        if (script->hasJITCode())
-            mjit::ic::SweepCallICs(script);
-#endif
         if (script->analysis)
             script->analysis->sweep(cx);
+
+#if defined JS_METHODJIT && defined JS_MONOIC
+        if (script->hasJITCode()) {
+            mjit::ic::SweepCallICs(script, discardScripts);
+            if (discardScripts) {
+                if (script->jitNormal &&
+                    ScriptPoolDestroyed(cx, script->jitNormal, releaseInterval, counter)) {
+                    mjit::ReleaseScriptCode(cx, script);
+                    continue;
+                }
+                if (script->jitCtor &&
+                    ScriptPoolDestroyed(cx, script->jitCtor, releaseInterval, counter)) {
+                    mjit::ReleaseScriptCode(cx, script);
+                }
+            }
+        }
+#endif
     }
 
     types.sweep(cx);
+    active = false;
 }
 
 void
 JSCompartment::purge(JSContext *cx)
 {
     freeLists.purge();
 
 #ifdef JS_METHODJIT
@@ -366,18 +418,18 @@ JSCompartment::purge(JSContext *cx)
          &script->links != &scripts;
          script = (JSScript *)script->links.next) {
         if (script->hasJITCode()) {
 # if defined JS_POLYIC
             mjit::ic::PurgePICs(cx, script);
 # endif
 # if defined JS_MONOIC
             /*
-             * MICs do not refer to data which can be GC'ed, but are sensitive
-             * to shape regeneration.
+             * MICs do not refer to data which can be GC'ed and do not generate stubs
+             * which might need to be discarded, but are sensitive to shape regeneration.
              */
             if (cx->runtime->gcRegenShapes)
                 mjit::ic::PurgeMICs(cx, script);
 # endif
         }
     }
 #endif
 }
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -72,16 +72,17 @@ struct JS_FRIEND_API(JSCompartment) {
     js::gc::JSGCArenaStats       compartmentStats[js::gc::FINALIZE_LIMIT];
 #endif
 
     /* Type information about the scripts and objects in this compartment. */
     js::types::TypeCompartment   types;
 
     void                         *data;
     bool                         marked;
+    bool                         active;  // GC flag, whether there are active frames
     js::WrapperMap               crossCompartmentWrappers;
 
 #ifdef JS_METHODJIT
     js::mjit::JaegerCompartment  *jaegerCompartment;
 #endif
 
     bool                         debugMode;  // true iff debug mode on
     JSCList                      scripts;    // scripts in this compartment
@@ -105,17 +106,17 @@ struct JS_FRIEND_API(JSCompartment) {
     bool wrap(JSContext *cx, JSString **strp);
     bool wrap(JSContext *cx, JSObject **objp);
     bool wrapId(JSContext *cx, jsid *idp);
     bool wrap(JSContext *cx, js::PropertyOp *op);
     bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
     bool wrap(JSContext *cx, js::AutoIdVector &props);
     bool wrapException(JSContext *cx);
 
-    void sweep(JSContext *cx);
+    void sweep(JSContext *cx, uint32 releaseInterval);
     void purge(JSContext *cx);
     void finishArenaLists();
     bool arenaListsAreEmpty();
 };
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -747,17 +747,17 @@ ndigits(size_t n, size_t *result, const 
  *   hh   = two digits of hour (00 through 23) (am/pm NOT allowed)
  *   mm   = two digits of minute (00 through 59)
  *   ss   = two digits of second (00 through 59)
  *   s    = one or more digits representing a decimal fraction of a second
  *   TZD  = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
  */
 
 static JSBool
-date_parseISOString(JSString *str, jsdouble *result, JSContext *cx)
+date_parseISOString(JSLinearString *str, jsdouble *result, JSContext *cx)
 {
     jsdouble msec;
 
     const jschar *s;
     size_t limit;
     size_t i = 0;
     int tzMul = 1;
     int dateMul = 1;
@@ -789,17 +789,18 @@ date_parseISOString(JSString *str, jsdou
         if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
     JS_END_MACRO 
 
 #define NEED_NDIGITS(n, field)                                      \
     JS_BEGIN_MACRO                                                  \
         if (!ndigits(n, &field, s, &i, limit)) { goto syntax; }     \
     JS_END_MACRO 
 
-    str->getCharsAndLength(s, limit);
+    s = str->chars();
+    limit = str->length();
 
     if (PEEK('+') || PEEK('-')) {
         if (PEEK('-'))
             dateMul = -1;
         ++i;
         NEED_NDIGITS(6, year);
     } else if (!PEEK('T')) {
         NEED_NDIGITS(4, year);
@@ -880,17 +881,17 @@ date_parseISOString(JSString *str, jsdou
 
 #undef PEEK
 #undef NEED
 #undef DONE_UNLESS
 #undef NEED_NDIGITS
 }
 
 static JSBool
-date_parseString(JSString *str, jsdouble *result, JSContext *cx)
+date_parseString(JSLinearString *str, jsdouble *result, JSContext *cx)
 {
     jsdouble msec;
 
     const jschar *s;
     size_t limit;
     size_t i = 0;
     int year = -1;
     int mon = -1;
@@ -904,17 +905,18 @@ date_parseString(JSString *str, jsdouble
     int prevc = 0;
     JSBool seenplusminus = JS_FALSE;
     int temp;
     JSBool seenmonthname = JS_FALSE;
 
     if (date_parseISOString(str, result, cx))
         return JS_TRUE;
 
-    str->getCharsAndLength(s, limit);
+    s = str->chars();
+    limit = str->length();
     if (limit == 0)
         goto syntax;
     while (i < limit) {
         c = s[i];
         i++;
         if (c <= ' ' || c == ',' || c == '-') {
             if (c == '-' && '0' <= s[i] && s[i] <= '9') {
               prevc = c;
@@ -1164,17 +1166,21 @@ date_parse(JSContext *cx, uintN argc, Va
     if (argc == 0) {
         vp->setDouble(js_NaN);
         return true;
     }
     str = js_ValueToString(cx, vp[2]);
     if (!str)
         return JS_FALSE;
     vp[2].setString(str);
-    if (!date_parseString(str, &result, cx)) {
+    JSLinearString *linearStr = str->ensureLinear(cx);
+    if (!linearStr)
+        return false;
+
+    if (!date_parseString(linearStr, &result, cx)) {
         vp->setDouble(js_NaN);
         return true;
     }
 
     result = TIMECLIP(result);
     vp->setNumber(result);
     return true;
 }
@@ -2385,21 +2391,20 @@ date_toSource(JSContext *cx, uintN argc,
     }
 
     bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
     if (!bytes) {
         JS_ReportOutOfMemory(cx);
         return JS_FALSE;
     }
 
-    str = JS_NewString(cx, bytes, strlen(bytes));
-    if (!str) {
-        js_free(bytes);
+    str = JS_NewStringCopyZ(cx, bytes);
+    js_free(bytes);
+    if (!str)
         return JS_FALSE;
-    }
     vp->setString(str);
     return JS_TRUE;
 }
 #endif
 
 static JSBool
 date_toString(JSContext *cx, uintN argc, Value *vp)
 {
@@ -2408,33 +2413,34 @@ date_toString(JSContext *cx, uintN argc,
     if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &utctime))
         return JS_FALSE;
     return date_format(cx, utctime, FORMATSPEC_FULL, vp);
 }
 
 static JSBool
 date_valueOf(JSContext *cx, uintN argc, Value *vp)
 {
-    JSString *str, *number_str;
-
     /* It is an error to call date_valueOf on a non-date object, but we don't
      * need to check for that explicitly here because every path calls
      * GetUTCTime, which does the check.
      */
 
     /* If called directly with no arguments, convert to a time number. */
     if (argc == 0)
         return date_getTime(cx, argc, vp);
 
     /* Convert to number only if the hint was given, otherwise favor string. */
-    str = js_ValueToString(cx, vp[2]);
+    JSString *str = js_ValueToString(cx, vp[2]);
     if (!str)
         return JS_FALSE;
-    number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
-    if (js_EqualStrings(str, number_str))
+    JSLinearString *linear_str = str->ensureLinear(cx);
+    if (!linear_str)
+        return JS_FALSE;
+    JSAtom *number_str = cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER];
+    if (EqualStrings(linear_str, number_str))
         return date_getTime(cx, argc, vp);
     return date_toString(cx, argc, vp);
 }
 
 // Don't really need an argument here, but we don't support arg-less builtins
 JS_DEFINE_TRCINFO_1(date_now,
     (1, (static, DOUBLE, date_now_tn, CONTEXT, 0, nanojit::ACCSET_STORE_ANY)))
 
@@ -2518,18 +2524,21 @@ js_Date(JSContext *cx, uintN argc, Value
                 return false;
             d = TIMECLIP(d);
         } else {
             /* the argument is a string; parse it. */
             JSString *str = js_ValueToString(cx, argv[0]);
             if (!str)
                 return false;
             argv[0].setString(str);
+            JSLinearString *linearStr = str->ensureLinear(cx);
+            if (!linearStr)
+                return false;
 
-            if (!date_parseString(str, &d, cx))
+            if (!date_parseString(linearStr, &d, cx))
                 d = js_NaN;
             else
                 d = TIMECLIP(d);
         }
     } else {
         jsdouble msec_time;
         if (!date_msecFromArgs(cx, argc, argv, &msec_time))
             return false;
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -141,17 +141,17 @@ PurgeCallICs(JSContext *cx, JSScript *st
 JS_FRIEND_API(JSBool)
 js_SetDebugMode(JSContext *cx, JSBool debug)
 {
     cx->compartment->debugMode = debug;
 #ifdef JS_METHODJIT
     for (JSScript *script = (JSScript *)cx->compartment->scripts.next;
          &script->links != &cx->compartment->scripts;
          script = (JSScript *)script->links.next) {
-        if (script->debugMode != (bool) debug &&
+        if (script->debugMode != !!debug &&
             script->hasJITCode() &&
             !IsScriptLive(cx, script)) {
             /*
              * In the event that this fails, debug mode is left partially on,
              * leading to a small performance overhead but no loss of
              * correctness. We set the debug flag to false so that the caller
              * will not later attempt to use debugging features.
              */
@@ -178,32 +178,65 @@ JS_SetDebugMode(JSContext *cx, JSBool de
 #ifdef DEBUG
     for (AllFramesIter i(cx); !i.done(); ++i)
         JS_ASSERT(!JS_IsScriptFrame(cx, i.fp()));
 #endif
 
     return js_SetDebugMode(cx, debug);
 }
 
+JS_FRIEND_API(JSBool)
+js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
+{
+    if (!script->singleStepMode == !singleStep)
+        return JS_TRUE;
+
+    JS_ASSERT_IF(singleStep, cx->compartment->debugMode);
+
+#ifdef JS_METHODJIT
+    /* request the next recompile to inject single step interrupts */
+    script->singleStepMode = !!singleStep;
+
+    js::mjit::JITScript *jit = script->jitNormal ? script->jitNormal : script->jitCtor;
+    if (jit && script->singleStepMode != jit->singleStepMode) {
+        js::mjit::Recompiler recompiler(cx, script);
+        if (!recompiler.recompile()) {
+            script->singleStepMode = !singleStep;
+            return JS_FALSE;
+        }
+    }
+#endif
+    return JS_TRUE;
+}
+
 static JSBool
 CheckDebugMode(JSContext *cx)
 {
     JSBool debugMode = JS_GetDebugMode(cx);
     /*
      * :TODO:
      * This probably should be an assertion, since it's indicative of a severe
      * API misuse.
      */
     if (!debugMode) {
         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
                                      NULL, JSMSG_NEED_DEBUG_MODE);
     }
     return debugMode;
 }
 
+JS_PUBLIC_API(JSBool)
+JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
+{
+    if (!CheckDebugMode(cx))
+        return JS_FALSE;
+
+    return js_SetSingleStepMode(cx, script, singleStep);
+}
+
 /*
  * NB: FindTrap must be called with rt->debuggerLock acquired.
  */
 static JSTrap *
 FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
 {
     JSTrap *trap;
 
@@ -261,22 +294,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);
@@ -1759,35 +1786,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;
@@ -1999,25 +2026,23 @@ js_StopCallgrind(JSContext *cx, uintN ar
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return JS_TRUE;
 }
 
 JS_FRIEND_API(JSBool)
 js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp)
 {
     JSString *str;
-    char *cstr;
 
     jsval *argv = JS_ARGV(cx, vp);
     if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
         str = JSVAL_TO_STRING(argv[0]);
-        cstr = js_DeflateString(cx, str->chars(), str->length());
-        if (cstr) {
-            CALLGRIND_DUMP_STATS_AT(cstr);
-            cx->free(cstr);
+        JSAutoByteString bytes(cx, str);
+        if (!!bytes) {
+            CALLGRIND_DUMP_STATS_AT(bytes.ptr());
             return JS_TRUE;
         }
     }
     CALLGRIND_DUMP_STATS;
 
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return JS_TRUE;
 }
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -73,16 +73,24 @@ JS_GetDebugMode(JSContext *cx);
 /* Turn on debugging mode, ignoring the presence of live frames. */
 extern JS_FRIEND_API(JSBool)
 js_SetDebugMode(JSContext *cx, JSBool debug);
 
 /* Turn on debugging mode. */
 extern JS_PUBLIC_API(JSBool)
 JS_SetDebugMode(JSContext *cx, JSBool debug);
 
+/* Turn on single step mode. Requires debug mode. */
+extern JS_FRIEND_API(JSBool)
+js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep);
+
+/* Turn on single step mode. */
+extern JS_PUBLIC_API(JSBool)
+JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep);
+
 /*
  * Unexported library-private helper used to unpatch all traps in a script.
  * Returns script->code if script has no traps, else a JS_malloc'ed copy of
  * script->code which the caller must JS_free, or null on JS_malloc OOM.
  */
 extern jsbytecode *
 js_UntrapScriptCode(JSContext *cx, JSScript *script);
 
--- 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:
@@ -2236,16 +2230,37 @@ BindNameToSlot(JSContext *cx, JSCodeGene
              * and the scope chain matches the function's variable object.
              * Optimize access to function's arguments and variable and the
              * arguments object.
              */
             if (op != JSOP_NAME)
                 return JS_TRUE;
 
             /*
+             * It is illegal to add upvars to heavyweight functions (and
+             * unnecessary, since the optimization avoids creating call
+             * objects). Take the following code as an eval string:
+             *
+             *   (function () {
+             *       $(init);
+             *       function init() {
+             *           $();
+             *       }
+             *    })();
+             *
+             * The first instance of "$" cannot be an upvar, because the
+             * outermost lambda is on "init"'s scope chain, which escapes.
+             *
+             * A similar restriction exists for upvars which do not cross
+             * eval (see the end of BindNameToSlot and bug 616762).
+             */
+            if (cg->flags & TCF_FUN_HEAVYWEIGHT)
+                return JS_TRUE;
+
+            /*
              * Generator functions may be resumed from any call stack, which
              * defeats the display optimization to static link searching used
              * by JSOP_{GET,CALL}UPVAR.
              */
             JSFunction *fun = cg->parser->callerFrame->fun();
             JS_ASSERT(cg->staticLevel >= fun->u.i.script->staticLevel);
             unsigned skip = cg->staticLevel - fun->u.i.script->staticLevel;
             if (cg->skipSpansGenerator(skip))
@@ -2327,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.
          */
@@ -2407,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;
             }
 
@@ -2429,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;
@@ -4575,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) {
@@ -4599,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/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -609,20 +609,21 @@ StackTraceToString(JSContext *cx, JSExnP
             stackbuf = (jschar *) ptr_;                                       \
         }                                                                     \
         stackbuf[stacklen++] = (c);                                           \
     JS_END_MACRO
 
 #define APPEND_STRING_TO_STACK(str)                                           \
     JS_BEGIN_MACRO                                                            \
         JSString *str_ = str;                                                 \
-        const jschar *chars_;                                                 \
-        size_t length_;                                                       \
+        size_t length_ = str_->length();                                      \
+        const jschar *chars_ = str_->getChars(cx);                            \
+        if (!chars_)                                                          \
+            goto bad;                                                         \
                                                                               \
-        str_->getCharsAndLength(chars_, length_);                             \
         if (length_ > stackmax - stacklen) {                                  \
             void *ptr_;                                                       \
             if (stackmax >= STACK_LENGTH_LIMIT ||                             \
                 length_ >= STACK_LENGTH_LIMIT - stacklen) {                   \
                 goto done;                                                    \
             }                                                                 \
             stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_));            \
             ptr_ = cx->realloc(stackbuf, (stackmax+1) * sizeof(jschar));      \
@@ -810,21 +811,27 @@ exn_toString(JSContext *cx, uintN argc, 
         name_length = name->length();
         message_length = message->length();
         length = (name_length ? name_length + 2 : 0) + message_length;
         cp = chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
         if (!chars)
             return JS_FALSE;
 
         if (name_length) {
-            js_strncpy(cp, name->chars(), name_length);
+            const jschar *name_chars = name->getChars(cx);
+            if (!name_chars)
+                return JS_FALSE;
+            js_strncpy(cp, name_chars, name_length);
             cp += name_length;
             *cp++ = ':'; *cp++ = ' ';
         }
-        js_strncpy(cp, message->chars(), message_length);
+        const jschar *message_chars = message->getChars(cx);
+        if (!message_chars)
+            return JS_FALSE;
+        js_strncpy(cp, message_chars, message_length);
         cp += message_length;
         *cp = 0;
 
         result = js_NewString(cx, chars, length);
         if (!result) {
             cx->free(chars);
             return JS_FALSE;
         }
@@ -914,42 +921,54 @@ exn_toSource(JSContext *cx, uintN argc, 
             }
         }
 
         cp = chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
         if (!chars)
             return false;
 
         *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' ';
-        js_strncpy(cp, name->chars(), name_length);
+        const jschar *name_chars = name->getChars(cx);
+        if (!name_chars)
+            return false;
+        js_strncpy(cp, name_chars, name_length);
         cp += name_length;
         *cp++ = '(';
+        const jschar *message_chars = message->getChars(cx);
+        if (!message_chars)
+            return false;
         if (message_length != 0) {
-            js_strncpy(cp, message->chars(), message_length);
+            js_strncpy(cp, message_chars, message_length);
             cp += message_length;
         }
 
         if (filename_length != 0) {
             /* append filename as ``, {filename}'' */
             *cp++ = ','; *cp++ = ' ';
-            js_strncpy(cp, filename->chars(), filename_length);
+            const jschar *filename_chars = filename->getChars(cx);
+            if (!filename_chars)
+                return false;
+            js_strncpy(cp, filename_chars, filename_length);
             cp += filename_length;
         } else {
             if (lineno_as_str) {
                 /*
                  * no filename, but have line number,
                  * need to append ``, "", {lineno_as_str}''
                  */
                 *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"';
             }
         }
         if (lineno_as_str) {
             /* append lineno as ``, {lineno_as_str}'' */
             *cp++ = ','; *cp++ = ' ';
-            js_strncpy(cp, lineno_as_str->chars(), lineno_length);
+            const jschar *lineno_chars = lineno_as_str->getChars(cx);
+            if (!lineno_chars)
+                return false;
+            js_strncpy(cp, lineno_chars, lineno_length);
             cp += lineno_length;
         }
 
         *cp++ = ')'; *cp++ = ')'; *cp = 0;
 
         result = js_NewString(cx, chars, length);
         if (!result) {
             cx->free(chars);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -392,56 +392,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);
@@ -606,37 +606,40 @@ ArgSetter(JSContext *cx, JSObject *obj, 
 
 static JSBool
 args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
              JSObject **objp)
 {
     JS_ASSERT(obj->isNormalArguments());
 
     *objp = NULL;
-    bool valid = false;
-    uintN attrs = JSPROP_SHARED;
+
+    uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
     if (JSID_IS_INT(id)) {
         uint32 arg = uint32(JSID_TO_INT(id));
-        attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
-        if (arg < obj->getArgsInitialLength() && !obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
-            valid = true;
+        if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
+            return true;
+
+        attrs |= JSPROP_ENUMERATE;
     } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
-        if (!obj->isArgsLengthOverridden())
-            valid = true;
-    } else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
-        if (!obj->getArgsCallee().isMagic(JS_ARGS_HOLE))
-            valid = true;
+        if (obj->isArgsLengthOverridden())
+            return true;
+    } else {
+        if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom))
+            return true;
+
+        if (obj->getArgsCallee().isMagic(JS_ARGS_HOLE))
+            return true;
     }
 
-    if (valid) {
-        Value tmp = UndefinedValue();
-        if (!js_DefineProperty(cx, obj, id, &tmp, ArgGetter, ArgSetter, attrs))
-            return JS_FALSE;
-        *objp = obj;
-    }
+    Value undef = UndefinedValue();
+    if (!js_DefineProperty(cx, obj, id, &undef, ArgGetter, ArgSetter, attrs))
+        return JS_FALSE;
+
+    *objp = obj;
     return true;
 }
 
 static JSBool
 args_enumerate(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNormalArguments());
 
@@ -715,57 +718,45 @@ StrictArgSetter(JSContext *cx, JSObject 
 }
 
 static JSBool
 strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
 {
     JS_ASSERT(obj->isStrictArguments());
 
     *objp = NULL;
-    bool valid = false;
-    uintN attrs = JSPROP_SHARED;
+
+    uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
+    PropertyOp getter = StrictArgGetter;
+    PropertyOp setter = StrictArgSetter;
+
     if (JSID_IS_INT(id)) {
         uint32 arg = uint32(JSID_TO_INT(id));
-        attrs = JSPROP_SHARED | JSPROP_ENUMERATE;
-        if (arg < obj->getArgsInitialLength() && !obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
-            valid = true;
+        if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
+            return true;
+
+        attrs |= JSPROP_ENUMERATE;
     } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
-        if (!obj->isArgsLengthOverridden())
-            valid = true;
-    } else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
-        Value tmp = UndefinedValue();
-        PropertyOp throwTypeError = CastAsPropertyOp(obj->getThrowTypeError());
-        uintN attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
-        if (!js_DefineProperty(cx, obj, id, &tmp, throwTypeError, throwTypeError, attrs))
-            return false;
-
-        *objp = obj;
-        return true;
-    } else if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
-        /*
-         * Strict mode arguments objects have an immutable poison-pill caller
-         * property that throws a TypeError on getting or setting.
-         */
-        PropertyOp throwTypeError = CastAsPropertyOp(obj->getThrowTypeError());
-        Value tmp = UndefinedValue();
-        if (!js_DefineProperty(cx, obj, id, &tmp, throwTypeError, throwTypeError,
-                               JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED)) {
-            return false;
+        if (obj->isArgsLengthOverridden())
+            return true;
+    } else {
+        if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) &&
+            !JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
+            return true;
         }
 
-        *objp = obj;
-        return true;
+        attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
+        getter = setter = CastAsPropertyOp(obj->getThrowTypeError());
     }
 
-    if (valid) {
-        Value tmp = UndefinedValue();
-        if (!js_DefineProperty(cx, obj, id, &tmp, StrictArgGetter, StrictArgSetter, attrs))
-            return false;
-        *objp = obj;
-    }
+    Value undef = UndefinedValue();
+    if (!js_DefineProperty(cx, obj, id, &undef, getter, setter, attrs))
+        return false;
+
+    *objp = obj;
     return true;
 }
 
 static JSBool
 strictargs_enumerate(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isStrictArguments());
 
@@ -1709,20 +1700,16 @@ fun_resolve(JSContext *cx, JSObject *obj
         /*
          * Assert that fun is not a compiler-created function object, which
          * must never leak to script or embedding code and then be mutated.
          * Also assert that obj is not bound, per the ES5 15.3.4.5 ref above.
          */
         JS_ASSERT(!IsInternalFunctionObject(obj));
         JS_ASSERT(!obj->isBoundFunction());
 
-        /* No need to reflect fun.prototype in 'fun.prototype = ... '. */
-        if (flags & JSRESOLVE_ASSIGNING)
-            return true;
-
         /*
          * Make the prototype object an instance of Object with the same parent
          * as the function object itself.
          */
         JSObject *parent = obj->getParent();
         JSObject *objProto;
         if (!js_GetClassPrototype(cx, parent, JSProto_Object, &objProto))
             return false;
@@ -1979,29 +1966,26 @@ 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);
-        }
-
         cx->setTypeFunctionScript(fun, fun->u.i.script);
+        js_CallNewScriptHook(cx, fun->script(), fun);
     }
 
     return true;
 }
 
 #else  /* !JS_HAS_XDR */
 
 #define js_XDRFunctionObject NULL
@@ -2628,17 +2612,22 @@ Function(JSContext *cx, uintN argc, Valu
         jschar *collected_args = cp;
 
         /*
          * Concatenate the arguments into the new string, separated by commas.
          */
         for (uintN i = 0; i < n; i++) {
             JSString *arg = argv[i].toString();
             size_t arg_length = arg->length();
-            (void) js_strncpy(cp, arg->chars(), arg_length);
+            const jschar *arg_chars = arg->getChars(cx);
+            if (!arg_chars) {
+                JS_ARENA_RELEASE(&cx->tempPool, mark);
+                return JS_FALSE;
+            }
+            (void) js_strncpy(cp, arg_chars, arg_length);
             cp += arg_length;
 
             /* Add separating comma or terminating 0. */
             *cp++ = (i + 1 < n) ? ',' : 0;
         }
 
         /* Initialize a tokenstream that reads from the given string. */
         TokenStream ts(cx);
@@ -2715,18 +2704,21 @@ Function(JSContext *cx, uintN argc, Valu
         str = js_ValueToString(cx, argv[argc-1]);
         if (!str)
             return JS_FALSE;
         argv[argc-1].setString(str);
     } else {
         str = cx->runtime->emptyString;
     }
 
-    JSBool res = Compiler::compileFunctionBody(cx, fun, principals,
-                                               str->chars(), str->length(),
+    size_t length = str->length();
+    const jschar *chars = str->getChars(cx);
+    if (!chars)
+        return JS_FALSE;
+    JSBool res = Compiler::compileFunctionBody(cx, fun, principals, chars, length,
                                                filename, lineno);
     if (res)
         fun->u.i.script->setTypeNesting(caller->script(), caller->pc(cx));
     return res;
 }
 
 static JSBool
 ThrowTypeError(JSContext *cx, uintN argc, Value *vp)
@@ -2743,17 +2735,28 @@ js_InitFunctionClass(JSContext *cx, JSOb
                                    JS_TypeHandlerDynamic, NULL, function_methods, NULL, NULL);
     if (!proto)
         return NULL;
 
     JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL, NULL, 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, JS_TypeHandlerVoid, "ThrowTypeError");
         if (!throwTypeError)
             return NULL;
@@ -2872,28 +2875,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)
@@ -2903,17 +2904,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/jsfun.h
+++ b/js/src/jsfun.h
@@ -430,16 +430,24 @@ IsFunctionObject(const js::Value &v, JSF
 {
     JSObject *funobj;
     bool b = IsFunctionObject(v, &funobj);
     if (b)
         *fun = funobj->getFunctionPrivate();
     return b;
 }
 
+extern JS_ALWAYS_INLINE bool
+SameTraceType(const Value &lhs, const Value &rhs)
+{
+    return SameType(lhs, rhs) &&
+           (lhs.isPrimitive() ||
+            lhs.toObject().isFunction() == rhs.toObject().isFunction());
+}
+
 /*
  * Macro to access the private slot of the function object after the slot is
  * initialized.
  */
 #define GET_FUNCTION_PRIVATE(cx, funobj)                                      \
     (JS_ASSERT((funobj)->isFunction()),                                       \
      (JSFunction *) (funobj)->getPrivate())
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -487,16 +487,23 @@ IsAboutToBeFinalized(void *thing)
 JS_FRIEND_API(bool)
 js_GCThingIsMarked(void *thing, uint32 color = BLACK)
 {
     JS_ASSERT(thing);
     AssertValidColor(thing, color);
     return reinterpret_cast<Cell *>(thing)->isMarked(color);
 }
 
+/*
+ * 1/8 life for JIT code. After this number of microseconds have passed, 1/8 of all
+ * JIT code is discarded in inactive compartments, regardless of how often that
+ * code runs.
+ */
+static const int64 JIT_SCRIPT_EIGHTH_LIFETIME = 120 * 1000 * 1000;
+
 JSBool
 js_InitGC(JSRuntime *rt, uint32 maxbytes)
 {
     /*
      * Make room for at least 16 chunks so the table would not grow before
      * the browser starts up.
      */
     if (!rt->gcChunkSet.init(16))
@@ -534,16 +541,18 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes
     rt->gcTriggerFactor = uint32(100.0f * GC_HEAP_GROWTH_FACTOR);
 
     /*
      * The assigned value prevents GC from running when GC memory is too low
      * (during JS engine start).
      */
     rt->setGCLastBytes(8192);
 
+    rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_EIGHTH_LIFETIME;
+
     METER(PodZero(&rt->gcStats));
     return true;
 }
 
 namespace js {
 
 template <typename T>
 static inline ConservativeGCTest
@@ -1430,18 +1439,20 @@ js_TraceStackFrame(JSTracer *trc, JSStac
     MarkObject(trc, fp->scopeChain(), "scope chain");
     if (fp->isDummyFrame())
         return;
 
     if (fp->hasCallObj())
         MarkObject(trc, fp->callObj(), "call");
     if (fp->hasArgsObj())
         MarkObject(trc, fp->argsObj(), "arguments");
-    if (fp->isScriptFrame())
+    if (fp->isScriptFrame()) {
         js_TraceScript(trc, fp->script());
+        fp->script()->compartment->active = true;
+    }
 
     MarkValue(trc, fp->returnValue(), "rval");
 }
 
 void
 AutoIdArray::trace(JSTracer *trc)
 {
     JS_ASSERT(tag == IDARRAY);
@@ -1768,17 +1779,17 @@ js_FinalizeStringRT(JSRuntime *rt, JSStr
         JS_ASSERT(str->asCell()->arena()->header()->thingKind == FINALIZE_STRING);
         JS_ASSERT(str->dependentBase());
         JS_RUNTIME_UNMETER(rt, liveDependentStrings);
     } else {
         unsigned thingKind = str->asCell()->arena()->header()->thingKind;
         JS_ASSERT(IsFinalizableStringKind(thingKind));
 
         /* A stillborn string has null chars, so is not valid. */
-        jschar *chars = str->flatChars();
+        jschar *chars = const_cast<jschar *>(str->flatChars());
         if (!chars)
             return;
         if (thingKind == FINALIZE_STRING) {
             rt->free(chars);
         } else if (thingKind == FINALIZE_EXTERNAL_STRING) {
             ((JSExternalString *)str)->finalize();
         }
     }
@@ -2030,34 +2041,51 @@ SweepCompartments(JSContext *cx, JSGCInv
     JSCompartmentCallback callback = rt->compartmentCallback;
     JSCompartment **read = rt->compartments.begin();
     JSCompartment **end = rt->compartments.end();
     JSCompartment **write = read;
 
     /* Delete defaultCompartment only during runtime shutdown */
     rt->defaultCompartment->marked = true;
 
+    /*
+     * Figure out how much JIT code should be released from inactive compartments.
+     * If multiple eighth-lifes have passed, compound the release interval linearly;
+     * if enough time has passed, all inactive JIT code will be released.
+     */
+    uint32 releaseInterval = 0;
+    int64 now = PRMJ_Now();
+    if (now >= rt->gcJitReleaseTime) {
+        releaseInterval = 8;
+        while (now >= rt->gcJitReleaseTime) {
+            if (--releaseInterval == 1)
+                rt->gcJitReleaseTime = now;
+            rt->gcJitReleaseTime += JIT_SCRIPT_EIGHTH_LIFETIME;
+        }
+    }
+
     while (read < end) {
         JSCompartment *compartment = (*read++);
         if (compartment->marked) {
             compartment->marked = false;
             *write++ = compartment;
-            compartment->sweep(cx);
+            /* Remove dead wrappers from the compartment map. */
+            compartment->sweep(cx, releaseInterval);
         } else {
             JS_ASSERT(compartment->freeLists.isEmpty());
             if (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT) {
                 if (callback)
                     (void) callback(cx, compartment, JSCOMPARTMENT_DESTROY);
                 if (compartment->principals)
                     JSPRINCIPALS_DROP(cx, compartment->principals);
                 delete compartment;
             } else {
                 compartment->marked = false;
                 *write++ = compartment;
-                compartment->sweep(cx);
+                compartment->sweep(cx, releaseInterval);
             }
         }
     }
     rt->compartments.resize(write - rt->compartments.begin());
 }
 
 /*
  * Common cache invalidation and so forth that must be done before GC. Even if
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -261,18 +261,16 @@ MarkChildren(JSTracer *trc, JSObject *ob
 }
 
 static inline void
 MarkChildren(JSTracer *trc, JSString *str)
 {
     if (str->isDependent())
         MarkString(trc, str->dependentBase(), "base");
     else if (str->isRope()) {
-        if (str->isInteriorNode())
-            MarkString(trc, str->interiorNodeParent(), "parent");
         MarkString(trc, str->ropeLeft(), "left child");
         MarkString(trc, str->ropeRight(), "right child");
     }
 }
 
 #ifdef JS_HAS_XML_SUPPORT
 static inline void
 MarkChildren(JSTracer *trc, JSXML *xml)
@@ -339,38 +337,118 @@ TypedMarker(JSTracer *trc, JSFunction *t
 }
 
 static JS_ALWAYS_INLINE void
 TypedMarker(JSTracer *trc, JSShortString *thing)
 {
     thing->asCell()->markIfUnmarked();
 }
 
+}  /* namespace gc */
+
+namespace detail {
+
+static JS_ALWAYS_INLINE JSString *
+Tag(JSString *str)
+{
+    JS_ASSERT(!(size_t(str) & 1));
+    return (JSString *)(size_t(str) | 1);
+}
+
+static JS_ALWAYS_INLINE bool
+Tagged(JSString *str)
+{
+    return (size_t(str) & 1) != 0;
+}
+
+static JS_ALWAYS_INLINE JSString *
+Untag(JSString *str)
+{
+    JS_ASSERT((size_t(str) & 1) == 1);
+    return (JSString *)(size_t(str) & ~size_t(1));
+}
+
 static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, JSString *thing)
+NonRopeTypedMarker(JSString *str)
 {
+    JS_ASSERT(!str->isRope());
+    if (JSString::isStatic(str) ||
+        !str->asCell()->markIfUnmarked() ||
+        !str->isDependent()) {
+        return;
+    }
+    JSString *base = str->dependentBase();
+    if (JSString::isStatic(base))
+        return;
+    base->asCell()->markIfUnmarked();
+}
+
+}  /* namespace detail */
+
+namespace gc {
+
+static JS_ALWAYS_INLINE void
+TypedMarker(JSTracer *trc, JSString *str)
+{
+    using namespace detail;
+
+    if (!str->isRope()) {
+        NonRopeTypedMarker(str);
+        return;
+    }
+
     /*
-     * 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.
+     * This function must not fail, so a simple stack-based traversal must not
+     * be used (since it may oom if the stack grows large). Instead, strings
+     * are temporarily mutated to embed parent pointers as they are traversed.
+     * This algorithm is homomorphic to JSString::flatten.
      */
-    JSRopeNodeIterator iter(thing);
-    JSString *str = iter.init();
-    do {
-        for (;;) {
-            if (JSString::isStatic(str))
-                break;
-            JS_ASSERT(JSTRACE_STRING == GetFinalizableTraceKind(str->asCell()->arena()->header()->thingKind));
-            if (!str->asCell()->markIfUnmarked())
-                break;
-            if (!str->isDependent())
-                break;
-            str = str->dependentBase();
+    JSString *parent = NULL;
+    first_visit_node: {
+        if (!str->asCell()->markIfUnmarked())
+            goto finish_node;
+        JSString *left = str->ropeLeft();
+        if (left->isRope()) {
+            JS_ASSERT(!Tagged(str->u.left) && !Tagged(str->s.right));
+            str->u.left = Tag(parent);
+            parent = str;
+            str = left;
+            goto first_visit_node;
         }
-        str = iter.next();
-    } while (str);
+        NonRopeTypedMarker(left);
+    }
+    visit_right_child: {
+        JSString *right = str->ropeRight();
+        if (right->isRope()) {
+            JS_ASSERT(!Tagged(str->u.left) && !Tagged(str->s.right));
+            str->s.right = Tag(parent);
+            parent = str;
+            str = right;
+            goto first_visit_node;
+        }
+        NonRopeTypedMarker(right);
+    }
+    finish_node: {
+        if (!parent)
+            return;
+        if (Tagged(parent->u.left)) {
+            JS_ASSERT(!Tagged(parent->s.right));
+            JSString *nextParent = Untag(parent->u.left);
+            parent->u.left = str;
+            str = parent;
+            parent = nextParent;
+            goto visit_right_child;
+        }
+        JS_ASSERT(Tagged(parent->s.right));
+        JSString *nextParent = Untag(parent->s.right);
+        parent->s.right = str;
+        str = parent;
+        parent = nextParent;
+        goto finish_node;
+    }
 }
 
 static inline void
 MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name)
 {
     for (uint32 i = 0; i < len; i++) {
         if (JSAtom *atom = vec[i]) {
             JS_SET_TRACING_INDEX(trc, name, i);
--- a/js/src/jsgcstats.cpp
+++ b/js/src/jsgcstats.cpp
@@ -357,19 +357,23 @@ GCMarker::dumpConservativeRoots()
 
           case JSTRACE_OBJECT: {
             JSObject *obj = (JSObject *) i->thing;
             fprintf(fp, "object %s", obj->getClass()->name);
             break;
           }
           case JSTRACE_STRING: {
             JSString *str = (JSString *) i->thing;
-            char buf[50];
-            PutEscapedString(buf, sizeof buf, str, '"');
-            fprintf(fp, "string %s", buf);
+            if (str->isLinear()) {
+                char buf[50];
+                PutEscapedString(buf, sizeof buf, str->assertIsLinear(), '"');
+                fprintf(fp, "string %s", buf);
+            } else {
+                fprintf(fp, "rope: length %d", (int)str->length());
+            }
             break;
           }
 # if JS_HAS_XML_SUPPORT
           case JSTRACE_XML: {
             JSXML *xml = (JSXML *) i->thing;
             fprintf(fp, "xml %u", (unsigned)xml->xml_class);
             break;
           }
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -477,30 +477,31 @@ TypeSet::addArith(JSContext *cx, JSArena
     JS_ASSERT(this->pool == &pool);
     add(cx, ArenaNew<TypeConstraintArith>(pool, code, target, other));
 }
 
 /* Subset constraint which transforms primitive values into appropriate objects. */
 class TypeConstraintTransformThis : public TypeConstraint
 {
 public:
+    analyze::Bytecode *code;
     TypeSet *target;
 
-    TypeConstraintTransformThis(TypeSet *target)
-        : TypeConstraint("transformthis"), target(target)
+    TypeConstraintTransformThis(analyze::Bytecode *code, TypeSet *target)
+        : TypeConstraint("transformthis"), code(code), target(target)
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
-TypeSet::addTransformThis(JSContext *cx, JSArenaPool &pool, TypeSet *target)
+TypeSet::addTransformThis(JSContext *cx, analyze::Bytecode *code, TypeSet *target)
 {
-    JS_ASSERT(this->pool == &pool);
-    add(cx, ArenaNew<TypeConstraintTransformThis>(pool, target));
+    JS_ASSERT(this->pool == &code->pool());
+    add(cx, ArenaNew<TypeConstraintTransformThis>(code->pool(), code, target));
 }
 
 /* Subset constraint which filters out primitive types. */
 class TypeConstraintFilterPrimitive : public TypeConstraint
 {
 public:
     TypeSet *target;
 
@@ -671,17 +672,18 @@ PropertyAccess(JSContext *cx, analyze::B
             writeTypes->addType(cx, TYPE_DOUBLE);
         readTypes->addArith(cx, *object->pool, code, writeTypes);
     }
 }
 
 void
 TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
 {
-    if (type == TYPE_UNKNOWN) {
+    if (type == TYPE_UNKNOWN ||
+        (!TypeIsObject(type) && !code->script->getScript()->compileAndGo)) {
         /*
          * Access on an unknown object.  Reads produce an unknown result, writes
          * need to be monitored.  Note: this isn't a problem for handling overflows
          * on inc/dec below, as these go through a slow path which must call
          * addTypeProperty.
          */
         if (assign)
             cx->compartment->types.monitorBytecode(cx, code);
@@ -783,17 +785,17 @@ TypeConstraintCall::newType(JSContext *c
 
             /*
              * Make a new callsite transforming the arguments appropriately, as is
              * done by the generic native dispatchers. watch out for cases where the
              * first argument is null, which will transform to the global object.
              */
 
             TypeSet *thisTypes = TypeSet::make(cx, pool, "genericthis");
-            callsite->argumentTypes[0]->addTransformThis(cx, pool, thisTypes);
+            callsite->argumentTypes[0]->addTransformThis(cx, callsite->code, thisTypes);
 
             TypeCallsite *newSite = ArenaNew<TypeCallsite>(pool, callsite->code, callsite->isNew,
                                                            callsite->argumentCount - 1);
             newSite->thisTypes = thisTypes;
             newSite->returnTypes = callsite->returnTypes;
             for (unsigned i = 0; i < callsite->argumentCount - 1; i++)
                 newSite->argumentTypes[i] = callsite->argumentTypes[i + 1];
 
@@ -930,16 +932,21 @@ TypeConstraintArith::newType(JSContext *
 void
 TypeConstraintTransformThis::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     if (type == TYPE_UNKNOWN || TypeIsObject(type)) {
         target->addType(cx, type);
         return;
     }
 
+    if (!code->script->getScript()->compileAndGo) {
+        target->addType(cx, TYPE_UNKNOWN);
+        return;
+    }
+
     /* TODO: handle strict mode code correctly. */
     TypeObject *object = NULL;
     switch (type) {
       case TYPE_NULL:
       case TYPE_UNDEFINED:
         object = cx->globalTypeObject();
         break;
       case TYPE_INT32:
@@ -1847,16 +1854,25 @@ namespace analyze {
 using namespace types;
 
 void
 Script::addVariable(JSContext *cx, jsid id, types::Variable *&var)
 {
     JS_ASSERT(!var);
     var = ArenaNew<types::Variable>(pool, &pool, id);
 
+    /* Augment with builtin types for the 'arguments' variable. */
+    if (fun && id == id_arguments(cx)) {
+        TypeSet *types = &var->types;
+        if (script->compileAndGo)
+            types->addType(cx, (jstype) cx->getTypeNewObject(JSProto_Object));
+        else
+            types->addType(cx, TYPE_UNKNOWN);
+    }
+
     InferSpew(ISpewOps, "addVariable: #%lu %s T%u",
               this->id, TypeIdString(id), var->types.id());
 }
 
 inline Bytecode*
 Script::parentCode()
 {
     return parent ? &parent->analysis->getCode(parentpc) : NULL;
@@ -1969,17 +1985,20 @@ SearchScope(JSContext *cx, Script *scrip
         }
 
         if (!script->parent)
             break;
 
         /* Function scripts have 'arguments' local variables. */
         if (id == id_arguments(cx) && script->fun) {
             TypeSet *types = script->getVariable(cx, id);
-            types->addType(cx, (jstype) cx->getTypeNewObject(JSProto_Object));
+            if (script->getScript()->compileAndGo)
+                types->addType(cx, (jstype) cx->getTypeNewObject(JSProto_Object));
+            else
+                types->addType(cx, TYPE_UNKNOWN);
             return script;
         }
 
         /* Function scripts with names have local variables of that name. */
         if (script->fun && id == ATOM_TO_JSID(script->fun->atom)) {
             TypeSet *types = script->getVariable(cx, id);
             types->addType(cx, (jstype) script->function());
             return script;
@@ -2258,17 +2277,20 @@ Script::analyzeTypes(JSContext *cx, Byte
       case JSOP_ADDATTRVAL:
       case JSOP_XMLELTEXPR:
         code->setFixed(cx, 0, TYPE_STRING);
         break;
       case JSOP_NULL:
         code->setFixed(cx, 0, TYPE_NULL);
         break;
       case JSOP_REGEXP:
-        code->setFixed(cx, 0, (jstype) cx->getTypeNewObject(JSProto_RegExp));
+        if (script->compileAndGo)
+            code->setFixed(cx, 0, (jstype) cx->getTypeNewObject(JSProto_RegExp));
+        else
+            code->setFixed(cx, 0, TYPE_UNKNOWN);
         break;
 
       case JSOP_STOP:
         /* If a stop is reachable then the return type may be void. */
         if (fun)
             function()->returnTypes.addType(cx, TYPE_UNDEFINED);
         break;
 
@@ -2561,35 +2583,24 @@ Script::analyzeTypes(JSContext *cx, Byte
         MergePushed(cx, evalParent()->pool, code, 0, types);
 
         if (code->hasIncDecOverflow)
             types->addType(cx, TYPE_DOUBLE);
         break;
       }
 
       case JSOP_ARGUMENTS: {
-        /*
-         * This can show up either when the arguments array is being accessed
-         * or when there is a local variable named arguments.
-         */
-        JS_ASSERT(fun);
         TypeSet *types = getVariable(cx, id_arguments(cx));
-        types->addType(cx, (jstype) cx->getTypeNewObject(JSProto_Object));
         MergePushed(cx, pool, code, 0, types);
         break;
       }
 
-      case JSOP_ARGCNT: {
-        JS_ASSERT(fun);
-        TypeSet *types = getVariable(cx, id_arguments(cx));
-        types->addType(cx, (jstype) cx->getTypeNewObject(JSProto_Object));
-
-        types->addGetProperty(cx, code, code->pushed(0), id_length(cx));
+      case JSOP_ARGCNT:
+        code->setFixed(cx, 0, TYPE_UNKNOWN);
         break;
-      }
 
       case JSOP_SETPROP:
       case JSOP_SETMETHOD: {
         jsid id = GetAtomId(cx, this, pc, 0);
         code->popped(1)->addSetProperty(cx, code, code->popped(0), id);
 
         MergePushed(cx, pool, code, 0, code->popped(0));
         break;
@@ -2616,17 +2627,17 @@ Script::analyzeTypes(JSContext *cx, Byte
         break;
       }
 
       case JSOP_GETTHISPROP: {
         jsid id = GetAtomId(cx, this, pc, 0);
 
         /* Need a new type set to model conversion of NULL to the global object. */
         TypeSet *newTypes = TypeSet::make(cx, pool, "thisprop");
-        thisTypes.addTransformThis(cx, pool, newTypes);
+        thisTypes.addTransformThis(cx, code, newTypes);
         newTypes->addGetProperty(cx, code, code->pushed(0), id);
 
         CheckNextTest(cx, code, pc);
         break;
       }
 
       case JSOP_GETARGPROP: {
         jsid id = getArgumentId(GET_ARGNO(pc));
@@ -2686,17 +2697,17 @@ Script::analyzeTypes(JSContext *cx, Byte
         break;
 
       case JSOP_LENGTH:
         /* Treat this as an access to the length property. */
         code->popped(0)->addGetProperty(cx, code, code->pushed(0), id_length(cx));
         break;
 
       case JSOP_THIS:
-        thisTypes.addTransformThis(cx, pool, code->pushed(0));
+        thisTypes.addTransformThis(cx, code, code->pushed(0));
 
         /* 'this' refers to the parent global/scope object in non-function scripts. */
         if (!fun)
             code->setFixed(cx, 0, (jstype) cx->globalTypeObject());
         break;
 
       case JSOP_RETURN:
       case JSOP_SETRVAL:
@@ -2756,20 +2767,24 @@ Script::analyzeTypes(JSContext *cx, Byte
                 res = cx->globalTypeObject()->getProperty(cx, id, true);
             } else {
                 /* Defined function in a function eval() or ambiguous function scope. */
                 TrashScope(cx, this, id);
                 break;
             }
         }
 
-        if (res)
-            res->addType(cx, (jstype) function);
-        else
+        if (res) {
+            if (script->compileAndGo)
+                res->addType(cx, (jstype) function);
+            else
+                res->addType(cx, TYPE_UNKNOWN);
+        } else {
             cx->compartment->types.monitorBytecode(cx, code);
+        }
         break;
       }
 
       case JSOP_CALL:
       case JSOP_SETCALL:
       case JSOP_EVAL:
       case JSOP_FUNCALL:
       case JSOP_FUNAPPLY:
@@ -2786,91 +2801,99 @@ Script::analyzeTypes(JSContext *cx, Byte
         }
 
         code->popped(argCount + 1)->addCall(cx, callsite);
         break;
       }
 
       case JSOP_NEWINIT:
       case JSOP_NEWARRAY:
-      case JSOP_NEWOBJECT: {
-        TypeObject *object;
-        if (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && pc[1] == JSProto_Array)) {
-            object = code->initArray;
-            jsbytecode *next = pc + GetBytecodeLength(pc);
-            if (JSOp(*next) != JSOP_ENDINIT)
-                object->possiblePackedArray = true;
+      case JSOP_NEWOBJECT:
+        if (script->compileAndGo) {
+            TypeObject *object;
+            if (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && pc[1] == JSProto_Array)) {
+                object = code->initArray;
+                jsbytecode *next = pc + GetBytecodeLength(pc);
+                if (JSOp(*next) != JSOP_ENDINIT)
+                    object->possiblePackedArray = true;
+            } else {
+                object = code->initObject;
+            }
+            code->pushed(0)->addType(cx, (jstype) object);
         } else {
-            object = code->initObject;
+            code->setFixed(cx, 0, TYPE_UNKNOWN);
         }
-
-        code->pushed(0)->addType(cx, (jstype) object);
         break;
-      }
 
       case JSOP_ENDINIT:
         break;
 
-      case JSOP_INITELEM: {
-        TypeObject *object = code->initObject;
-        JS_ASSERT(object);
-
-        code->pushed(0)->addType(cx, (jstype) object);
-
-        TypeSet *types;
-        if (state.popped(1).hasDouble) {
-            Value val = DoubleValue(state.popped(1).doubleValue);
-            jsid id;
-            if (!js_InternNonIntElementId(cx, NULL, val, &id))
-                JS_NOT_REACHED("Bad");
-            types = object->getProperty(cx, id, true);
+      case JSOP_INITELEM:
+        if (script->compileAndGo) {
+            TypeObject *object = code->initObject;
+            JS_ASSERT(object);
+
+            code->pushed(0)->addType(cx, (jstype) object);
+
+            TypeSet *types;
+            if (state.popped(1).hasDouble) {
+                Value val = DoubleValue(state.popped(1).doubleValue);
+                jsid id;
+                if (!js_InternNonIntElementId(cx, NULL, val, &id))
+                    JS_NOT_REACHED("Bad");
+                types = object->getProperty(cx, id, true);
+            } else {
+                types = object->getProperty(cx, JSID_VOID, true);
+            }
+
+            if (state.hasGetSet)
+                types->addType(cx, (jstype) cx->getTypeGetSet());
+            else if (state.hasHole)
+                cx->markTypeArrayNotPacked(object, false);
+            else
+                code->popped(0)->addSubset(cx, pool, types);
         } else {
-            types = object->getProperty(cx, JSID_VOID, true);
+            code->setFixed(cx, 0, TYPE_UNKNOWN);
         }
-
-        if (state.hasGetSet)
-            types->addType(cx, (jstype) cx->getTypeGetSet());
-        else if (state.hasHole)
-            cx->markTypeArrayNotPacked(object, false);
-        else
-            code->popped(0)->addSubset(cx, pool, types);
         state.hasGetSet = false;
         state.hasHole = false;
         break;
-      }
 
       case JSOP_GETTER:
       case JSOP_SETTER:
         state.hasGetSet = true;
         break;
 
       case JSOP_HOLE:
         state.hasHole = true;
         break;
 
       case JSOP_INITPROP:
-      case JSOP_INITMETHOD: {
-        TypeObject *object = code->initObject;
-        JS_ASSERT(object);
-
-        code->pushed(0)->addType(cx, (jstype) object);
-
-        jsid id = GetAtomId(cx, this, pc, 0);
-        TypeSet *types = object->getProperty(cx, id, true);
-
-        if (id == id___proto__(cx) || id == id_prototype(cx))
-            cx->compartment->types.monitorBytecode(cx, code);
-        else if (state.hasGetSet)
-            types->addType(cx, (jstype) cx->getTypeGetSet());
-        else
-            code->popped(0)->addSubset(cx, pool, types);
+      case JSOP_INITMETHOD:
+        if (script->compileAndGo) {
+            TypeObject *object = code->initObject;
+            JS_ASSERT(object);
+
+            code->pushed(0)->addType(cx, (jstype) object);
+
+            jsid id = GetAtomId(cx, this, pc, 0);
+            TypeSet *types = object->getProperty(cx, id, true);
+
+            if (id == id___proto__(cx) || id == id_prototype(cx))
+                cx->compartment->types.monitorBytecode(cx, code);
+            else if (state.hasGetSet)
+                types->addType(cx, (jstype) cx->getTypeGetSet());
+            else
+                code->popped(0)->addSubset(cx, pool, types);
+        } else {
+            code->setFixed(cx, 0, TYPE_UNKNOWN);
+        }
         state.hasGetSet = false;
         JS_ASSERT(!state.hasHole);
         break;
-      }
 
       case JSOP_ENTERWITH:
         /*
          * Scope lookups can occur on the value being pushed here.  We don't track
          * the value or its properties, and just monitor all name opcodes contained
          * by the with.
          */
         code->pushedArray[0].group()->boundWith = true;
@@ -2990,18 +3013,22 @@ Script::analyzeTypes(JSContext *cx, Byte
         break;
 
       case JSOP_UNBRAND:
         MergePushed(cx, pool, code, 0, code->popped(0));
         break;
 
       case JSOP_GENERATOR:
         if (fun) {
-            TypeObject *object = cx->getTypeNewObject(JSProto_Generator);
-            function()->returnTypes.addType(cx, (jstype) object);
+            if (script->compileAndGo) {
+                TypeObject *object = cx->getTypeNewObject(JSProto_Generator);
+                function()->returnTypes.addType(cx, (jstype) object);
+            } else {
+                function()->returnTypes.addType(cx, TYPE_UNKNOWN);
+            }
         }
         break;
 
       case JSOP_YIELD:
         code->setFixed(cx, 0, TYPE_UNKNOWN);
         break;
 
       case JSOP_CALLXMLNAME:
@@ -3072,17 +3099,20 @@ Script::analyzeTypes(JSContext *cx, Byte
         jsid id = ATOM_TO_JSID(atom);
 
         TypeSet *types = evalParent()->getVariable(cx, id);
         MergePushed(cx, evalParent()->pool, code, 0, types);
         break;
       }
 
       case JSOP_CALLEE:
-        code->setFixed(cx, 0, (jstype) function());
+        if (script->compileAndGo)
+            code->setFixed(cx, 0, (jstype) function());
+        else
+            code->setFixed(cx, 0, TYPE_UNKNOWN);
         break;
 
       default:
         TypeFailure(cx, "Unknown bytecode: %s", js_CodeNameTwo[op]);
     }
 
     /* Compute temporary analysis state after the bytecode. */
 
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -246,17 +246,17 @@ struct TypeSet
     void addGetProperty(JSContext *cx, analyze::Bytecode *code, TypeSet *target, jsid id);
     void addSetProperty(JSContext *cx, analyze::Bytecode *code, TypeSet *target, jsid id);
     void addGetElem(JSContext *cx, analyze::Bytecode *code, TypeSet *object, TypeSet *target);
     void addSetElem(JSContext *cx, analyze::Bytecode *code, TypeSet *object, TypeSet *target);
     void addNewObject(JSContext *cx, JSArenaPool &pool, TypeSet *target);
     void addCall(JSContext *cx, TypeCallsite *site);
     void addArith(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code,
                   TypeSet *target, TypeSet *other = NULL);
-    void addTransformThis(JSContext *cx, JSArenaPool &pool, TypeSet *target);
+    void addTransformThis(JSContext *cx, analyze::Bytecode *code, TypeSet *target);
     void addFilterPrimitives(JSContext *cx, JSArenaPool &pool, TypeSet *target, bool onlyNullVoid);
     void addMonitorRead(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *target);
 
     /*
      * Make an intermediate type set with the specified debugging name,
      * not embedded in another structure.
      */
     static inline TypeSet* make(JSContext *cx, JSArenaPool &pool, const char *name);
@@ -358,16 +358,18 @@ struct TypeCallsite
     inline void forceThisTypes(JSContext *cx);
     inline void forceReturnTypes(JSContext *cx);
 
     /* Get the new object at this callsite, per Bytecode::getInitObject. */
     inline TypeObject* getInitObject(JSContext *cx, bool isArray);
 
     /* Pool which handlers on this call site should use. */
     inline JSArenaPool & pool();
+
+    inline bool compileAndGo();
 };
 
 /* Type information about a variable. */
 struct Variable
 {
     /* Variable identifier. */
     jsid id;
 
@@ -505,17 +507,21 @@ struct TypeObject
     void addProperty(JSContext *cx, jsid id, Property *&prop);
     void markUnknown(JSContext *cx);
     void storeToInstances(JSContext *cx, Property *base);
 
     void print(JSContext *cx);
     void trace(JSTracer *trc);
 };
 
-/* Type information about an interpreted or native function. */
+/*
+ * Type information about an interpreted or native function. Note: it is possible for
+ * a function JSObject to have a type which is not a TypeFunction. This happens when
+ * we are not able to statically model the type of a function due to non-compileAndGo code.
+ */
 struct TypeFunction : public TypeObject
 {
     /* If this function is native, the handler to use at calls to it. */
     JSTypeHandler handler;
 
     /* If this function is interpreted, the corresponding script. */
     JSScript *script;
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -103,18 +103,18 @@ MakeTypeId(JSContext *cx, jsid id)
     if (JSID_IS_OBJECT(id))
         return JSID_VOID;
 
     /*
      * Check for numeric strings, as in js_StringIsIndex, but allow negative
      * and overflowing integers.
      */
     if (JSID_IS_STRING(id)) {
-        JSString *str = JSID_TO_STRING(id);
-        jschar *cp = str->chars();
+        JSFlatString *str = JSID_TO_FLAT_STRING(id);
+        const jschar *cp = str->getCharsZ(cx);
         if (JS7_ISDEC(*cp) || *cp == '-') {
             cp++;
             while (JS7_ISDEC(*cp))
                 cp++;
             if (unsigned(cp - str->chars()) == str->length())
                 return JSID_VOID;
         }
         /* :FIXME: bug 613221 sweep type constraints so that atoms don't need to be pinned. */
@@ -347,47 +347,47 @@ JSContext::markTypeObjectUnknownProperti
 inline void
 JSContext::typeMonitorCall(JSScript *caller, const jsbytecode *callerpc,
                            const js::CallArgs &args, bool constructing, bool force)
 {
     JS_ASSERT_IF(caller == NULL, force);
 #ifdef JS_TYPE_INFERENCE
     if (!args.callee().isObject() || !args.callee().toObject().isFunction())
         return;
-    JSObject *callee = &args.callee().toObject();
-    js::types::TypeFunction *fun = callee->getType()->asFunction();
+    JSFunction *callee = args.callee().toObject().getFunctionPrivate();
 
     /*
      * Don't do anything on calls to native functions.  If the call is monitored
      * then the return value is unknown, and when cx->isTypeCallerMonitored() natives
      * should inform inference of any side effects not on the return value.
      */
-    if (!fun->script)
+    if (!callee->isInterpreted())
         return;
-    js::analyze::Script *script = fun->script->analysis;
 
     if (!force) {
         if (caller->analysis->failed() || caller->analysis->getCode(callerpc).monitorNeeded)
             force = true;
     }
 
-    typeMonitorEntry(fun->script);
+    typeMonitorEntry(callee->script());
 
     /* Don't need to do anything if this is at a non-monitored callsite. */
     if (!force)
         return;
 
     js::types::jstype type;
     if (constructing) {
         /* Don't duplicate the logic in js_CreateThis, just mark 'this' as unknown. */
         type = js::types::TYPE_UNKNOWN;
     } else {
         type = js::types::GetValueType(this, args.thisv());
     }
 
+    js::analyze::Script *script = callee->script()->analysis;
+
     if (!constructing) {
         if (!script->thisTypes.hasType(type)) {
             js::types::InferSpew(js::types::ISpewDynamic, "AddThis: #%u: %s",
                                  script->id, js::types::TypeString(type));
             compartment->types.addDynamicType(this, &script->thisTypes, type);
         }
     }
 
@@ -484,17 +484,17 @@ JSScript::setTypeNesting(JSScript *paren
 }
 
 inline js::types::TypeObject *
 JSScript::getTypeInitObject(JSContext *cx, const jsbytecode *pc, bool isArray)
 {
 #ifdef JS_TYPE_INFERENCE
     /* :FIXME: */
     JS_ASSERT(!analysis->failed());
-    if (compileAndGo)
+    if (!compileAndGo)
         return cx->getTypeNewObject(isArray ? JSProto_Array : JSProto_Object);
     return analysis->getCode(pc).getInitObject(cx, isArray);
 #else
     return cx->getTypeNewObject(isArray ? JSProto_Array : JSProto_Object);
 #endif
 }
 
 inline void
@@ -607,16 +607,17 @@ inline void
 Bytecode::setFixed(JSContext *cx, unsigned num, types::jstype type)
 {
     pushed(num)->addType(cx, type);
 }
 
 inline types::TypeObject *
 Bytecode::getInitObject(JSContext *cx, bool isArray)
 {
+    JS_ASSERT(script->script->compileAndGo);
 #ifdef JS_TYPE_INFERENCE
     types::TypeObject *&object = isArray ? initArray : initObject;
     if (!object) {
         char *name = NULL;
 #ifdef DEBUG
         name = (char *) alloca(32);
         JS_snprintf(name, 32, "#%u:%u:%s", script->id, offset, isArray ? "Array" : "Object");
 #endif
@@ -1091,16 +1092,22 @@ TypeCallsite::getInitObject(JSContext *c
 }
 
 inline JSArenaPool &
 TypeCallsite::pool()
 {
     return code->pool();
 }
 
+inline bool
+TypeCallsite::compileAndGo()
+{
+    return code->script->getScript()->compileAndGo;
+}
+
 /////////////////////////////////////////////////////////////////////
 // TypeObject
 /////////////////////////////////////////////////////////////////////
 
 inline TypeSet *
 TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
 {
     JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -615,17 +615,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;
@@ -909,17 +909,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.
      */
@@ -997,33 +997,24 @@ Execute(JSContext *cx, JSObject *chain, 
         JSObject *thisp = chain->thisObject(cx);
         if (!thisp)
             return false;
         frame.fp()->globalThis().setObject(*thisp);
     }
 
     Probes::startExecution(cx, script);
 
-    void *hookData = NULL;
-    if (JSInterpreterHook hook = cx->debugHooks->executeHook)
-        hookData = hook(cx, frame.fp(), JS_TRUE, 0, cx->debugHooks->executeHookData);
-
     cx->typeMonitorEntry(script, frame.fp()->thisValue());
 
     /* Run script until JSOP_STOP or error. */
     AutoPreserveEnumerators preserve(cx);
     JSBool ok = RunScript(cx, script, frame.fp());
     if (result)
         *result = frame.fp()->returnValue();
 
-    if (hookData) {
-        if (JSInterpreterHook hook = cx->debugHooks->executeHook)
-            hook(cx, frame.fp(), JS_FALSE, &ok, hookData);
-    }
-
     Probes::stopExecution(cx, script);
 
     return !!ok;
 }
 
 bool
 CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
                    JSObject **objp, JSProperty **propp)
@@ -1142,74 +1133,84 @@ HasInstance(JSContext *cx, JSObject *obj
     Class *clasp = obj->getClass();
     if (clasp->hasInstance)
         return clasp->hasInstance(cx, obj, v, bp);
     js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
                         JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL);
     return JS_FALSE;
 }
 
-static JS_ALWAYS_INLINE bool
-EqualObjects(JSContext *cx, JSObject *lobj, JSObject *robj)
-{
-    return lobj == robj;
-}
-
 bool
-StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref)
+StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, JSBool *equal)
 {
     Value lval = lref, rval = rref;
     if (SameType(lval, rval)) {
         if (lval.isString())
-            return js_EqualStrings(lval.toString(), rval.toString());
-        if (lval.isDouble())
-            return JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE);
-        if (lval.isObject())
-            return EqualObjects(cx, &lval.toObject(), &rval.toObject());
-        if (lval.isUndefined())
-                return true;
-        return lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
+            return EqualStrings(cx, lval.toString(), rval.toString(), equal);
+        if (lval.isDouble()) {
+            *equal = JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE);
+            return true;
+        }
+        if (lval.isObject()) {
+            *equal = &lval.toObject() == &rval.toObject();
+            return true;
+        }
+        if (lval.isUndefined()) {
+            *equal = true;
+            return true;
+        }
+        *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
+        return true;
     }
 
     if (lval.isDouble() && rval.isInt32()) {
         double ld = lval.toDouble();
         double rd = rval.toInt32();
-        return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
+        *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
+        return true;
     }
     if (lval.isInt32() && rval.isDouble()) {
         double ld = lval.toInt32();
         double rd = rval.toDouble();
-        return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
-    }
-
-    return false;
+        *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
+        return true;
+    }
+
+    *equal = false;
+    return true;
 }
 
 static inline bool
 IsNegativeZero(const Value &v)
 {
     return v.isDouble() && JSDOUBLE_IS_NEGZERO(v.toDouble());
 }
 
 static inline bool
 IsNaN(const Value &v)
 {
     return v.isDouble() && JSDOUBLE_IS_NaN(v.toDouble());
 }
 
 bool
-SameValue(const Value &v1, const Value &v2, JSContext *cx)
-{
-    if (IsNegativeZero(v1))
-        return IsNegativeZero(v2);
-    if (IsNegativeZero(v2))
-        return false;
-    if (IsNaN(v1) && IsNaN(v2))
+SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same)
+{
+    if (IsNegativeZero(v1)) {
+        *same = IsNegativeZero(v2);
         return true;
-    return StrictlyEqual(cx, v1, v2);
+    }
+    if (IsNegativeZero(v2)) {
+        *same = false;
+        return true;
+    }
+    if (IsNaN(v1) && IsNaN(v2)) {
+        *same = true;
+        return true;
+    }
+    return StrictlyEqual(cx, v1, v2, same);
 }
 
 JSType
 TypeOfValue(JSContext *cx, const Value &vref)
 {
     Value v = vref;
     if (v.isNumber())
         return JSTYPE_NUMBER;
@@ -1528,17 +1529,16 @@ js::GetUpvar(JSContext *cx, uintN closur
 
 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
 js_LogOpcode(JSContext *cx)
 {
     FILE *logfp;
     JSStackFrame *fp;
     JSFrameRegs *regs;
     intN ndefs, n, nuses;
-    JSString *str;
     JSOp op;
 
     logfp = (FILE *) cx->logfp;
     JS_ASSERT(logfp);
     fp = cx->fp();
     regs = cx->regs;
 
     /*
@@ -1574,22 +1574,23 @@ js_LogOpcode(JSContext *cx)
         for (Value *siter = fp->base(); siter < regs->sp; siter++) {
             if (siter->isObject() && siter->toObject().getClass() == &js_CallClass) {
                 /*
                  * Call objects have NULL convert ops so that we catch cases
                  * where they escape. So js_ValueToString doesn't work on them.
                  */
                 fputs("<call>", logfp);
             } else {
-                str = js_ValueToString(cx, *siter);
-                if (!str) {
+                JSString *str = js_ValueToString(cx, *siter);
+                JSLinearString *linearStr = str ? str->ensureLinear(cx) : NULL;
+                if (!linearStr) {
                     fputs("<null>", logfp);
+                    JS_ClearPendingException(cx);
                 } else {
-                    JS_ClearPendingException(cx);
-                    FileEscapedString(logfp, str, 0);
+                    FileEscapedString(logfp, linearStr, 0);
                 }
             }
             fputc(' ', logfp);
         }
         fputc('\n', logfp);
     }
 
     fprintf(logfp, "%4u: ",
@@ -2204,22 +2205,24 @@ static inline bool
 ScriptPrologue(JSContext *cx, JSStackFrame *fp)
 {
     if (fp->isConstructing()) {
         JSObject *obj = js_CreateThisForFunction(cx, &fp->callee());
         if (!obj)
             return false;
         fp->functionThis().setObject(*obj);
     }
-    JSInterpreterHook hook = cx->debugHooks->callHook;
-    if (JS_UNLIKELY(hook != NULL) && !fp->isExecuteFrame())
-        fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
-
-    if (!fp->isExecuteFrame())
+    if (fp->isExecuteFrame()) {
+        if (JSInterpreterHook hook = cx->debugHooks->executeHook)
+            fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->executeHookData));
+    } else {
+        if (JSInterpreterHook hook = cx->debugHooks->callHook)
+            fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
         Probes::enterJSFun(cx, fp->maybeFun(), fp->maybeScript());
+    }
 
     return true;
 }
 
 namespace js {
 
 JS_REQUIRES_STACK JS_NEVER_INLINE bool
 Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInterpMode interpMode)
@@ -2522,19 +2525,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)
@@ -3094,18 +3094,16 @@ BEGIN_CASE(JSOP_MOREITER)
 {
     JS_ASSERT(regs.sp - 1 >= regs.fp->base());
     JS_ASSERT(regs.sp[-1].isObject());
     PUSH_NULL();
     bool cond;
     if (!IteratorMore(cx, &regs.sp[-2].toObject(), &cond, &regs.sp[-1]))
         goto error;
     CHECK_INTERRUPT_HANDLER();
-    TRY_BRANCH_AFTER_COND(cond, 1);
-    JS_ASSERT(regs.pc[1] == JSOP_IFNEX);
     regs.sp[-1].setBoolean(cond);
 }
 END_CASE(JSOP_MOREITER)
 
 BEGIN_CASE(JSOP_ENDITER)
 {
     JS_ASSERT(regs.sp - 1 >= regs.fp->base());
     bool ok = !!js_CloseIterator(cx, &regs.sp[-1].toObject());
@@ -3424,17 +3422,20 @@ END_CASE(JSOP_BITAND)
     JS_BEGIN_MACRO                                                            \
         JSBool cond;                                                          \
         Value rval = regs.sp[-1];                                             \
         Value lval = regs.sp[-2];                                             \
         XML_EQUALITY_OP(OP)                                                   \
         if (SameType(lval, rval)) {                                           \
             if (lval.isString()) {                                            \
                 JSString *l = lval.toString(), *r = rval.toString();          \
-                cond = js_EqualStrings(l, r) OP JS_TRUE;                      \
+                JSBool equal;                                                 \
+                if (!EqualStrings(cx, l, r, &equal))                          \
+                    goto error;                                               \
+                cond = equal OP JS_TRUE;                                      \
             } else if (lval.isDouble()) {                                     \
                 double l = lval.toDouble(), r = rval.toDouble();              \
                 cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN);                     \
             } else if (lval.isObject()) {                                     \
                 JSObject *l = &lval.toObject(), *r = &rval.toObject();        \
                 l->assertSpecialEqualitySynced();                             \
                 if (EqualityOp eq = l->getClass()->ext.equality) {            \
                     if (!eq(cx, l, &rval, &cond))                             \
@@ -3453,17 +3454,20 @@ END_CASE(JSOP_BITAND)
                 cond = true OP false;                                         \
             } else {                                                          \
                 if (lval.isObject())                                          \
                     DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);                 \
                 if (rval.isObject())                                          \
                     DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);                 \
                 if (lval.isString() && rval.isString()) {                     \
                     JSString *l = lval.toString(), *r = rval.toString();      \
-                    cond = js_EqualStrings(l, r) OP JS_TRUE;                  \
+                    JSBool equal;                                             \
+                    if (!EqualStrings(cx, l, r, &equal))                      \
+                        goto error;                                           \
+                    cond = equal OP JS_TRUE;                                  \
                 } else {                                                      \
                     double l, r;                                              \
                     if (!ValueToNumber(cx, lval, &l) ||                       \
                         !ValueToNumber(cx, rval, &r)) {                       \
                         goto error;                                           \
                     }                                                         \
                     cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN);                 \
                 }                                                             \
@@ -3485,17 +3489,20 @@ END_CASE(JSOP_NE)
 #undef EQUALITY_OP
 #undef XML_EQUALITY_OP
 #undef EXTENDED_EQUALITY_OP
 
 #define STRICT_EQUALITY_OP(OP, COND)                                          \
     JS_BEGIN_MACRO                                                            \
         const Value &rref = regs.sp[-1];                                      \
         const Value &lref = regs.sp[-2];                                      \
-        COND = StrictlyEqual(cx, lref, rref) OP true;                         \
+        JSBool equal;                                                         \
+        if (!StrictlyEqual(cx, lref, rref, &equal))                           \
+            goto error;                                                       \
+        COND = equal OP true;                                                 \
         regs.sp--;                                                            \
     JS_END_MACRO
 
 BEGIN_CASE(JSOP_STRICTEQ)
 {
     bool cond;
     STRICT_EQUALITY_OP(==, cond);
     regs.sp[-1].setBoolean(cond);
@@ -3546,17 +3553,20 @@ END_CASE(JSOP_CASEX)
             cond = lval.toInt32() OP rval.toInt32();                          \
         } else {                                                              \
             if (lval.isObject())                                              \
                 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval);                   \
             if (rval.isObject())                                              \
                 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval);                   \
             if (lval.isString() && rval.isString()) {                         \
                 JSString *l = lval.toString(), *r = rval.toString();          \
-                cond = js_CompareStrings(l, r) OP 0;                          \
+                int32 result;                                                 \
+                if (!CompareStrings(cx, l, r, &result))                       \
+                    goto error;                                               \
+                cond = result OP 0;                                           \
             } else {                                                          \
                 double l, r;                                                  \
                 if (!ValueToNumber(cx, lval, &l) ||                           \
                     !ValueToNumber(cx, rval, &r)) {                           \
                     goto error;                                               \
                 }                                                             \
                 cond = JSDOUBLE_COMPARE(l, OP, r, false);                     \
             }                                                                 \
@@ -4119,18 +4129,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;
@@ -4339,18 +4349,17 @@ BEGIN_CASE(JSOP_CALLPROP)
 #endif
     if (rval.isUndefined())
         script->typeMonitorUndefined(cx, regs.pc, 0);
 }
 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];
@@ -4734,17 +4743,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)) {
@@ -5169,22 +5178,24 @@ BEGIN_CASE(JSOP_LOOKUPSWITCH)
         pc2 += off;                                                           \
         if (--npairs == 0) {                                                  \
             pc2 = regs.pc;                                                    \
             break;                                                            \
         }                                                                     \
     }
 
     if (lval.isString()) {
-        JSString *str = lval.toString();
-        JSString *str2;
+        JSLinearString *str = lval.toString()->ensureLinear(cx);
+        if (!str)
+            goto error;
+        JSLinearString *str2;
         SEARCH_PAIRS(
             match = (rval.isString() &&
-                     ((str2 = rval.toString()) == str ||
-                      js_EqualStrings(str2, str)));
+                     ((str2 = rval.toString()->assertIsLinear()) == str ||
+                      EqualStrings(str2, str)));
         )
     } else if (lval.isNumber()) {
         double ldbl = lval.toNumber();
         SEARCH_PAIRS(
             match = rval.isNumber() && ldbl == rval.toNumber();
         )
     } else {
         SEARCH_PAIRS(
@@ -5963,17 +5974,17 @@ 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;
 
@@ -5985,20 +5996,18 @@ BEGIN_CASE(JSOP_NEWINIT)
     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;
 
     TypeObject *type = script->getTypeInitObject(cx, regs.pc, true);
     if (!type)
         goto error;
     obj->setType(type);
 
     PUSH_OBJECT(*obj);
@@ -6155,17 +6164,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()) {
@@ -6919,17 +6928,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() */
@@ -1024,21 +1024,21 @@ RunScript(JSContext *cx, JSScript *scrip
 
 #define JSPROP_INITIALIZER 0x100   /* NB: Not a valid property attribute. */
 
 extern bool
 CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
                    JSObject **objp, JSProperty **propp);
 
 extern bool
-StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval);
+StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *equal);
 
 /* === except that NaN is the same as NaN and -0 is not the same as +0. */
 extern bool
-SameValue(const Value &v1, const Value &v2, JSContext *cx);
+SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same);
 
 extern JSType
 TypeOfValue(JSContext *cx, const Value &v);
 
 inline bool
 InstanceOf(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
 {
     if (obj && obj->getClass() == clasp)
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -708,35 +708,36 @@ ValuePropertyBearer(JSContext *cx, const
 }
 
 static inline bool
 ScriptEpilogue(JSContext *cx, JSStackFrame *fp, JSBool ok)
 {
     if (!fp->isExecuteFrame())
         Probes::exitJSFun(cx, fp->maybeFun(), fp->maybeScript());
 
-    JSInterpreterHook hook = cx->debugHooks->callHook;
+    JSInterpreterHook hook =
+        fp->isExecuteFrame() ? cx->debugHooks->executeHook : cx->debugHooks->callHook;
+
     void* hookData;
-
-    if (hook && (hookData = fp->maybeHookData()) && !fp->isExecuteFrame())
+    if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData()))
         hook(cx, fp, JS_FALSE, &ok, hookData);
 
     /*
      * An eval frame's parent owns its activation objects. A yielding frame's
      * activation objects are transferred to the floating frame, stored in the
      * generator.
      */
     if (fp->isFunctionFrame() && !fp->isEvalFrame() && !fp->isYielding())
         PutActivationObjects(cx, fp);
 
     /*
      * If inline-constructing, replace primitive rval with the new object
      * passed in via |this|, and instrument this constructor invocation.
      */
-    if (fp->isConstructing()) {
+    if (fp->isConstructing() && ok) {
         if (fp->returnValue().isPrimitive())
             fp->setReturnValue(ObjectValue(fp->constructorThis()));
         JS_RUNTIME_METER(cx->runtime, constructs);
     }
 
     return ok;
 }
 
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -163,17 +163,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
 {
@@ -623,16 +623,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;
 
@@ -662,16 +670,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
@@ -699,16 +708,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;
                 }
             }
         }
 
@@ -975,16 +985,17 @@ public:
 
     bool operator()(jsid id) { return id == this->id; }
     bool matchesAtMostOne() { return true; }
 };
 
 bool
 js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)