Get merge building js shell and passing trace/ref tests. Still need to fix up browser
authorLuke Wagner <lw@mozilla.com>
Sun, 04 Jul 2010 00:12:06 -0700
changeset 53059 bf88f75a66ce7f75b7b558210469ca38296cab96
parent 53058 65081752a7c809d478743141fa5d5bea0b1f68c0
child 53060 1ac576188ed6b152d56a3ed2796792af79af6c5d
push idunknown
push userunknown
push dateunknown
milestone2.0b2pre
Get merge building js shell and passing trace/ref tests. Still need to fix up browser
js/jetpack/Handle.h
js/jetpack/JetpackActorCommon.cpp
js/jetpack/JetpackChild.cpp
js/jetpack/JetpackChild.h
js/src/jsapi-tests/testConservativeGC.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsbuiltins.cpp
js/src/jsbuiltins.h
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jsdate.cpp
js/src/jsdbgapi.cpp
js/src/jsemit.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jsiter.h
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsops.cpp
js/src/jsparse.cpp
js/src/jsproxy.cpp
js/src/jsproxy.h
js/src/jsrecursion.cpp
js/src/jsregexp.cpp
js/src/jsscope.cpp
js/src/jsscopeinlines.h
js/src/jsstr.cpp
js/src/jstl.h
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jstypedarray.cpp
js/src/jsval.h
js/src/jsvalue.h
js/src/jswrapper.cpp
js/src/jswrapper.h
js/src/jsxml.cpp
js/src/jsxml.h
js/src/shell/js.cpp
js/src/trace-test/tests/basic/delete-named-names.js
js/src/xpconnect/src/qsgen.py
--- a/js/jetpack/Handle.h
+++ b/js/jetpack/Handle.h
@@ -155,32 +155,32 @@ private:
 
   // Used to cache the JSObject returned by ToJSObject, which is
   // otherwise a const method.
   mutable JSObject*  mObj;
   mutable JSRuntime* mRuntime;
 
   static Handle*
   Unwrap(JSContext* cx, JSObject* obj) {
-    while (obj && obj->getClass() != &sHandle_JSClass)
+    while (obj && obj->getJSClass() != &sHandle_JSClass)
       obj = obj->getProto();
 
     if (!obj)
       return NULL;
 
     Handle* self = static_cast<Handle*>(JS_GetPrivate(cx, obj));
 
     NS_ASSERTION(!self || self->ToJSObject(cx) == obj,
                  "Wrapper and wrapped object disagree?");
 
     return self;
   }
 
   static JSBool
-  GetParent(JSContext* cx, JSObject* obj, jsval, jsval* vp) {
+  GetParent(JSContext* cx, JSObject* obj, jsid, jsval* vp) {
     JS_SET_RVAL(cx, vp, JSVAL_NULL);
 
     Handle* self = Unwrap(cx, obj);
     if (!self)
       return JS_TRUE;
 
     Handle* parent = self->mParent;
     if (!parent)
@@ -188,17 +188,17 @@ private:
 
     JSObject* pobj = parent->ToJSObject(cx);
     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(pobj));
 
     return JS_TRUE;
   }
 
   static JSBool
-  GetIsValid(JSContext* cx, JSObject* obj, jsval, jsval* vp) {
+  GetIsValid(JSContext* cx, JSObject* obj, jsid, jsval* vp) {
     Handle* self = Unwrap(cx, obj);
     JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(!!self));
     return JS_TRUE;
   }
 
   static JSBool
   Invalidate(JSContext* cx, uintN argc, jsval* vp) {
     if (argc > 0) {
--- a/js/jetpack/JetpackActorCommon.cpp
+++ b/js/jetpack/JetpackActorCommon.cpp
@@ -141,17 +141,17 @@ JetpackActorCommon::jsval_to_PrimVariant
     *to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(from)),
                             JS_GetStringLength(JSVAL_TO_STRING(from)));
     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);
+      *to = JSVAL_TO_DOUBLE(from);
     else
       return false;
     return true;
 
   case JSTYPE_BOOLEAN:
     *to = !!JSVAL_TO_BOOLEAN(from);
     return true;
 
@@ -273,17 +273,17 @@ JetpackActorCommon::jsval_from_PrimVaria
     *to = from.get_bool() ? JSVAL_TRUE : JSVAL_FALSE;
     return true;
 
   case PrimVariant::Tint:
     *to = INT_TO_JSVAL(from.get_int());
     return true;
 
   case PrimVariant::Tdouble:
-    return !!JS_NewDoubleValue(cx, from.get_double(), to);
+    return !!JS_NewNumberValue(cx, from.get_double(), to);
 
   case PrimVariant::TnsString: {
     const nsString& str = from.get_nsString();
     // TODO Use some sort of sharedstring/stringbuffer abstraction to
     // exploit sharing opportunities more generally.
     if (!str.Length()) {
       *to = JS_GetEmptyStringValue(cx);
       return true;
@@ -344,21 +344,21 @@ JetpackActorCommon::jsval_from_CompVaria
     OpaqueSeenType::IdType ignored;
     if (!seen->add(obj, &ignored))
       return false;
 
     const nsTArray<KeyValue>& kvs = from.get_ArrayOfKeyValue();
     for (PRUint32 i = 0; i < kvs.Length(); ++i) {
       const KeyValue& kv = kvs.ElementAt(i);
       js::AutoValueRooter toSet(cx);
-      if (!jsval_from_Variant(cx, kv.value(), toSet.addr(), seen) ||
+      if (!jsval_from_Variant(cx, kv.value(), toSet.jsval_addr(), seen) ||
           !JS_SetUCProperty(cx, obj,
                             kv.key().get(),
                             kv.key().Length(),
-                            toSet.addr()))
+                            toSet.jsval_addr()))
         return false;
     }
 
     break;
   }
 
   case CompVariant::TArrayOfVariant: {
     const nsTArray<Variant>& vs = from.get_ArrayOfVariant();
@@ -452,24 +452,24 @@ JetpackActorCommon::RecvMessage(JSContex
   JSObject* implGlobal = JS_GetGlobalObject(cx);
   js::AutoValueRooter rval(cx);
 
   const uint32 savedOptions =
     JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT);
 
   for (PRUint32 i = 0; i < snapshot.Length(); ++i) {
     Variant* vp = results ? results->AppendElement() : NULL;
-    rval.set(JSVAL_VOID);
+    rval.jsval_set(JSVAL_VOID);
     if (!JS_CallFunctionValue(cx, implGlobal, snapshot[i], argc, argv,
-                              rval.addr())) {
+                              rval.jsval_addr())) {
       // If a receiver throws, we drop the exception on the floor.
       JS_ClearPendingException(cx);
       if (vp)
         *vp = void_t();
-    } else if (vp && !jsval_to_Variant(cx, rval.value(), vp))
+    } else if (vp && !jsval_to_Variant(cx, rval.jsval_value(), vp))
       *vp = void_t();
   }
 
   JS_SetOptions(cx, savedOptions);
 
   return true;
 }
 
--- a/js/jetpack/JetpackChild.cpp
+++ b/js/jetpack/JetpackChild.cpp
@@ -150,17 +150,17 @@ JetpackChild::RecvSendMessage(const nsSt
 }
 
 static bool
 Evaluate(JSContext* cx, const nsCString& code)
 {
   JSAutoRequest request(cx);
   js::AutoValueRooter ignored(cx);
   JS_EvaluateScript(cx, JS_GetGlobalObject(cx), code.get(),
-                    code.Length(), "", 1, ignored.addr());
+                    code.Length(), "", 1, ignored.jsval_addr());
   return true;
 }
 
 bool
 JetpackChild::RecvLoadImplementation(const nsCString& code)
 {
   return Evaluate(mImplCx, code);
 }
@@ -190,17 +190,17 @@ JetpackChild::GetThis(JSContext* cx)
   JetpackChild* self =
     static_cast<JetpackChild*>(JS_GetContextPrivate(cx));
   JS_ASSERT(cx == self->mImplCx ||
             cx == self->mUserCx);
   return self;
 }
 
 JSBool
-JetpackChild::UserJetpackGetter(JSContext* cx, JSObject* obj, jsval id,
+JetpackChild::UserJetpackGetter(JSContext* cx, JSObject* obj, jsid id,
                                 jsval* vp)
 {
   JSObject* userGlobal = JS_GetGlobalObject(GetThis(cx)->mUserCx);
   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(userGlobal));
   return JS_TRUE;
 }
 
 struct MessageResult {
--- a/js/jetpack/JetpackChild.h
+++ b/js/jetpack/JetpackChild.h
@@ -75,17 +75,17 @@ protected:
 
 private:
   JSRuntime* mRuntime;
   JSContext *mImplCx, *mUserCx;
 
   static JetpackChild* GetThis(JSContext* cx);
 
   static const JSPropertySpec sImplProperties[];
-  static JSBool UserJetpackGetter(JSContext* cx, JSObject* obj, jsval idval,
+  static JSBool UserJetpackGetter(JSContext* cx, JSObject* obj, jsid id,
                                   jsval* vp);
 
   static const JSFunctionSpec sImplMethods[];
   static JSBool SendMessage(JSContext* cx, uintN argc, jsval *vp);
   static JSBool CallMessage(JSContext* cx, uintN argc, jsval *vp);
   static JSBool RegisterReceiver(JSContext* cx, uintN argc, jsval *vp);
   static JSBool UnregisterReceiver(JSContext* cx, uintN argc, jsval *vp);
   static JSBool UnregisterReceivers(JSContext* cx, uintN argc, jsval *vp);
--- a/js/src/jsapi-tests/testConservativeGC.cpp
+++ b/js/src/jsapi-tests/testConservativeGC.cpp
@@ -1,35 +1,25 @@
 #include "tests.h"
 #include "jsobj.h"
 #include "jsstr.h"
 
 BEGIN_TEST(testConservativeGC)
 {
-    jsval v1;
-    EVAL("Math.sqrt(42);", &v1);
-    CHECK(JSVAL_IS_DOUBLE(v1));
-    double numCopy = *JSVAL_TO_DOUBLE(v1);
-
     jsval v2;
     EVAL("({foo: 'bar'});", &v2);
     CHECK(JSVAL_IS_OBJECT(v2));
     JSObject objCopy = *JSVAL_TO_OBJECT(v2);
 
     jsval v3;
     EVAL("String(Math.PI);", &v3);
     CHECK(JSVAL_IS_STRING(v3));
     JSString strCopy = *JSVAL_TO_STRING(v3);
 
     jsval tmp;
-    EVAL("Math.sqrt(41);", &tmp);
-    CHECK(JSVAL_IS_DOUBLE(tmp));
-    jsdouble *num2 = JSVAL_TO_DOUBLE(tmp);
-    jsdouble num2Copy = *num2;
-
     EVAL("({foo2: 'bar2'});", &tmp);
     CHECK(JSVAL_IS_OBJECT(tmp));
     JSObject *obj2 = JSVAL_TO_OBJECT(tmp);
     JSObject obj2Copy = *obj2;
 
     EVAL("String(Math.sqrt(3));", &tmp);
     CHECK(JSVAL_IS_STRING(tmp));
     JSString *str2 = JSVAL_TO_STRING(tmp);
@@ -41,19 +31,17 @@ BEGIN_TEST(testConservativeGC)
 
     EVAL("var a = [];\n"
          "for (var i = 0; i != 10000; ++i) {\n"
          "a.push(i + 0.1, [1, 2], String(Math.sqrt(i)));\n"
          "}", &tmp);
 
     JS_GC(cx);
 
-    CHECK(numCopy == *JSVAL_TO_DOUBLE(v1));
     CHECK(!memcmp(&objCopy,  JSVAL_TO_OBJECT(v2), sizeof(objCopy)));
     CHECK(!memcmp(&strCopy,  JSVAL_TO_STRING(v3), sizeof(strCopy)));
 
-    CHECK(num2Copy == *num2);
     CHECK(!memcmp(&obj2Copy,  obj2, sizeof(obj2Copy)));
     CHECK(!memcmp(&str2Copy,  str2, sizeof(str2Copy)));
 
     return true;
 }
 END_TEST(testConservativeGC)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -88,16 +88,17 @@
 #include "prmjtime.h"
 #include "jsstaticcheck.h"
 #include "jsvector.h"
 #include "jswrapper.h"
 #include "jstypedarray.h"
 
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
+#include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jscntxtinlines.h"
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 using namespace js;
@@ -187,17 +188,17 @@ JS_ConvertArgumentsVA(JSContext *cx, uin
     JSBool required;
     char c;
     JSFunction *fun;
     jsdouble d;
     JSString *str;
     JSObject *obj;
 
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, ValueArray(argv - 2, argc + 2));
+    assertSameCompartment(cx, JSValueArray(argv - 2, argc + 2));
     sp = argv;
     required = JS_TRUE;
     while ((c = *format++) != '\0') {
         if (isspace(c))
             continue;
         if (c == '/') {
             required = JS_FALSE;
             continue;
@@ -263,23 +264,21 @@ JS_ConvertArgumentsVA(JSContext *cx, uin
                 if (!chars)
                     return JS_FALSE;
                 *va_arg(ap, const jschar **) = chars;
             } else {
                 *va_arg(ap, JSString **) = str;
             }
             break;
           case 'o':
-          {
-            Value objv;
-            if (!js_ValueToObjectOrNull(cx, Valueify(*sp), Valueify(sp)))
+            if (!js_ValueToObjectOrNull(cx, Valueify(*sp), &obj))
                 return JS_FALSE;
-            *va_arg(ap, JSObject **) = JSVAL_TO_OBJECT(*sp);
+            *sp = OBJECT_TO_JSVAL(obj);
+            *va_arg(ap, JSObject **) = obj;
             break;
-          }
           case 'f':
             obj = js_ValueToFunctionObject(cx, Valueify(sp), 0);
             if (!obj)
                 return JS_FALSE;
             *sp = OBJECT_TO_JSVAL(obj);
             *va_arg(ap, JSFunction **) = GET_FUNCTION_PRIVATE(cx, obj);
             break;
           case 'v':
@@ -358,17 +357,19 @@ JS_ConvertValue(JSContext *cx, jsval v, 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
     switch (type) {
       case JSTYPE_VOID:
         *vp = JSVAL_VOID;
         ok = JS_TRUE;
         break;
       case JSTYPE_OBJECT:
-        ok = js_ValueToObjectOrNull(cx, Valueify(v), Valueify(vp));
+        ok = js_ValueToObjectOrNull(cx, Valueify(v), &obj);
+        if (ok)
+            *vp = OBJECT_TO_JSVAL(obj);
         break;
       case JSTYPE_FUNCTION:
         *vp = v;
         obj = js_ValueToFunctionObject(cx, Valueify(vp), JSV2F_SEARCH_STACK);
         ok = (obj != NULL);
         break;
       case JSTYPE_STRING:
         str = js_ValueToString(cx, Valueify(v));
@@ -395,20 +396,17 @@ JS_ConvertValue(JSContext *cx, jsval v, 
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
-    Value objv;
-    bool ok = !!js_ValueToObjectOrNull(cx, Valueify(v), &objv);
-    *objp = objv.asObjectOrNull();
-    return ok;
+    return js_ValueToObjectOrNull(cx, Valueify(v), objp);
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS_ValueToFunction(JSContext *cx, jsval v)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
     return js_ValueToFunction(cx, Valueify(&v), JSV2F_SEARCH_STACK);
@@ -1077,16 +1075,17 @@ JS_SetWrapObjectCallback(JSContext *cx, 
     return old;
 }
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target)
 {
     CHECK_REQUEST(cx);
 
+    JS_ASSERT(target);
     AutoCompartment *call = new AutoCompartment(cx, target);
     if (!call)
         return NULL;
     if (!call->enter()) {
         delete call;
         return NULL;
     }
     return reinterpret_cast<JSCrossCompartmentCall *>(call);
@@ -1197,19 +1196,19 @@ js_InitFunctionAndObjectClasses(JSContex
     if (!obj_proto)
         obj_proto = js_InitObjectClass(cx, obj);
     if (!obj_proto) {
         fun_proto = NULL;
         goto out;
     }
 
     /* Function.prototype and the global object delegate to Object.prototype. */
-    fun_proto->setProto(ObjectTag(*obj_proto));
+    fun_proto->setProto(obj_proto);
     if (!obj->getProto())
-        obj->setProto(ObjectTag(*obj_proto));
+        obj->setProto(obj_proto);
 
 out:
     /* If resolving, remove the other entry (Object or Function) from table. */
     JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE);
     if (!resolving) {
         /* If not resolving, remove the first entry added above, for Object. */
         JS_ASSERT(key.id ==                                                   \
                   ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]));
@@ -1706,17 +1705,17 @@ JS_GetScopeChain(JSContext *cx)
          * acting as a stand-in.
          */
         JSObject *obj = cx->globalObject;
         if (!obj) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
             return NULL;
         }
 
-        Innerize(cx, &obj);
+        OBJ_TO_INNER_OBJECT(cx, obj);
         return obj;
     }
     return js_GetScopeChain(cx, fp);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetGlobalForObject(JSContext *cx, JSObject *obj)
 {
@@ -1733,32 +1732,32 @@ JS_GetGlobalForScopeChain(JSContext *cx)
      *
      * This use of cx->fp, possibly on trace, is deliberate:
      * cx->fp->scopeChain->getGlobal() returns the same object whether we're on
      * trace or not, since we do not trace calls across global objects.
      */
     VOUCH_DOES_NOT_REQUIRE_STACK();
 
     if (cx->fp)
-        return cx->fp->scopeChainObj()->getGlobal();
+        return cx->fp->scopeChain->getGlobal();
 
     JSObject *scope = cx->globalObject;
     if (!scope) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
         return NULL;
     }
-    Innerize(cx, &scope);
+    OBJ_TO_INNER_OBJECT(cx, scope);
     return scope;
 }
 
 JS_PUBLIC_API(jsval)
 JS_ComputeThis(JSContext *cx, jsval *vp)
 {
-    assertSameCompartment(cx, ValueArray(vp, 2));
-    if (!ComputeThisFromVpInPlace(cx, Valueify(vp)))
+    assertSameCompartment(cx, JSValueArray(vp, 2));
+    if (!ComputeThisFromVp(cx, Valueify(vp)))
         return JSVAL_NULL;
     return vp[1];
 }
 
 JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes)
 {
     return cx->malloc(nbytes);
@@ -2825,17 +2824,17 @@ JS_NewCompartmentAndGlobalObject(JSConte
     cx->compartment = compartment;
     JSObject *obj = JS_NewGlobalObject(cx, clasp);
     cx->compartment = saved;
 
     return obj;
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
+JS_NewObject(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, proto, parent);
     Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
     JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
     JSObject *obj = NewObject(cx, clasp, proto, parent);
@@ -2935,17 +2934,17 @@ JS_ConstructObject(JSContext *cx, JSClas
     return js_ConstructObject(cx, clasp, proto, parent, 0, NULL);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_ConstructObjectWithArguments(JSContext *cx, JSClass *jsclasp, JSObject *proto,
                                 JSObject *parent, uintN argc, jsval *argv)
 {
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, proto, parent, ValueArray(argv, argc));
+    assertSameCompartment(cx, proto, parent, JSValueArray(argv, argc));
     Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
     return js_ConstructObject(cx, clasp, proto, parent, argc, Valueify(argv));
 }
 
 static JSBool
 LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, uintN flags,
@@ -3687,17 +3686,17 @@ JS_ClearScope(JSContext *cx, JSObject *o
 }
 
 JS_PUBLIC_API(JSIdArray *)
 JS_Enumerate(JSContext *cx, JSObject *obj)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
-    AutoValueVector props(cx);
+    AutoIdVector props(cx);
     JSIdArray *ida;
     if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, props) || !VectorToIdArray(cx, props, &ida))
         return false;
     for (size_t n = 0; n < size_t(ida->length); ++n)
         JS_ASSERT(js_CheckForStringIndex(ida->vector[n]) == ida->vector[n]);
     return ida;
 }
 
@@ -3856,17 +3855,17 @@ JS_SetReservedSlot(JSContext *cx, JSObje
     return js_SetReservedSlot(cx, obj, index, Valueify(v));
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector)
 {
     CHECK_REQUEST(cx);
     /* NB: jsuint cast does ToUint32. */
-    assertSameCompartment(cx, ValueArray(vector, vector ? (jsuint)length : 0));
+    assertSameCompartment(cx, JSValueArray(vector, vector ? (jsuint)length : 0));
     return js_NewArrayObject(cx, (jsuint)length, Valueify(vector));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsArrayObject(JSContext *cx, JSObject *obj)
 {
     assertSameCompartment(cx, obj);
     return obj->wrappedObject(cx)->isArray();
@@ -3897,17 +3896,17 @@ JS_HasArrayLength(JSContext *cx, JSObjec
 }
 
 JS_PUBLIC_API(JSBool)
 JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
                jsval *vp, uintN *attrsp)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
-    return CheckAccess(cx, obj, id, mode, vp, attrsp);
+    return CheckAccess(cx, obj, id, mode, Valueify(vp), attrsp);
 }
 
 #ifdef JS_THREADSAFE
 JS_PUBLIC_API(jsrefcount)
 JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals)
 {
     return JS_ATOMIC_INCREMENT(&principals->refcount);
 }
@@ -4094,33 +4093,35 @@ JS_ObjectIsFunction(JSContext *cx, JSObj
 {
     return obj->getClass() == &js_FunctionClass;
 }
 
 static JSBool
 js_generic_fast_native_method_dispatcher(JSContext *cx, uintN argc, Value *vp)
 {
     JSFunctionSpec *fs;
+    JSObject *tmp;
     FastNative native;
 
     fs = (JSFunctionSpec *) vp->asObject().getReservedSlot(0).asPrivate();
     JS_ASSERT((~fs->flags & (JSFUN_FAST_NATIVE | JSFUN_GENERIC_NATIVE)) == 0);
 
     if (argc < 1) {
         js_ReportMissingArg(cx, *vp, 0);
         return JS_FALSE;
     }
 
     if (vp[2].isPrimitive()) {
         /*
          * Make sure that this is an object or null, as required by the generic
          * functions.
          */
-        if (!js_ValueToObjectOrNull(cx, vp[2], &vp[2]))
+        if (!js_ValueToObjectOrNull(cx, vp[2], &tmp))
             return JS_FALSE;
+        vp[2].setObjectOrNull(tmp);
     }
 
     /*
      * Copy all actual (argc) arguments down over our |this| parameter, vp[1],
      * which is almost always the class constructor object, e.g. Array.  Then
      * call the corresponding prototype native method with our first argument
      * passed as |this|.
      */
@@ -4146,33 +4147,35 @@ js_generic_fast_native_method_dispatcher
     return native(cx, argc, vp);
 }
 
 static JSBool
 js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj,
                                     uintN argc, Value *argv, Value *rval)
 {
     JSFunctionSpec *fs;
+    JSObject *tmp;
 
     fs = (JSFunctionSpec *) argv[-2].asObject().getReservedSlot(0).asPrivate();
     JS_ASSERT((fs->flags & (JSFUN_FAST_NATIVE | JSFUN_GENERIC_NATIVE)) ==
               JSFUN_GENERIC_NATIVE);
 
     if (argc < 1) {
         js_ReportMissingArg(cx, *(argv - 2), 0);
         return JS_FALSE;
     }
 
     if (argv[0].isPrimitive()) {
         /*
          * Make sure that this is an object or null, as required by the generic
          * functions.
          */
-        if (!js_ValueToObjectOrNull(cx, argv[0], &argv[0]))
+        if (!js_ValueToObjectOrNull(cx, argv[0], &tmp))
             return JS_FALSE;
+        argv[0].setObjectOrNull(tmp);
     }
 
     /*
      * Copy all actual (argc) arguments down over our |this| parameter,
      * argv[-1], which is almost always the class constructor object, e.g.
      * Array.  Then call the corresponding prototype native method with our
      * first argument passed as |this|.
      */
@@ -4695,28 +4698,28 @@ JS_EvaluateScript(JSContext *cx, JSObjec
 
 JS_PUBLIC_API(JSBool)
 JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, jsval *argv,
                 jsval *rval)
 {
     JSBool ok;
 
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, fun, ValueArray(argv, argc));
+    assertSameCompartment(cx, obj, fun, JSValueArray(argv, argc));
     ok = InternalCall(cx, obj, ObjectTag(*fun), argc, Valueify(argv), Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, jsval *argv,
                     jsval *rval)
 {
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, ValueArray(argv, argc));
+    assertSameCompartment(cx, obj, JSValueArray(argv, argc));
 
     AutoValueRooter tvr(cx);
     JSAtom *atom = js_Atomize(cx, name, strlen(name), 0);
     JSBool ok = atom &&
                 js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, tvr.addr()) &&
                 InternalCall(cx, obj, tvr.value(), argc, Valueify(argv), Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     return ok;
@@ -4724,27 +4727,27 @@ JS_CallFunctionName(JSContext *cx, JSObj
 
 JS_PUBLIC_API(JSBool)
 JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv,
                      jsval *rval)
 {
     JSBool ok;
 
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, fval, ValueArray(argv, argc));
+    assertSameCompartment(cx, obj, fval, JSValueArray(argv, argc));
     ok = InternalCall(cx, obj, Valueify(fval), argc, Valueify(argv), Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     return ok;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv)
 {
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, ctor, ValueArray(argv, argc));
+    assertSameCompartment(cx, ctor, JSValueArray(argv, argc));
 
     // This is not a simple variation of JS_CallFunctionValue because JSOP_NEW
     // is not a simple variation of JSOP_CALL. We have to determine what class
     // of object to create, create it, and clamp the return value to an object,
     // among other details. js_InvokeConstructor does the hard work.
     InvokeArgsGuard args;
     if (!cx->stack().pushInvokeArgs(cx, argc, args))
         return NULL;
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -204,20 +204,17 @@ ValueIsLength(JSContext *cx, Value* vp)
         goto error;
 
     if (JSDOUBLE_IS_NaN(d))
         goto error;
     jsuint length;
     length = (jsuint) d;
     if (d != (jsdouble) length)
         goto error;
-    if (!js_NewNumberInRootedValue(cx, d, vp)) {
-        *vp = JSVAL_NULL;
-        return 0;
-    }
+    vp->setNumber(length);
     return length;
 
   error:
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                          JSMSG_BAD_ARRAY_LENGTH);
     vp->setNull();
     return 0;
 }
@@ -742,17 +739,17 @@ array_getProperty(JSContext *cx, JSObjec
     uint32 i;
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
         vp->setNumber(obj->getArrayLength());
         return JS_TRUE;
     }
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom)) {
-        *vp = obj->getProtoValue();
+        vp->setObjectOrNull(obj->getProto());
         return JS_TRUE;
     }
 
     if (!obj->isDenseArray())
         return js_GetProperty(cx, obj, id, vp);
 
     if (!js_IdIsIndex(id, &i) || i >= obj->getDenseArrayCapacity() ||
         obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
@@ -1136,17 +1133,17 @@ BufferToString(JSContext *cx, JSCharBuff
 }
 
 #if JS_HAS_TOSOURCE
 static JSBool
 array_toSource(JSContext *cx, uintN argc, Value *vp)
 {
     JS_CHECK_RECURSION(cx, return false);
 
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj ||
         (obj->getClass() != &js_SlowArrayClass &&
          !InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
         return false;
     }
 
     /* Find joins or cycles in the reachable object graph. */
     jschar *sharpchars;
@@ -1302,20 +1299,23 @@ array_toString_sub(JSContext *cx, JSObje
             !GetArrayElement(cx, obj, index, &hole, rval)) {
             goto out;
         }
 
         /* Get element's character string. */
         if (!(hole || rval->isNullOrUndefined())) {
             if (locale) {
                 /* Work on obj.toLocalString() instead. */
-                if (!js_ValueToObjectOrNull(cx, *rval, rval))
+                JSObject *robj;
+
+                if (!js_ValueToObjectOrNull(cx, *rval, &robj))
                     goto out;
+                rval->setObjectOrNull(robj);
                 JSAtom *atom = cx->runtime->atomState.toLocaleStringAtom;
-                if (!js_TryMethod(cx, rval->asObjectOrNull(), atom, 0, NULL, rval))
+                if (!js_TryMethod(cx, robj, atom, 0, NULL, rval))
                     goto out;
             }
 
             if (!js_ValueToCharBuffer(cx, *rval, cb))
                 goto out;
         }
 
         /* Append the separator. */
@@ -1337,30 +1337,30 @@ array_toString_sub(JSContext *cx, JSObje
     else
         cx->busyArrays.remove(obj);
     return ok;
 }
 
 static JSBool
 array_toString(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj ||
         (obj->getClass() != &js_SlowArrayClass &&
          !InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
         return JS_FALSE;
     }
 
     return array_toString_sub(cx, obj, JS_FALSE, NULL, vp);
 }
 
 static JSBool
 array_toLocaleString(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj ||
         (obj->getClass() != &js_SlowArrayClass &&
          !InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
         return JS_FALSE;
     }
 
     /*
      *  Passing comma here as the separator. Need a way to get a
@@ -1519,25 +1519,25 @@ array_join(JSContext *cx, uintN argc, Va
     if (argc == 0 || vp[2].isUndefined()) {
         str = NULL;
     } else {
         str = js_ValueToString(cx, vp[2]);
         if (!str)
             return JS_FALSE;
         vp[2].setString(str);
     }
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     return obj && array_toString_sub(cx, obj, JS_FALSE, str, vp);
 }
 
 static JSBool
 array_reverse(JSContext *cx, uintN argc, Value *vp)
 {
     jsuint len;
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &len))
         return JS_FALSE;
     vp->setObject(*obj);
 
     if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) {
         /* An empty array or an array with no elements is already reversed. */
         if (len == 0 || obj->getDenseArrayCapacity() == 0)
             return JS_TRUE;
@@ -1827,17 +1827,17 @@ array_sort(JSContext *cx, uintN argc, Va
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SORT_ARG);
             return false;
         }
         fval = argv[0];     /* non-default compare function */
     } else {
         fval.setNull();
     }
 
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &len))
         return false;
     if (len == 0) {
         vp->setObject(*obj);
         return true;
     }
 
     /*
@@ -2134,17 +2134,17 @@ js_ArrayCompPush_tn(JSContext *cx, JSObj
 }
 JS_DEFINE_CALLINFO_3(extern, BOOL, js_ArrayCompPush_tn, CONTEXT, OBJECT, VALUE, 0,
                      nanojit::ACC_STORE_ANY)
 
 static JSBool
 array_push(JSContext *cx, uintN argc, Value *vp)
 {
     /* Insist on one argument and obj of the expected class. */
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
         return JS_FALSE;
     if (argc != 1 || !obj->isDenseArray())
         return array_push_slowly(cx, obj, argc, vp + 2, vp);
 
     return array_push1_dense(cx, obj, vp[2], vp);
 }
 
@@ -2188,31 +2188,31 @@ array_pop_dense(JSContext *cx, JSObject*
         return JS_FALSE;
     obj->setDenseArrayLength(index);
     return JS_TRUE;
 }
 
 static JSBool
 array_pop(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
         return JS_FALSE;
     if (obj->isDenseArray())
         return array_pop_dense(cx, obj, vp);
     return array_pop_slowly(cx, obj, vp);
 }
 
 static JSBool
 array_shift(JSContext *cx, uintN argc, Value *vp)
 {
     jsuint length, i;
     JSBool hole;
 
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
 
     if (length == 0) {
         vp->setUndefined();
     } else {
         length--;
 
@@ -2254,17 +2254,17 @@ array_shift(JSContext *cx, uintN argc, V
 static JSBool
 array_unshift(JSContext *cx, uintN argc, Value *vp)
 {
     Value *argv;
     jsuint length;
     JSBool hole;
     jsdouble last, newlen;
 
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
     newlen = length;
     if (argc > 0) {
         /* Slide up the array to make room for argc at the bottom. */
         argv = JS_ARGV(cx, vp);
         if (length > 0) {
             if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
@@ -2321,17 +2321,17 @@ array_splice(JSContext *cx, uintN argc, 
     if (!obj2)
         return JS_FALSE;
     vp->setObject(*obj2);
 
     /* Nothing to do if no args.  Otherwise get length. */
     if (argc == 0)
         return JS_TRUE;
     Value *argv = JS_ARGV(cx, vp);
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
 
     /* Convert the first argument into a starting index. */
     jsdouble d;
     if (!ValueToNumber(cx, *argv, &d))
         return JS_FALSE;
     d = js_DoubleToInteger(d);
@@ -2460,17 +2460,17 @@ array_splice(JSContext *cx, uintN argc, 
  */
 static JSBool
 array_concat(JSContext *cx, uintN argc, Value *vp)
 {
     /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
     Value *p = JS_ARGV(cx, vp) - 1;
 
     /* Create a new Array object and root it using *vp. */
-    JSObject *aobj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *aobj = ComputeThisFromVp(cx, vp);
     JSObject *nobj;
     jsuint length;
     if (aobj->isDenseArray()) {
         /*
          * Clone aobj but pass the minimum of its length and capacity, to
          * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In
          * such a case we'll pass 8 (not 3) due to ARRAY_CAPACITY_MIN, which
          * will cause nobj to be over-allocated to 16. But in the normal case
@@ -2550,17 +2550,17 @@ array_slice(JSContext *cx, uintN argc, V
 {
     Value *argv;
     JSObject *nobj;
     jsuint length, begin, end, slot;
     JSBool hole;
 
     argv = JS_ARGV(cx, vp);
 
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
     begin = 0;
     end = length;
 
     if (argc > 0) {
         jsdouble d;
         if (!ValueToNumber(cx, argv[0], &d))
@@ -2627,17 +2627,17 @@ array_slice(JSContext *cx, uintN argc, V
 static JSBool
 array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp)
 {
     jsuint length, i, stop;
     Value tosearch;
     jsint direction;
     JSBool hole;
 
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
     if (length == 0)
         goto not_found;
 
     if (argc <= 1) {
         i = isLast ? length - 1 : 0;
         tosearch = (argc != 0) ? vp[2] : Value(UndefinedTag());
@@ -2716,17 +2716,17 @@ typedef enum ArrayExtraMode {
     EVERY
 } ArrayExtraMode;
 
 #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)
 
 static JSBool
 array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     jsuint length;
     if (!obj || !js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
 
     /*
      * First, get or compute our callee, so that we error out consistently
      * when passed a non-callable object.
      */
@@ -2797,19 +2797,19 @@ array_extra(JSContext *cx, ArrayExtraMod
         break;
     }
 
     if (length == 0)
         return JS_TRUE;
 
     JSObject *thisp;
     if (argc > 1 && !REDUCE_MODE(mode)) {
-        if (!js_ValueToObjectOrNull(cx, argv[1], &argv[1]))
+        if (!js_ValueToObjectOrNull(cx, argv[1], &thisp))
             return JS_FALSE;
-        thisp = argv[1].asObjectOrNull();
+        argv[1].setObjectOrNull(thisp);
     } else {
         thisp = NULL;
     }
 
     /*
      * For all but REDUCE, we call with 3 args (value, index, array). REDUCE
      * requires 4 args (accum, value, index, array).
      */
@@ -3045,17 +3045,17 @@ js_NewEmptyArray(JSContext* cx, JSObject
 
     JSObject* obj = js_NewGCObject(cx);
     if (!obj)
         return NULL;
 
     /* Initialize all fields of JSObject. */
     obj->map = const_cast<JSObjectMap *>(&SharedArrayMap);
 
-    obj->init(&js_ArrayClass, ObjectTag(*proto), proto->getParentValue(), NullTag());
+    obj->init(&js_ArrayClass, proto, proto->getParent(), NullTag());
     obj->setDenseArrayLength(0);
     obj->setDenseArrayCount(0);
     return obj;
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, nanojit::ACC_STORE_ANY)
 #endif
 
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -319,18 +319,17 @@ js_NewNullClosure(JSContext* cx, JSObjec
 
     JSFunction *fun = (JSFunction*) funobj;
     JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun);
 
     JSObject* closure = js_NewGCObject(cx);
     if (!closure)
         return NULL;
 
-    closure->initSharingEmptyScope(&js_FunctionClass, ObjectTag(*proto), ObjectTag(*parent),
-                                   PrivateTag(fun));
+    closure->initSharingEmptyScope(&js_FunctionClass, proto, parent, PrivateTag(fun));
     return closure;
 }
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT, 0,
                      ACC_STORE_ANY)
 
 JS_REQUIRES_STACK JSBool FASTCALL
 js_PopInterpFrame(JSContext* cx, TracerState* state)
 {
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -48,19 +48,17 @@
 #undef THIS
 #endif
 
 enum JSTNErrType { INFALLIBLE, FAIL_STATUS, FAIL_NULL, FAIL_NEG, FAIL_NEITHER };
 enum { 
     JSTN_ERRTYPE_MASK        = 0x07,
     JSTN_UNBOX_AFTER         = 0x08,
     JSTN_MORE                = 0x10,
-    JSTN_CONSTRUCTOR         = 0x20,
-    JSTN_RETURN_NULLABLE_STR = 0x40,
-    JSTN_RETURN_NULLABLE_OBJ = 0x80
+    JSTN_CONSTRUCTOR         = 0x20
 };
 
 #define JSTN_ERRTYPE(jstn)  ((jstn)->flags & JSTN_ERRTYPE_MASK)
 
 /*
  * Type describing a type specialization of a JSFastNative.
  *
  * |prefix| and |argtypes| declare what arguments should be passed to the
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1152,53 +1152,58 @@ struct RootInfo {
     JSGCRootType type;
 };
 
 typedef js::HashMap<void *,
                     RootInfo,
                     js::DefaultHasher<void *>,
                     js::SystemAllocPolicy> RootedValueMap;
 
+/* If HashNumber grows, need to change WrapperHasher. */
+JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
+    
 struct WrapperHasher
 {
-    typedef jsval Lookup;
-    
-    static HashNumber hash(jsval key) {
-        return GCPtrHasher::hash(JSVAL_TO_GCTHING(key));
+    typedef Value Lookup;
+
+    static HashNumber hash(Value key) {
+        uint64 bits = JSVAL_BITS(Jsvalify(key));
+        return (uint32)bits ^ (uint32)(bits >> 32);
     }
 
-    static bool match(jsval l, jsval k) {
+    static bool match(const Value &l, const Value &k) {
         return l == k;
     }
 };
 
-typedef HashMap<void *, jsval, WrapperHasher, SystemAllocPolicy> WrapperMap;
+typedef HashMap<Value, Value, WrapperHasher, SystemAllocPolicy> WrapperMap;
 
 class AutoValueVector;
+class AutoIdVector;
 
 } /* namespace js */
 
 struct JSCompartment {
     JSRuntime *rt;
     JSPrincipals *principals;
     bool marked;
     js::WrapperMap crossCompartmentWrappers;
 
     JSCompartment(JSRuntime *cx);
     ~JSCompartment();
 
     bool init();
 
-    bool wrap(JSContext *cx, jsval *vp);
+    bool wrap(JSContext *cx, js::Value *vp);
     bool wrap(JSContext *cx, JSString **strp);
     bool wrap(JSContext *cx, JSObject **objp);
     bool wrapId(JSContext *cx, jsid *idp);
-    bool wrap(JSContext *cx, JSPropertyOp *op);
-    bool wrap(JSContext *cx, JSPropertyDescriptor *desc);
-    bool wrap(JSContext *cx, js::AutoValueVector &props);
+    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);
 };
 
 struct JSRuntime {
     /* Default compartment. */
     JSCompartment       *defaultCompartment;
@@ -2309,16 +2314,21 @@ class AutoValueRooter : private AutoGCRo
      * a function object. Also, AutoObjectRooter is smaller.
      */
 
     void set(Value v) {
         JS_ASSERT(tag == JSVAL);
         val = v;
     }
 
+    void jsval_set(jsval v) {
+        JS_ASSERT(tag == JSVAL);
+        val = js::Valueify(v);
+    }
+
     const Value &value() const {
         JS_ASSERT(tag == JSVAL);
         return val;
     }
 
     Value *addr() {
         JS_ASSERT(tag == JSVAL);
         return &val;
@@ -2469,33 +2479,33 @@ class AutoScriptRooter : private AutoGCR
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoIdRooter : private AutoGCRooter
 {
   public:
     explicit AutoIdRooter(JSContext *cx, jsid id = INT_TO_JSID(0)
                           JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, ID), idval(id)
+      : AutoGCRooter(cx, ID), id_(id)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     jsid id() {
-        return idval;
+        return id_;
     }
 
     jsid * addr() {
-        return &idval;
+        return &id_;
     }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
   private:
-    jsid idval;
+    jsid id_;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoIdArray : private AutoGCRooter {
   public:
     AutoIdArray(JSContext *cx, JSIdArray *ida JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, IDARRAY), idArray(ida)
     {
@@ -3079,17 +3089,17 @@ class AutoValueVector : private AutoGCRo
     bool resize(size_t newLength) {
         return vector.resize(newLength);
     }
 
     bool reserve(size_t newLength) {
         return vector.reserve(newLength);
     }
 
-    const Value &operator[](size_t i) { return vector[i]; }
+    Value &operator[](size_t i) { return vector[i]; }
     const Value &operator[](size_t i) const { return vector[i]; }
 
     const Value *begin() const { return vector.begin(); }
     Value *begin() { return vector.begin(); }
 
     const Value *end() const { return vector.end(); }
     Value *end() { return vector.end(); }
 
@@ -3125,17 +3135,17 @@ class AutoIdVector : private AutoGCRoote
     bool resize(size_t newLength) {
         return vector.resize(newLength);
     }
 
     bool reserve(size_t newLength) {
         return vector.reserve(newLength);
     }
 
-    const jsid &operator[](size_t i) { return vector[i]; }
+    jsid &operator[](size_t i) { return vector[i]; }
     const jsid &operator[](size_t i) const { return vector[i]; }
 
     const jsid *begin() const { return vector.begin(); }
     jsid *begin() { return vector.begin(); }
 
     const jsid *end() const { return vector.end(); }
     jsid *end() { return vector.end(); }
 
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -234,30 +234,46 @@ class CompartmentChecker
 
     void check(JSPrincipals *) { /* nothing for now */ }
 
     void check(JSObject *obj) {
         if (obj)
             check(obj->getCompartment(context));
     }
 
+    void check(const js::Value &v) {
+        if (v.isObject())
+            check(&v.asObject());
+    }
+
     void check(jsval v) {
-        if (!JSVAL_IS_PRIMITIVE(v))
-            check(JSVAL_TO_OBJECT(v));
+        check(Valueify(v));
     }
 
     void check(const ValueArray &arr) {
         for (size_t i = 0; i < arr.length; i++)
             check(arr.array[i]);
     }
+
+    void check(const JSValueArray &arr) {
+        for (size_t i = 0; i < arr.length; i++)
+            check(arr.array[i]);
+    }
+
+    void check(jsid id) {
+        if (JSID_IS_OBJECT(id))
+            check(JSID_TO_OBJECT(id));
+    }
     
     void check(JSIdArray *ida) {
         if (ida) {
-            for (jsint i = 0; i < ida->length; i++)
-                check(ID_TO_VALUE(ida->vector[i]));
+            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->u.object)
             check(script->u.object);
     }
 
@@ -328,47 +344,47 @@ assertSameCompartment(JSContext *cx, T1 
     c.check(t4);
     c.check(t5);
 #endif
 }
 
 #undef START_ASSERT_SAME_COMPARTMENT
 
 inline JSBool
-callJSNative(JSContext *cx, JSNative native, JSObject *thisobj, uintN argc, jsval *argv, jsval *rval)
+callJSNative(JSContext *cx, js::Native native, JSObject *thisobj, uintN argc, js::Value *argv, js::Value *rval)
 {
     assertSameCompartment(cx, thisobj, ValueArray(argv, argc));
     JSBool ok = native(cx, thisobj, argc, argv, rval);
     if (ok)
         assertSameCompartment(cx, *rval);
     return ok;
 }
 
 inline JSBool
-callJSFastNative(JSContext *cx, JSFastNative native, uintN argc, jsval *vp)
+callJSFastNative(JSContext *cx, js::FastNative native, uintN argc, js::Value *vp)
 {
     assertSameCompartment(cx, ValueArray(vp, argc + 2));
     JSBool ok = native(cx, argc, vp);
     if (ok)
         assertSameCompartment(cx, vp[0]);
     return ok;
 }
 
 inline JSBool
-callJSPropertyOp(JSContext *cx, JSPropertyOp op, JSObject *obj, jsval idval, jsval *vp)
+callJSPropertyOp(JSContext *cx, js::PropertyOp op, JSObject *obj, jsid id, js::Value *vp)
 {
-    assertSameCompartment(cx, obj, idval, *vp);
-    JSBool ok = op(cx, obj, idval, vp);
+    assertSameCompartment(cx, obj, id, *vp);
+    JSBool ok = op(cx, obj, id, vp);
     if (ok)
         assertSameCompartment(cx, obj, *vp);
     return ok;
 }
 
 inline JSBool
-callJSPropertyOpSetter(JSContext *cx, JSPropertyOp op, JSObject *obj, jsval idval, jsval *vp)
+callJSPropertyOpSetter(JSContext *cx, js::PropertyOp op, JSObject *obj, jsid id, js::Value *vp)
 {
-    assertSameCompartment(cx, obj, idval, *vp);
-    return op(cx, obj, idval, vp);
+    assertSameCompartment(cx, obj, id, *vp);
+    return op(cx, obj, id, vp);
 }
 
 }  /* namespace js */
 
 #endif /* jscntxtinlines_h___ */
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -1256,28 +1256,28 @@ GetAndCacheLocalTime(JSContext *cx, JSOb
 /*
  * See ECMA 15.9.5.4 thru 15.9.5.23
  */
 static JSBool
 date_getTime(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 GetYear(JSContext *cx, JSBool fullyear, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result)) {
         result = YearFromTime(result);
 
         /* Follow ECMA-262 to the letter, contrary to IE JScript. */
         if (!fullyear)
             result -= 1900;
@@ -1299,217 +1299,217 @@ date_getFullYear(JSContext *cx, uintN ar
     return GetYear(cx, JS_TRUE, vp);
 }
 
 static JSBool
 date_getUTCFullYear(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = YearFromTime(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getMonth(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = MonthFromTime(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getUTCMonth(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = MonthFromTime(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getDate(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = DateFromTime(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getUTCDate(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = DateFromTime(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getDay(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = WeekDay(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getUTCDay(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = WeekDay(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getHours(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = HourFromTime(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getUTCHours(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = HourFromTime(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getMinutes(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = MinFromTime(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getUTCMinutes(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = MinFromTime(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 /* Date.getSeconds is mapped to getUTCSeconds */
 
 static JSBool
 date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = SecFromTime(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
 
 static JSBool
 date_getUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = msFromTime(result);
 
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_getTimezoneOffset(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj;
     jsdouble utctime, localtime, result;
 
-    obj = ComputeThisObjectFromVp(cx, vp);
+    obj = ComputeThisFromVp(cx, vp);
     if (!GetUTCTime(cx, obj, vp, &utctime))
         return JS_FALSE;
     if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime))
         return JS_FALSE;
 
     /*
      * Return the time zone offset in minutes for the current locale that is
      * appropriate for this time. This value would be a constant except for
@@ -1518,17 +1518,17 @@ date_getTimezoneOffset(JSContext *cx, ui
     result = (utctime - localtime) / msPerMinute;
     vp->setNumber(result);
     return JS_TRUE;
 }
 
 static JSBool
 date_setTime(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!InstanceOf(cx, obj, &js_DateClass, vp + 2))
         return false;
 
     if (argc == 0) {
         SetDateToNaN(cx, obj, vp);
         return true;
     }
 
@@ -1547,17 +1547,17 @@ date_makeTime(JSContext *cx, uintN maxar
     uintN i;
     jsdouble args[4], *argp, *stop;
     jsdouble hour, min, sec, msec;
     jsdouble lorutime; /* Local or UTC version of *date */
 
     jsdouble msec_time;
     jsdouble result;
 
-    obj = ComputeThisObjectFromVp(cx, vp);
+    obj = ComputeThisFromVp(cx, vp);
     if (!GetUTCTime(cx, obj, vp, &result))
         return false;
 
     /* just return NaN if the date is already NaN */
     if (!JSDOUBLE_IS_FINITE(result)) {
         vp->setNumber(result);
         return true;
     }
@@ -1684,17 +1684,17 @@ date_makeDate(JSContext *cx, uintN maxar
     JSObject *obj;
     Value *argv;
     uintN i;
     jsdouble lorutime; /* local or UTC version of *date */
     jsdouble args[3], *argp, *stop;
     jsdouble year, month, day;
     jsdouble result;
 
-    obj = ComputeThisObjectFromVp(cx, vp);
+    obj = ComputeThisFromVp(cx, vp);
     if (!GetUTCTime(cx, obj, vp, &result))
         return false;
 
     /* see complaint about ECMA in date_MakeTime */
     if (argc == 0) {
         SetDateToNaN(cx, obj, vp);
         return true;
     }
@@ -1785,17 +1785,17 @@ static JSBool
 date_setUTCFullYear(JSContext *cx, uintN argc, Value *vp)
 {
     return date_makeDate(cx, 3, JS_FALSE, argc, vp);
 }
 
 static JSBool
 date_setYear(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
 
     jsdouble result;
     if (!GetUTCTime(cx, obj, vp, &result))
         return false;
 
     if (argc == 0) {
         /* Call this only after GetUTCTime has verified that obj is Date. */
         SetDateToNaN(cx, obj, vp);
@@ -1864,17 +1864,17 @@ print_iso_string(char* buf, size_t size,
 static JSBool
 date_utc_format(JSContext *cx, Value *vp,
                 void (*printFunc)(char*, size_t, jsdouble))
 {
     char buf[100];
     JSString *str;
     jsdouble utctime;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &utctime))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &utctime))
         return JS_FALSE;
 
     if (!JSDOUBLE_IS_FINITE(utctime)) {
         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
     } else {
         (*printFunc)(buf, sizeof buf, utctime);
     }
     str = JS_NewStringCopyZ(cx, buf);
@@ -2040,17 +2040,17 @@ static JSBool
 date_toLocaleHelper(JSContext *cx, const char *format, Value *vp)
 {
     JSObject *obj;
     char buf[100];
     JSString *str;
     PRMJTime split;
     jsdouble utctime;
 
-    obj = ComputeThisObjectFromVp(cx, vp);
+    obj = ComputeThisFromVp(cx, vp);
     if (!GetUTCTime(cx, obj, vp, &utctime))
         return JS_FALSE;
 
     if (!JSDOUBLE_IS_FINITE(utctime)) {
         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
     } else {
         intN result_len;
         jsdouble local = LocalTime(utctime, cx);
@@ -2146,43 +2146,43 @@ date_toLocaleFormat(JSContext *cx, uintN
     return date_toLocaleHelper(cx, fmtbytes, vp);
 }
 
 static JSBool
 date_toTimeString(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble utctime;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &utctime))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &utctime))
         return JS_FALSE;
     return date_format(cx, utctime, FORMATSPEC_TIME, vp);
 }
 
 static JSBool
 date_toDateString(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble utctime;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &utctime))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &utctime))
         return JS_FALSE;
     return date_format(cx, utctime, FORMATSPEC_DATE, vp);
 }
 
 #if JS_HAS_TOSOURCE
 #include <string.h>
 #include "jsdtoa.h"
 
 static JSBool
 date_toSource(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble utctime;
     char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
     JSString *str;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &utctime))
+    if (!GetUTCTime(cx, ComputeThisFromVp(cx, vp), vp, &utctime))
         return JS_FALSE;
 
     numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, sizeof buf, DTOSTR_STANDARD, 0, utctime);
     if (!numStr) {
         JS_ReportOutOfMemory(cx);
         return JS_FALSE;
     }
 
@@ -2202,17 +2202,17 @@ date_toSource(JSContext *cx, uintN argc,
 }
 #endif
 
 static JSBool
 date_toString(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble utctime;
 
-    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &utctime))
+    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;
@@ -2363,18 +2363,18 @@ js_InitDateClass(JSContext *cx, JSObject
      *   Date.prototype.toGMTString is the same Function
      *   object that is the initial value of
      *   Date.prototype.toUTCString.
      */
     AutoValueRooter toUTCStringFun(cx);
     jsid toUTCStringId = ATOM_TO_JSID(cx->runtime->atomState.toUTCStringAtom);
     jsid toGMTStringId = ATOM_TO_JSID(cx->runtime->atomState.toGMTStringAtom);
     if (!js_GetProperty(cx, proto, toUTCStringId, toUTCStringFun.addr()) ||
-        !js_DefineProperty(cx, proto, toGMTStringId, toUTCStringFun.value(),
-                           JS_PropertyStub, JS_PropertyStub, 0)) {
+        !js_DefineProperty(cx, proto, toGMTStringId, toUTCStringFun.addr(),
+                           PropertyStub, PropertyStub, 0)) {
         return NULL;
     }
 
     return proto;
 }
 
 JS_FRIEND_API(JSObject *)
 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -681,18 +681,18 @@ js_watch_set(JSContext *cx, JSObject *ob
                 MakeValueRangeGCSafe(vp, vplen);
                 vp[0].setObject(*closure);
                 JSStackFrame *fp = frame.getFrame();
                 PodZero(fp);
                 MakeValueRangeGCSafe(fp->slots(), nfixed);
                 fp->script = script;
                 fp->fun = fun;
                 fp->argv = vp + 2;
-                fp->setScopeChainObj(closure->getParent());
-                fp->setArgsObj(NULL);
+                fp->scopeChain = closure->getParent();
+                fp->argsobj = NULL;
 
                 /* Initialize regs. */
                 regs.pc = script ? script->code : NULL;
                 regs.sp = fp->slots() + nfixed;
 
                 /* Officially push |fp|. |frame|'s destructor pops. */
                 cx->stack().pushExecuteFrame(cx, frame, regs, NULL);
 
@@ -792,17 +792,17 @@ JS_SetWatchPoint(JSContext *cx, JSObject
     JSScopeProperty *sprop;
     JSRuntime *rt;
     JSBool ok;
     JSWatchPoint *wp;
     PropertyOp watcher;
 
     origobj = obj;
     obj = obj->wrappedObject(cx);
-    Innerize(cx, &obj);
+    OBJ_TO_INNER_OBJECT(cx, obj);
     if (!obj)
         return JS_FALSE;
 
     AutoValueRooter idroot(cx);
     if (JSID_IS_INT(id)) {
         propid = id;
     } else {
         if (!js_ValueToStringId(cx, IdToValue(id), &propid))
@@ -1200,17 +1200,17 @@ JS_IsNativeFrame(JSContext *cx, JSStackF
 {
     return !fp->script;
 }
 
 /* this is deprecated, use JS_GetFrameScopeChain instead */
 JS_PUBLIC_API(JSObject *)
 JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
 {
-    return fp->scopeChainObj();
+    return fp->scopeChain;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
 {
     JS_ASSERT(cx->stack().contains(fp));
 
     /* Force creation of argument and call objects if not yet created */
@@ -1580,19 +1580,19 @@ SetupFakeFrame(JSContext *cx, ExecuteFra
     JSFunction *fun = GET_FUNCTION_PRIVATE(cx, scopeobj);
     JS_ASSERT(fun->minArgs() == 0 && !fun->isInterpreted() && fun->u.n.extra == 0);
 
     const uintN vplen = 2;
     const uintN nfixed = 0;
     if (!cx->stack().getExecuteFrame(cx, js_GetTopStackFrame(cx), vplen, nfixed, frame))
         return false;
 
-    jsval *vp = frame.getvp();
+    Value *vp = frame.getvp();
     PodZero(vp, vplen);
-    vp[0] = OBJECT_TO_JSVAL(scopeobj);
+    vp[0].setObject(*scopeobj);
 
     JSStackFrame *fp = frame.getFrame();
     PodZero(fp);
     fp->fun = fun;
     fp->argv = vp + 2;
     fp->scopeChain = scopeobj->getGlobal();
 
     regs.pc = NULL;
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -1328,17 +1328,17 @@ js_PushStatement(JSTreeContext *tc, JSSt
 }
 
 void
 js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSObject *blockObj,
                   ptrdiff_t top)
 {
     js_PushStatement(tc, stmt, STMT_BLOCK, top);
     stmt->flags |= SIF_SCOPE;
-    blockObj->setParent(ObjectOrNullTag(tc->blockChain));
+    blockObj->setParent(tc->blockChain);
     stmt->downScope = tc->topScopeStmt;
     tc->topScopeStmt = stmt;
     tc->blockChain = blockObj;
     stmt->blockObj = blockObj;
 }
 
 /*
  * Emit a backpatch op with offset pointing to the previous jump of this type,
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -701,17 +701,17 @@ Exception(JSContext *cx, JSObject *obj, 
          * class prototype ourselves.
          */
         if (!argv[-2].asObject().getProperty(cx,
                                              ATOM_TO_JSID(cx->runtime->atomState
                                                           .classPrototypeAtom),
                                              rval)) {
             return JS_FALSE;
         }
-        JSObject *errProto = JSVAL_TO_OBJECT(*rval);
+        JSObject *errProto = &rval->asObject();
         obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent());
         if (!obj)
             return JS_FALSE;
         rval->setObject(*obj);
     }
 
     /*
      * If it's a new object of class Exception, then null out the private
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -120,25 +120,31 @@ js_GetArgsProperty(JSContext *cx, JSStac
     if (fp->flags & JSFRAME_OVERRIDE_ARGS) {
         JS_ASSERT(fp->callobj);
 
         jsid argumentsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
         Value v;
         if (!fp->callobj->getProperty(cx, argumentsid, &v))
             return false;
 
-        if (v.isPrimitive() && !js_ValueToNonNullObject(cx, v, &v))
-            return false;
-        return v.asObject().getProperty(cx, id, vp);
+        JSObject *obj;
+        if (v.isPrimitive()) {
+            obj = js_ValueToNonNullObject(cx, v);
+            if (!obj)
+                return false;
+        } else {
+            obj = &v.asObject();
+        }
+        return obj->getProperty(cx, id, vp);
     }
 
     vp->setUndefined();
     if (JSID_IS_INT(id)) {
         uint32 arg = uint32(JSID_TO_INT(id));
-        JSObject *argsobj = fp->argsObj();
+        JSObject *argsobj = fp->argsobj;
         if (arg < fp->argc) {
             if (argsobj) {
                 if (argsobj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
                     return argsobj->getProperty(cx, id, vp);
             }
             *vp = fp->argv[arg];
         } else {
             /*
@@ -152,17 +158,17 @@ js_GetArgsProperty(JSContext *cx, JSStac
              * the call to f should return undefined, not 42.  If fp->argsobj
              * is null at this point, as it would be in the example, return
              * undefined in *vp.
              */
             if (argsobj)
                 return argsobj->getProperty(cx, id, vp);
         }
     } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
-        JSObject *argsobj = fp->argsObj();
+        JSObject *argsobj = fp->argsobj;
         if (argsobj && argsobj->isArgsLengthOverridden())
             return argsobj->getProperty(cx, id, vp);
         vp->setInt32(fp->argc);
     }
     return true;
 }
 
 static JSObject *
@@ -172,17 +178,17 @@ NewArguments(JSContext *cx, JSObject *pa
     if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
         return NULL;
 
     JSObject *argsobj = js_NewGCObject(cx);
     if (!argsobj)
         return NULL;
 
     /* Init immediately to avoid GC seeing a half-init'ed object. */
-    argsobj->init(&js_ArgumentsClass, ObjectTag(*proto), ObjectTag(*parent), PrivateTag(NULL));
+    argsobj->init(&js_ArgumentsClass, proto, parent, PrivateTag(NULL));
     argsobj->setArgsCallee(ObjectOrNullTag(callee));
     argsobj->setArgsLength(argc);
 
     argsobj->map = cx->runtime->emptyArgumentsScope->hold();
 
     /* This must come after argsobj->map has been set. */
     if (!js_EnsureReservedSlots(cx, argsobj, argc))
         return NULL;
@@ -210,40 +216,40 @@ js_GetArgsObject(JSContext *cx, JSStackF
     JS_ASSERT_IF(fp->fun->flags & JSFUN_HEAVYWEIGHT,
                  fp->varobj(cx->containingCallStack(fp)));
 
     /* Skip eval and debugger frames. */
     while (fp->flags & JSFRAME_SPECIAL)
         fp = fp->down;
 
     /* Create an arguments object for fp only if it lacks one. */
-    JSObject *argsobj = fp->argsObj();
+    JSObject *argsobj = fp->argsobj;
     if (argsobj)
         return argsobj;
 
     /* Compute the arguments object's parent slot from fp's scope chain. */
     JSObject *global = fp->scopeChain->getGlobal();
     argsobj = NewArguments(cx, global, fp->argc, &fp->argv[-2].asObject());
     if (!argsobj)
         return argsobj;
 
     /* Link the new object to fp so it can get actual argument values. */
     argsobj->setPrivate(fp);
-    fp->setArgsObj(argsobj);
+    fp->argsobj = argsobj;
     return argsobj;
 }
 
 void
 js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
 {
-    JSObject *argsobj = fp->argsObj();
+    JSObject *argsobj = fp->argsobj;
     JS_ASSERT(argsobj->getPrivate() == fp);
     PutArguments(cx, argsobj, fp->argv);
     argsobj->setPrivate(NULL);
-    fp->setArgsObj(NULL);
+    fp->argsobj = NULL;
 }
 
 /*
  * Traced versions of js_GetArgsObject and js_PutArgsObject.
  */
 
 #ifdef JS_TRACER
 JSObject * JS_FASTCALL
@@ -749,34 +755,34 @@ CalleeGetter(JSContext *cx, JSObject *ob
 static JSObject *
 NewCallObject(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
 {
     JSObject *callobj = js_NewGCObject(cx);
     if (!callobj)
         return NULL;
 
     /* Init immediately to avoid GC seeing a half-init'ed object. */
-    callobj->init(&js_CallClass, NullTag(), ObjectTag(*scopeChain), PrivateTag(NULL));
+    callobj->init(&js_CallClass, NULL, scopeChain, PrivateTag(NULL));
     callobj->map = cx->runtime->emptyCallScope->hold();
 
     /* This must come after callobj->map has been set. */
     if (!js_EnsureReservedSlots(cx, callobj, fun->countArgsAndVars()))
         return NULL;
     return callobj;
 }
 
 static inline JSObject *
 NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
 {
     JSObject *envobj = js_NewGCObject(cx);
     if (!envobj)
         return NULL;
 
     /* Init immediately to avoid GC seeing a half-init'ed object. */
-    envobj->init(&js_DeclEnvClass, NULL, fp->scopeChain, reinterpret_cast<jsval>(fp));
+    envobj->init(&js_DeclEnvClass, NULL, fp->scopeChain, PrivateTag(fp));
     envobj->map = cx->runtime->emptyDeclEnvScope->hold();
     return envobj;
 }
 
 JSObject *
 js_GetCallObject(JSContext *cx, JSStackFrame *fp)
 {
     JSObject *callobj;
@@ -784,21 +790,21 @@ js_GetCallObject(JSContext *cx, JSStackF
     /* Create a call object for fp only if it lacks one. */
     JS_ASSERT(fp->fun);
     callobj = fp->callobj;
     if (callobj)
         return callobj;
 
 #ifdef DEBUG
     /* A call object should be a frame's outermost scope chain element.  */
-    Class *classp = fp->scopeChainObj()->getClass();
+    Class *classp = fp->scopeChain->getClass();
     if (classp == &js_WithClass || classp == &js_BlockClass)
-        JS_ASSERT(fp->scopeChainObj()->getPrivate() != js_FloatingFrameIfGenerator(cx, fp));
+        JS_ASSERT(fp->scopeChain->getPrivate() != js_FloatingFrameIfGenerator(cx, fp));
     else if (classp == &js_CallClass)
-        JS_ASSERT(fp->scopeChainObj()->getPrivate() != fp);
+        JS_ASSERT(fp->scopeChain->getPrivate() != fp);
 #endif
 
     /*
      * Create the call object, using the frame's enclosing scope as its
      * parent, and link the call to its stack frame. For a named function
      * expression Call's parent points to an environment object holding
      * function's name.
      */
@@ -806,40 +812,40 @@ js_GetCallObject(JSContext *cx, JSStackF
     if (lambdaName) {
         JSObject *envobj = NewDeclEnvObject(cx, fp);
         if (!envobj)
             return NULL;
 
         /* Root envobj before js_DefineNativeProperty (-> JSClass.addProperty). */
         fp->scopeChain = envobj;
         JS_ASSERT(fp->argv);
-        if (!js_DefineNativeProperty(cx, fp->scopeChainObj(), ATOM_TO_JSID(lambdaName),
+        if (!js_DefineNativeProperty(cx, fp->scopeChain, ATOM_TO_JSID(lambdaName),
                                      fp->calleeValue(),
                                      CalleeGetter, NULL,
                                      JSPROP_PERMANENT | JSPROP_READONLY,
                                      0, 0, NULL)) {
             return NULL;
         }
     }
 
-    callobj = NewCallObject(cx, fp->fun, fp->scopeChainObj());
+    callobj = NewCallObject(cx, fp->fun, fp->scopeChain);
     if (!callobj)
         return NULL;
 
     callobj->setPrivate(fp);
     JS_ASSERT(fp->argv);
     JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->callee()));
     callobj->setSlot(JSSLOT_CALLEE, fp->calleeValue());
     fp->callobj = callobj;
 
     /*
      * Push callobj on the top of the scope chain, and make it the
      * variables object.
      */
-    fp->setScopeChainObj(callobj);
+    fp->scopeChain = callobj;
     return callobj;
 }
 
 JSObject * JS_FASTCALL
 js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain)
 {
     JS_ASSERT(!js_IsNamedLambda(fun));
     JSObject *callobj = NewCallObject(cx, fun, scopeChain);
@@ -874,19 +880,19 @@ CopyValuesToCallObject(JSObject *callobj
 
 void
 js_PutCallObject(JSContext *cx, JSStackFrame *fp)
 {
     JSObject *callobj = fp->callobj;
     JS_ASSERT(callobj);
 
     /* Get the arguments object to snapshot fp's actual argument values. */
-    if (fp->argsObj()) {
+    if (fp->argsobj) {
         if (!(fp->flags & JSFRAME_OVERRIDE_ARGS))
-            callobj->setSlot(JSSLOT_CALL_ARGUMENTS, fp->argsval);
+            callobj->setSlot(JSSLOT_CALL_ARGUMENTS, ObjectOrNullTag(fp->argsobj));
         js_PutArgsObject(cx, fp);
     }
 
     JSFunction *fun = fp->fun;
     JS_ASSERT(fun == js_GetCallObjectFunction(callobj));
     uintN n = fun->countArgsAndVars();
 
     /*
@@ -1370,18 +1376,18 @@ fun_getProperty(JSContext *cx, JSObject 
             JS_ASSERT(fp->down->argv);
             *vp = fp->down->calleeValue();
         } else {
             vp->setNull();
         }
 
         /* Censor the caller if it is from another compartment. */
         if (vp->isObject()) {
-            if (JSVAL_TO_OBJECT(*vp)->getCompartment(cx) != cx->compartment)
-                *vp = JSVAL_NULL;
+            if (vp->asObject().getCompartment(cx) != cx->compartment)
+                vp->setNull();
         }
         break;
 
       default:
         /* XXX fun[0] and fun.arguments[0] are equivalent. */
         if (fp && fp->fun && (uintN)slot < fp->fun->nargs)
             *vp = fp->argv[slot];
         break;
@@ -1828,59 +1834,59 @@ fun_toStringHelper(JSContext *cx, JSObje
     return JS_DecompileFunction(cx, fun, indent);
 }
 
 }  /* namespace js */
 
 static JSBool
 fun_toString(JSContext *cx, uintN argc, Value *vp)
 {
-    JS_ASSERT(VALUE_IS_FUNCTION(cx, vp[0]));
+    JS_ASSERT(IsFunctionObject(vp[0]));
     uint32_t indent = 0;
 
     if (argc != 0 && !ValueToECMAUint32(cx, vp[2], &indent))
         return false;
 
-    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
         return false;
 
     JSString *str = fun_toStringHelper(cx, obj, indent);
     if (!str)
         return false;
 
-    *vp = STRING_TO_JSVAL(str);
+    vp->setString(str);
     return true;
 }
 
 #if JS_HAS_TOSOURCE
 static JSBool
 fun_toSource(JSContext *cx, uintN argc, Value *vp)
 {
-    JS_ASSERT(VALUE_IS_FUNCTION(cx, vp[0]));
-
-    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    JS_ASSERT(IsFunctionObject(vp[0]));
+
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
         return false;
 
     JSString *str = fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT);
     if (!str)
         return false;
 
-    *vp = STRING_TO_JSVAL(str);
+    vp->setString(str);
     return true;
 }
 #endif
 
 JSBool
 js_fun_call(JSContext *cx, uintN argc, Value *vp)
 {
     LeaveTrace(cx);
 
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
         return JS_FALSE;
     Value fval = vp[1];
 
     if (!js_IsCallable(fval)) {
         JSString *str = js_ValueToString(cx, fval);
         if (str) {
             const char *bytes = js_GetStringBytes(cx, str);
@@ -1891,38 +1897,37 @@ js_fun_call(JSContext *cx, uintN argc, V
                                      js_Function_str, js_call_str,
                                      bytes);
             }
         }
         return JS_FALSE;
     }
 
     Value *argv = vp + 2;
-    Value objv;
     if (argc == 0) {
         /* Call fun with its global object as the 'this' param if no args. */
-        objv.setNull();
+        obj = NULL;
     } else {
         /* Otherwise convert the first arg to 'this' and skip over it. */
         if (argv[0].isObject())
-            objv = argv[0];
-        else if (!js_ValueToObjectOrNull(cx, argv[0], &objv))
+            obj = &argv[0].asObject();
+        else if (!js_ValueToObjectOrNull(cx, argv[0], &obj))
             return JS_FALSE;
         argc--;
         argv++;
     }
 
     /* Allocate stack space for fval, obj, and the args. */
     InvokeArgsGuard args;
     if (!cx->stack().pushInvokeArgs(cx, argc, args))
         return JS_FALSE;
 
     /* Push fval, obj, and the args. */
     args.getvp()[0] = fval;
-    args.getvp()[1] = objv;
+    args.getvp()[1] = ObjectOrNullTag(obj);
     memcpy(args.getvp() + 2, argv, argc * sizeof *argv);
 
     bool ok = Invoke(cx, args, 0);
     *vp = *args.getvp();
     return ok;
 }
 
 JSBool
@@ -1930,17 +1935,17 @@ js_fun_apply(JSContext *cx, uintN argc, 
 {
     if (argc == 0) {
         /* Will get globalObject as 'this' and no other arguments. */
         return js_fun_call(cx, argc, vp);
     }
 
     LeaveTrace(cx);
 
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
         return JS_FALSE;
 
     Value fval = vp[1];
     if (!js_IsCallable(fval)) {
         JSString *str = js_ValueToString(cx, fval);
         if (str) {
             const char *bytes = js_GetStringBytes(cx, str);
@@ -1975,33 +1980,32 @@ js_fun_apply(JSContext *cx, uintN argc, 
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_BAD_APPLY_ARGS, js_apply_str);
                 return JS_FALSE;
             }
         }
     }
 
     /* Convert the first arg to 'this' and skip over it. */
-    Value objv;
     if (vp[2].isObject())
-        objv = vp[2];
-    else if (!js_ValueToObjectOrNull(cx, vp[2], &objv))
+        obj = &vp[2].asObject();
+    else if (!js_ValueToObjectOrNull(cx, vp[2], &obj))
         return JS_FALSE;
 
     /* Allocate stack space for fval, obj, and the args. */
     argc = (uintN)JS_MIN(length, JS_ARGS_LENGTH_MAX);
 
     InvokeArgsGuard args;
     if (!cx->stack().pushInvokeArgs(cx, argc, args))
         return JS_FALSE;
 
     /* Push fval, obj, and aobj's elements as args. */
     Value *sp = args.getvp();
     *sp++ = fval;
-    *sp++ = objv;
+    *sp++ = ObjectOrNullTag(obj);
     if (aobj && aobj->isArguments() && !aobj->isArgsLengthOverridden()) {
         /*
          * Two cases, two loops: note how in the case of an active stack frame
          * backing aobj, even though we copy from fp->argv, we still must check
          * aobj->getArgsElement(i) for a hole, to handle a delete on the
          * corresponding arguments element. See args_delProperty.
          */
         JSStackFrame *fp = (JSStackFrame *) aobj->getPrivate();
@@ -2343,17 +2347,17 @@ js_InitFunctionClass(JSContext *cx, JSOb
 JSFunction *
 js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs,
                uintN flags, JSObject *parent, JSAtom *atom)
 {
     JSFunction *fun;
 
     if (funobj) {
         JS_ASSERT(funobj->isFunction());
-        funobj->setParent(ObjectOrNullTag(parent));
+        funobj->setParent(parent);
     } else {
         funobj = NewObject(cx, &js_FunctionClass, NULL, parent);
         if (!funobj)
             return NULL;
     }
     JS_ASSERT(!funobj->getPrivate());
     fun = (JSFunction *) funobj;
 
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -238,18 +238,19 @@ extern js::Class js_ArgumentsClass;
 
 inline bool
 JSObject::isArguments() const
 {
     return getClass() == &js_ArgumentsClass;
 }
 
 extern JS_PUBLIC_DATA(js::Class) js_CallClass;
+extern JS_PUBLIC_DATA(js::Class) js_FunctionClass;
 extern js::Class js_DeclEnvClass;
-extern JS_PUBLIC_DATA(js::Class) js_FunctionClass;
+extern const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS;
 
 inline bool
 JSObject::isFunction() const
 {
     return getClass() == &js_FunctionClass;
 }
 
 inline bool
@@ -257,19 +258,19 @@ JSObject::isCallable()
 {
     if (isNative())
         return isFunction() || getClass()->call;
 
     return !!map->ops->call;
 }
 
 static inline bool
-js_IsCallable(jsval v)
+js_IsCallable(const js::Value &v)
 {
-    return !JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isCallable();
+    return v.isObject() && v.asObject().isCallable();
 }
 
 /*
  * NB: jsapi.h and jsobj.h must be included before any call to this macro.
  */
 #define VALUE_IS_FUNCTION(cx, v)                                              \
     (!JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isFunction())
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -791,18 +791,16 @@ GetFinalizableArenaTraceKind(JSGCArenaIn
 {
     JS_ASSERT(ainfo->list);
     return GetFinalizableTraceKind(ainfo->list->thingKind);
 }
 
 static inline size_t
 GetArenaTraceKind(JSGCArenaInfo *ainfo)
 {
-    if (!ainfo->list)
-        return JSTRACE_DOUBLE;
     return GetFinalizableArenaTraceKind(ainfo);
 }
 
 static inline size_t
 GetFinalizableThingTraceKind(void *thing)
 {
     JSGCArenaInfo *ainfo = JSGCArenaInfo::fromGCThing(thing);
     return GetFinalizableArenaTraceKind(ainfo);
@@ -945,25 +943,23 @@ class ConservativeGCStackMarker {
 
     ~ConservativeGCStackMarker() {
 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
         dumpConservativeRoots();
 #endif
 #ifdef JS_GCMETER
         JSConservativeGCStats *total = &trc->context->runtime->gcStats.conservative;
         total->words        += stats.words;
-        total->oddaddress   += stats.oddaddress;
-        total->special      += stats.special;
+        total->lowbitset    += stats.lowbitset;
         total->notarena     += stats.notarena;
         total->notchunk     += stats.notchunk;
         total->freearena    += stats.freearena;
         total->wrongtag     += stats.wrongtag;
         total->notlive      += stats.notlive;
         total->gcthings     += stats.gcthings;
-        total->raw          += stats.raw;
         total->unmarked     += stats.unmarked;
 #endif
     }
 
     void markRoots();
 
   private:
     void markRange(jsuword *begin, jsuword *end);
@@ -1023,25 +1019,23 @@ ConservativeGCStackMarker::ConservativeG
 #if defined(JS_DUMP_CONSERVATIVE_GC_ROOTS) || defined(JS_GCMETER)
 /* static */
 void
 ConservativeGCStackMarker::dumpStats(FILE *fp, JSConservativeGCStats *stats)
 {
 #define ULSTAT(x)       ((unsigned long)(stats->x))
     fprintf(fp, "CONSERVATIVE STACK SCANNING:\n");
     fprintf(fp, "      number of stack words: %lu\n", ULSTAT(words));
-    fprintf(fp, "      excluded, low bit set: %lu\n", ULSTAT(oddaddress));
-    fprintf(fp, "          excluded, special: %lu\n", ULSTAT(special));
+    fprintf(fp, "      excluded, low bit set: %lu\n", ULSTAT(lowbitset));
     fprintf(fp, "        not withing a chunk: %lu\n", ULSTAT(notchunk));
     fprintf(fp, "     not within arena range: %lu\n", ULSTAT(notarena));
     fprintf(fp, "       points to free arena: %lu\n", ULSTAT(freearena));
     fprintf(fp, "        excluded, wrong tag: %lu\n", ULSTAT(wrongtag));
     fprintf(fp, "         excluded, not live: %lu\n", ULSTAT(notlive));
     fprintf(fp, "              things marked: %lu\n", ULSTAT(gcthings));
-    fprintf(fp, "        raw pointers marked: %lu\n", ULSTAT(raw));
     fprintf(fp, "         conservative roots: %lu\n", ULSTAT(unmarked));
 #undef ULSTAT
 }
 #endif
 
 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
 void
 ConservativeGCStackMarker::dumpConservativeRoots()
@@ -1079,21 +1073,16 @@ ConservativeGCStackMarker::dumpConservat
           }
           case JSTRACE_STRING: {
             JSString *str = (JSString *) i->thing;
             char buf[50];
             js_PutEscapedString(buf, sizeof buf, str, '"');
             fprintf(fp, "string %s", buf);
             break;
           }
-          case JSTRACE_DOUBLE: {
-            jsdouble *dp = (jsdouble *) i->thing;
-            fprintf(fp, "double %e", *dp);
-            break;
-          }
 # if JS_HAS_XML_SUPPORT
           case JSTRACE_XML: {
             JSXML *xml = (JSXML *) i->thing;
             fprintf(fp, "xml %u", xml->xml_class);
             break;
           }
 # endif
         }
@@ -1115,119 +1104,112 @@ ConservativeGCStackMarker::markWord(jsuw
      * the memory we make as memcheck-defined the argument, a copy of the
      * original word. See bug 572678.
      */
 #ifdef JS_VALGRIND
     VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w));
 #endif
 
 #define RETURN(x) do { CONSERVATIVE_METER(stats.x++); return; } while (0)
+
     /*
      * We assume that the compiler never uses sub-word alignment to store
-     * pointers and does not tag pointers on its own. Thus we exclude words
-     * with JSVAL_INT (any odd words) or JSVAL_SPECIAL tags as they never
-     * point to GC things. We also exclude words with a double tag that point
-     * into a non-double. But, for example, on 32-bit platforms we cannot
-     * exclude a pointer into an object arena tagged with JSVAL_STRING. The
-     * latter is 4 and a compiler can store a pointer not to the object but
-     * rather a pointer to its second field.
+     * pointers and does not tag pointers on its own. Additionally, neither
+     * value representation scheme touches the low bits of a GC-thing payload.
+     * Also, none of JSString, JSObject, and JSXML have sub-word-sized fields.
+     * Thus, we can immediately rule out a word if it has one of the low
+     * log2(sizeof(jsuword)) bits set.
      */
-    JS_STATIC_ASSERT(JSVAL_INT == 1);
-    JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);
-    JS_STATIC_ASSERT(JSVAL_STRING == 4);
-    JS_STATIC_ASSERT(JSVAL_SPECIAL == 6);
-
-    if (w & 1)
-        RETURN(oddaddress);
-
-    /* Strip off the tag bits. */
-    jsuword tag = w & JSVAL_TAGMASK;
-
-    if (tag == JSVAL_SPECIAL)
-        RETURN(special);
-
-    jsuword chunk = w & ~GC_CHUNK_MASK;
+#if JS_BITS_PER_WORD == 32
+    if (w & 0x3)
+        RETURN(lowbitset);
+    jsuword payload = w;
+#elif JS_BITS_PER_WORD == 64
+    if (w & 0x7)
+        RETURN(lowbitset);
+    jsuword payload = w & JSVAL_PAYLOAD_MASK;
+#endif
+
+    jsuword chunk = payload & ~GC_CHUNK_MASK;
     JSGCChunkInfo *ci;
     if (JS_LIKELY(chunkSet.initialized())) {
         if (!chunkSet.has(chunk))
             RETURN(notchunk);
         ci = JSGCChunkInfo::fromChunk(chunk);
     } else {
         ci = JSGCChunkInfo::fromChunk(chunk);
         for (JSGCChunkInfo **i = trc->context->runtime->gcChunks.begin(); ; ++i) {
             if (i == trc->context->runtime->gcChunks.end())
                 RETURN(notchunk);
             if (*i == ci)
                 break;
         }
     }
 
-    if ((w & GC_CHUNK_MASK) >= GC_MARK_BITMAP_ARRAY_OFFSET)
+    if ((payload & GC_CHUNK_MASK) >= GC_MARK_BITMAP_ARRAY_OFFSET)
         RETURN(notarena);
 
-    size_t arenaIndex = (w & GC_CHUNK_MASK) >> GC_ARENA_SHIFT;
+    size_t arenaIndex = (payload & GC_CHUNK_MASK) >> GC_ARENA_SHIFT;
     if (JS_TEST_BIT(ci->getFreeArenaBitmap(), arenaIndex))
         RETURN(freearena);
 
     JSGCArena *a = JSGCArena::fromChunkAndIndex(chunk, arenaIndex);
     JSGCArenaInfo *ainfo = a->getInfo();
 
     JSGCThing *thing;
     uint32 traceKind;
-    if (!ainfo->list) { /* doubles */
-        if (tag && tag != JSVAL_DOUBLE)
-            RETURN(wrongtag);
-        JS_STATIC_ASSERT(JSVAL_TAGMASK == 7 && (sizeof(double) - 1) == 7);
-        thing = (JSGCThing *) (w & ~JSVAL_TAGMASK);
-        traceKind = JSTRACE_DOUBLE;
-    } else {
-        if (tag == JSVAL_DOUBLE)
-            RETURN(wrongtag);
-        traceKind = GetFinalizableArenaTraceKind(ainfo);
+    traceKind = GetFinalizableArenaTraceKind(ainfo);
+
 #if JS_BYTES_PER_WORD == 8
-        if (tag == JSVAL_STRING && traceKind != JSTRACE_STRING)
-            RETURN(wrongtag);
+    /*
+     * On 64-bit we have the tag information in the same word as the payload.
+     * Since all GC-things have the high bits clear, we are safe in using these
+     * bits as tag information for the purpose avoiding false roots.
+     */
+    JSValueTag tag = w >> JSVAL_TAG_SHIFT;
+    if ((tag == JSVAL_TAG_SHIFT && traceKind != JSTRACE_STRING) ||
+        (tag == JSVAL_TAG_OBJECT && traceKind != JSTRACE_OBJECT)) {
+        RETURN(wrongtag);
+    }
 #endif
 
-        jsuword start = a->toPageStart();
-        jsuword offset = w - start;
-        size_t thingSize = ainfo->list->thingSize;
-        offset -= offset % thingSize;
-
-        /*
-         * If GC_ARENA_SIZE % thingSize != 0 or when thingSize is not a power
-         * of two, thingSize-aligned pointer may point at the end of the last
-         * thing yet be inside the arena.
-         */
-        if (offset + thingSize > GC_ARENA_SIZE) {
-            JS_ASSERT(thingSize & (thingSize - 1));
-            RETURN(notarena);
-        }
-        thing = (JSGCThing *) (start + offset);
-
-        /* Make sure the thing is not on the freelist of the arena. */
-        JSGCThing *cursor = ainfo->freeList;
-        while (cursor) {
-            JS_ASSERT((((jsuword) cursor) & GC_ARENA_MASK) % thingSize == 0);
-            JS_ASSERT(!IsMarkedGCThing(cursor));
-
-            /* If the cursor moves past the thing, it's not in the freelist. */
-            if (thing < cursor)
-                break;
-
-            /* If we find it on the freelist, it's dead. */
-            if (thing == cursor)
-                RETURN(notlive);
-            JS_ASSERT_IF(cursor->link, cursor < cursor->link);
-            cursor = cursor->link;
-        }
+    jsuword start = a->toPageStart();
+    jsuword offset = payload - start;
+    size_t thingSize = ainfo->list->thingSize;
+    offset -= offset % thingSize;
+
+    /*
+     * If GC_ARENA_SIZE % thingSize != 0 or when thingSize is not a power
+     * of two, thingSize-aligned pointer may point at the end of the last
+     * thing yet be inside the arena.
+     */
+    if (offset + thingSize > GC_ARENA_SIZE) {
+        JS_ASSERT(thingSize & (thingSize - 1));
+        RETURN(notarena);
+    }
+    thing = (JSGCThing *) (start + offset);
+
+    /* Make sure the thing is not on the freelist of the arena. */
+    JSGCThing *cursor = ainfo->freeList;
+    while (cursor) {
+        JS_ASSERT((((jsuword) cursor) & GC_ARENA_MASK) % thingSize == 0);
+        JS_ASSERT(!IsMarkedGCThing(cursor));
+
+        /* If the cursor moves past the thing, it's not in the freelist. */
+        if (thing < cursor)
+            break;
+
+        /* If we find it on the freelist, it's dead. */
+        if (thing == cursor)
+            RETURN(notlive);
+        JS_ASSERT_IF(cursor->link, cursor < cursor->link);
+        cursor = cursor->link;
     }
 
     CONSERVATIVE_METER(stats.gcthings++);
-    CONSERVATIVE_METER_IF(!tag, stats.raw++);
 
     /*
      * We have now a valid pointer, that is either raw or tagged properly.
      * Since we do not rely on the conservative scanning yet and assume that
      * all the roots are precisely reported, any unmarked GC things here mean
      * those things leaked.
      */
     if (IS_GC_MARKING_TRACER(trc)) {
@@ -1237,18 +1219,17 @@ ConservativeGCStackMarker::markWord(jsuw
     }
 
 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
     if (IS_GC_MARKING_TRACER(trc) && dumpFileName) {
         ConservativeRoot root = {thing, traceKind};
         conservativeRoots.append(root);
     }
 #endif
-    JS_SET_TRACING_NAME(trc, "machine stack");
-    js_CallGCMarker(trc, thing, traceKind);
+    Mark(trc, thing, traceKind, "machine stack");
 
 #undef RETURN
 }
 
 void
 ConservativeGCStackMarker::markRange(jsuword *begin, jsuword *end)
 {
     JS_ASSERT(begin <= end);
@@ -2281,26 +2262,26 @@ gc_lock_traversal(const GCLocks::Entry &
 }
 
 void
 js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp)
 {
 
     if (fp->callobj)
         JS_CALL_OBJECT_TRACER(trc, fp->callobj, "call");
-    if (fp->argsObj())
-        JS_CALL_OBJECT_TRACER(trc, fp->argsObj(), "arguments");
+    if (fp->argsobj)
+        JS_CALL_OBJECT_TRACER(trc, fp->argsobj, "arguments");
     if (fp->script)
         js_TraceScript(trc, fp->script);
 
     /* Allow for primitive this parameter due to JSFUN_THISP_* flags. */
     MarkValue(trc, fp->thisv, "this");
     MarkValue(trc, fp->rval, "rval");
-    if (fp->scopeChainObj())
-        JS_CALL_OBJECT_TRACER(trc, fp->scopeChainObj(), "scope chain");
+    if (fp->scopeChain)
+        JS_CALL_OBJECT_TRACER(trc, fp->scopeChain, "scope chain");
 }
 
 void
 JSWeakRoots::mark(JSTracer *trc)
 {
 #ifdef DEBUG
     const char * const newbornNames[] = {
         "newborn_object",             /* FINALIZE_OBJECT */
@@ -2331,18 +2312,17 @@ JSWeakRoots::mark(JSTracer *trc)
     MarkGCThing(trc, lastInternalResult, "lastInternalResult");
 }
 
 inline void
 AutoGCRooter::trace(JSTracer *trc)
 {
     switch (tag) {
       case JSVAL:
-        JS_SET_TRACING_NAME(trc, "js::AutoValueRooter.val");
-        js_CallValueTracerIfGCThing(trc, static_cast<AutoValueRooter *>(this)->val);
+        MarkValue(trc, static_cast<AutoValueRooter *>(this)->val, "js::AutoValueRooter.val");
         return;
 
       case SPROP:
         static_cast<AutoScopePropertyRooter *>(this)->sprop->trace(trc);
         return;
 
       case WEAKROOTS:
         static_cast<AutoPreserveWeakRoots *>(this)->savedRoots.mark(trc);
@@ -2358,79 +2338,87 @@ AutoGCRooter::trace(JSTracer *trc)
         return;
 
       case ENUMERATOR:
         static_cast<AutoEnumStateRooter *>(this)->trace(trc);
         return;
 
       case IDARRAY: {
         JSIdArray *ida = static_cast<AutoIdArray *>(this)->idArray;
-        TraceValues(trc, ida->length, ida->vector, "js::AutoIdArray.idArray");
+        MarkIdRange(trc, ida->length, ida->vector, "js::AutoIdArray.idArray");
         return;
       }
 
       case DESCRIPTORS: {
-        PropertyDescriptorArray &descriptors =
-            static_cast<AutoDescriptorArray *>(this)->descriptors;
+        PropDescArray &descriptors =
+            static_cast<AutoPropDescArrayRooter *>(this)->descriptors;
         for (size_t i = 0, len = descriptors.length(); i < len; i++) {
-            PropertyDescriptor &desc = descriptors[i];
-
-            JS_CALL_VALUE_TRACER(trc, desc.pd, "PropertyDescriptor::pd");
-            JS_CALL_VALUE_TRACER(trc, desc.value, "PropertyDescriptor::value");
-            JS_CALL_VALUE_TRACER(trc, desc.get, "PropertyDescriptor::get");
-            JS_CALL_VALUE_TRACER(trc, desc.set, "PropertyDescriptor::set");
-            js_TraceId(trc, desc.id);
+            PropDesc &desc = descriptors[i];
+            MarkValue(trc, desc.pd, "PropDesc::pd");
+            MarkValue(trc, desc.value, "PropDesc::value");
+            MarkValue(trc, desc.get, "PropDesc::get");
+            MarkValue(trc, desc.set, "PropDesc::set");
+            MarkId(trc, desc.id, "PropDesc::id");
         }
         return;
       }
 
       case DESCRIPTOR : {
-        AutoDescriptor &desc = *static_cast<AutoDescriptor *>(this);
+        PropertyDescriptor &desc = *static_cast<AutoPropertyDescriptorRooter *>(this);
         if (desc.obj)
-            JS_CALL_OBJECT_TRACER(trc, desc.obj, "Descriptor::obj");
-        JS_CALL_VALUE_TRACER(trc, desc.value, "Descriptor::value");
+            MarkObject(trc, desc.obj, "Descriptor::obj");
+        MarkValue(trc, desc.value, "Descriptor::value");
         if (desc.attrs & JSPROP_GETTER)
-            JS_CALL_VALUE_TRACER(trc, jsval(desc.getter), "Descriptor::get");
+            MarkObject(trc, CastAsObject(desc.getter), "Descriptor::get");
         if (desc.attrs & JSPROP_SETTER)
-            JS_CALL_VALUE_TRACER(trc, jsval(desc.setter), "Descriptor::set");
+            MarkObject(trc, CastAsObject(desc.setter), "Descriptor::set");
         return;
       }
 
       case NAMESPACES: {
         JSXMLArray &array = static_cast<AutoNamespaceArray *>(this)->array;
-        TraceObjectVector(trc, reinterpret_cast<JSObject **>(array.vector), array.length);
+        MarkObjectRange(trc, array.length, reinterpret_cast<JSObject **>(array.vector),
+                        "JSXMLArray.vector");
         array.cursors->trace(trc);
         return;
       }
 
       case XML:
         js_TraceXML(trc, static_cast<AutoXMLRooter *>(this)->xml);
         return;
 
       case OBJECT:
-        if (JSObject *obj = static_cast<AutoObjectRooter *>(this)->obj) {
-            JS_SET_TRACING_NAME(trc, "js::AutoObjectRooter.obj");
-            js_CallGCMarker(trc, obj, JSTRACE_OBJECT);
-        }
+        if (JSObject *obj = static_cast<AutoObjectRooter *>(this)->obj)
+            MarkObject(trc, obj, "js::AutoObjectRooter.obj");
         return;
 
       case ID:
-        JS_SET_TRACING_NAME(trc, "js::AutoIdRooter.val");
-        js_CallValueTracerIfGCThing(trc, static_cast<AutoIdRooter *>(this)->idval);
+        MarkId(trc, static_cast<AutoIdRooter *>(this)->id_, "js::AutoIdRooter.val");
+        return;
+
+      case VALVECTOR: {
+        Vector<Value, 8> &vector = static_cast<js::AutoValueVector *>(this)->vector;
+        MarkValueRange(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector");
         return;
-
-      case VECTOR: {
-        js::Vector<jsval, 8> &vector = static_cast<js::AutoValueVector *>(this)->vector;
-        js::TraceValues(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector");
+      }
+
+      case STRING:
+        if (JSString *str = static_cast<js::AutoStringRooter *>(this)->str)
+            MarkString(trc, str, "js::AutoStringRooter.str");
+        return;
+
+      case IDVECTOR: {
+        Vector<jsid, 8> &vector = static_cast<js::AutoIdVector *>(this)->vector;
+        MarkIdRange(trc, vector.length(), vector.begin(), "js::AutoIdVector.vector");
         return;
       }
     }
 
     JS_ASSERT(tag >= 0);
-    TraceValues(trc, tag, static_cast<AutoArrayRooter *>(this)->array, "js::AutoArrayRooter.array");
+    MarkValueRange(trc, tag, static_cast<AutoArrayRooter *>(this)->array, "js::AutoArrayRooter.array");
 }
 
 void
 js_TraceContext(JSTracer *trc, JSContext *acx)
 {
     /* Stack frames and slots are traced by StackSpace::mark. */
 
     /* Mark other roots-by-definition in acx. */
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -409,25 +409,23 @@ const bool JS_WANT_GC_METER_PRINT = true
 # define JS_GCMETER 1
 const bool JS_WANT_GC_METER_PRINT = false;
 #endif
 
 #if defined JS_GCMETER || defined JS_DUMP_CONSERVATIVE_GC_ROOTS
 
 struct JSConservativeGCStats {
     uint32  words;      /* number of words on native stacks */
-    uint32  oddaddress; /* excluded because low bit was set */
-    uint32  special;    /* excluded because a special value */
+    uint32  lowbitset;  /* excluded because one of the low bits was set */
     uint32  notarena;   /* not within arena range in a chunk */
     uint32  notchunk;   /* not within a valid chunk */
     uint32  freearena;  /* not within non-free arena */
     uint32  wrongtag;   /* tagged pointer but wrong type */
     uint32  notlive;    /* gcthing is not allocated */
     uint32  gcthings;   /* number of live gcthings */
-    uint32  raw;        /* number of raw pointers marked */
     uint32  unmarked;   /* number of unmarked gc things discovered on the
                            stack */
 };
 
 #endif
 
 #ifdef JS_GCMETER
 
@@ -521,40 +519,49 @@ MarkString(JSTracer *trc, JSString *str,
     JS_SET_TRACING_NAME(trc, name);
     Mark(trc, str, JSTRACE_STRING);
 }
 
 static inline void
 MarkStringRange(JSTracer *trc, size_t len, JSString **vec, const char *name)
 {
     for (uint32 i = 0; i < len; i++) {
-        if (JSString *str = vec[i])
-            MarkString(trc, str, name);
+        if (JSString *str = vec[i]) {
+            JS_SET_TRACING_INDEX(trc, name, i);
+            Mark(trc, str, JSTRACE_STRING);
+        }
     }
 }
 
 static inline void
 MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name)
 {
-    MarkStringRange(trc, len, reinterpret_cast<JSString **>(vec), name);
+    for (uint32 i = 0; i < len; i++) {
+        if (JSAtom *atom = vec[i]) {
+            JS_SET_TRACING_INDEX(trc, name, i);
+            Mark(trc, ATOM_TO_STRING(atom), JSTRACE_STRING);
+        }
+    }
 }
 
 static inline void
 MarkObject(JSTracer *trc, JSObject *obj, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
     Mark(trc, obj, JSTRACE_OBJECT);
 }
 
 static inline void
 MarkObjectRange(JSTracer *trc, size_t len, JSObject **vec, const char *name)
 {
     for (uint32 i = 0; i < len; i++) {
-        if (JSObject *obj = vec[i])
-            MarkObject(trc, obj, name);
+        if (JSObject *obj = vec[i]) {
+            JS_SET_TRACING_INDEX(trc, name, i);
+            Mark(trc, obj, JSTRACE_OBJECT);
+        }
     }
 }
 
 /* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */
 static inline void
 MarkValueRaw(JSTracer *trc, const js::Value &v)
 {
     if (v.isMarkable())
@@ -579,29 +586,38 @@ MarkValueRange(JSTracer *trc, Value *beg
 
 static inline void
 MarkValueRange(JSTracer *trc, size_t len, Value *vec, const char *name)
 {
     MarkValueRange(trc, vec, vec + len, name);
 }
 
 static inline void
-MarkId(JSTracer *trc, jsid id, const char *name)
+MarkId(JSTracer *trc, jsid id)
 {
     if (JSID_IS_STRING(id))
-        Mark(trc, JSID_TO_STRING(id), JSTRACE_STRING, name);
+        Mark(trc, JSID_TO_STRING(id), JSTRACE_STRING);
     else if (JS_UNLIKELY(JSID_IS_OBJECT(id)))
-        Mark(trc, JSID_TO_OBJECT(id), JSTRACE_OBJECT, name);
+        Mark(trc, JSID_TO_OBJECT(id), JSTRACE_OBJECT);
+}
+
+static inline void
+MarkId(JSTracer *trc, jsid id, const char *name)
+{
+    JS_SET_TRACING_NAME(trc, name);
+    MarkId(trc, id);
 }
 
 static inline void
 MarkIdRange(JSTracer *trc, jsid *beg, jsid *end, const char *name)
 {
-    for (jsid *idp = beg; idp != end; ++idp)
-        MarkId(trc, *idp, name);
+    for (jsid *idp = beg; idp != end; ++idp) {
+        JS_SET_TRACING_INDEX(trc, name, (idp - beg));
+        MarkId(trc, *idp);
+    }
 }
 
 static inline void
 MarkIdRange(JSTracer *trc, size_t len, jsid *vec, const char *name)
 {
     MarkIdRange(trc, vec, vec + len, name);
 }
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -110,47 +110,47 @@ js_GetScopeChain(JSContext *cx, JSStackF
     if (!sharedBlock) {
         /*
          * Don't force a call object for a lightweight function call, but do
          * insist that there is a call object for a heavyweight function call.
          */
         JS_ASSERT(!fp->fun ||
                   !(fp->fun->flags & JSFUN_HEAVYWEIGHT) ||
                   fp->callobj);
-        JS_ASSERT(fp->scopeChainObj());
-        return fp->scopeChainObj();
+        JS_ASSERT(fp->scopeChain);
+        return fp->scopeChain;
     }
 
     /* We don't handle cloning blocks on trace.  */
     LeaveTrace(cx);
 
     /*
      * We have one or more lexical scopes to reflect into fp->scopeChain, so
      * make sure there's a call object at the current head of the scope chain,
      * if this frame is a call frame.
      *
      * Also, identify the innermost compiler-allocated block we needn't clone.
      */
     JSObject *limitBlock, *limitClone;
     if (fp->fun && !fp->callobj) {
-        JS_ASSERT_IF(fp->scopeChainObj()->getClass() == &js_BlockClass,
-                     fp->scopeChainObj()->getPrivate() != js_FloatingFrameIfGenerator(cx, fp));
+        JS_ASSERT_IF(fp->scopeChain->getClass() == &js_BlockClass,
+                     fp->scopeChain->getPrivate() != js_FloatingFrameIfGenerator(cx, fp));
         if (!js_GetCallObject(cx, fp))
             return NULL;
 
         /* We know we must clone everything on blockChain. */
         limitBlock = limitClone = NULL;
     } else {
         /*
          * scopeChain includes all blocks whose static scope we're within that
          * have already been cloned.  Find the innermost such block.  Its
          * prototype should appear on blockChain; we'll clone blockChain up
          * to, but not including, that prototype.
          */
-        limitClone = fp->scopeChainObj();
+        limitClone = fp->scopeChain;
         while (limitClone->getClass() == &js_WithClass)
             limitClone = limitClone->getParent();
         JS_ASSERT(limitClone);
 
         /*
          * It may seem like we don't know enough about limitClone to be able
          * to just grab its prototype as we do here, but it's actually okay.
          *
@@ -167,17 +167,17 @@ js_GetScopeChain(JSContext *cx, JSStackF
          * static nesting).  If limitClone isn't a block, its prototype won't
          * be a block either.  So we can just grab limitClone's prototype here
          * regardless of its type or which frame it belongs to.
          */
         limitBlock = limitClone->getProto();
 
         /* If the innermost block has already been cloned, we are done. */
         if (limitBlock == sharedBlock)
-            return fp->scopeChainObj();
+            return fp->scopeChain;
     }
 
     /*
      * Special-case cloning the innermost block; this doesn't have enough in
      * common with subsequent steps to include in the loop.
      *
      * js_CloneBlockObject leaves the clone's parent slot uninitialized. We
      * populate it below.
@@ -200,126 +200,141 @@ js_GetScopeChain(JSContext *cx, JSStackF
         if (sharedBlock == limitBlock || !sharedBlock)
             break;
 
         /* As in the call above, we don't know the real parent yet.  */
         JSObject *clone = js_CloneBlockObject(cx, sharedBlock, fp);
         if (!clone)
             return NULL;
 
-        newChild->setParent(ObjectTag(*clone));
+        newChild->setParent(clone);
         newChild = clone;
     }
     newChild->setParent(fp->scopeChain);
 
 
     /*
      * If we found a limit block belonging to this frame, then we should have
      * found it in blockChain.
      */
     JS_ASSERT_IF(limitBlock &&
                  limitBlock->getClass() == &js_BlockClass &&
                  limitClone->getPrivate() == js_FloatingFrameIfGenerator(cx, fp),
                  sharedBlock);
 
     /* Place our newly cloned blocks at the head of the scope chain.  */
-    fp->setScopeChainObj(innermostNewChild);
+    fp->scopeChain = innermostNewChild;
     return innermostNewChild;
 }
 
 JSBool
 js_GetPrimitiveThis(JSContext *cx, Value *vp, Class *clasp, const Value **vpp)
 {
     const Value *p = &vp[1];
     if (p->isObject()) {
-        JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+        JSObject *obj = ComputeThisFromVp(cx, vp);
         if (!InstanceOf(cx, obj, clasp, vp + 2))
             return JS_FALSE;
         *vpp = &obj->getPrimitiveThis();
     } else {
         *vpp = p;
     }
     return JS_TRUE;
 }
 
+/* Some objects (e.g., With) delegate 'this' to another object. */
+static inline JSObject *
+CallThisObjectHook(JSContext *cx, JSObject *obj, Value *argv)
+{
+    JSObject *thisp = obj->thisObject(cx);
+    if (!thisp)
+        return NULL;
+    argv[-1].setObject(*thisp);
+    return thisp;
+}
+
 /*
  * ECMA requires "the global object", but in embeddings such as the browser,
  * which have multiple top-level objects (windows, frames, etc. in the DOM),
  * we prefer fun's parent.  An example that causes this code to run:
  *
  *   // in window w1
  *   function f() { return this }
  *   function g() { return f }
  *
  *   // in window w2
  *   var h = w1.g()
  *   alert(h() == w1)
  *
  * The alert should display "true".
  */
-JS_STATIC_INTERPRET bool
+JS_STATIC_INTERPRET JSObject *
 ComputeGlobalThis(JSContext *cx, Value *argv)
 {
     /* Find the inner global. */
     JSObject *inner;
-    if (JSVAL_IS_PRIMITIVE(argv[-2]) || !JSVAL_TO_OBJECT(argv[-2])->getParent()) {
+    if (argv[-2].isPrimitive() || !argv[-2].asObject().getParent()) {
         inner = cx->globalObject;
         OBJ_TO_INNER_OBJECT(cx, inner);
         if (!inner)
             return NULL;
     } else {
-        inner = JSVAL_TO_OBJECT(argv[-2])->getGlobal();
+        inner = argv[-2].asObject().getGlobal();
     }
     JS_ASSERT(inner->getClass()->flags & JSCLASS_IS_GLOBAL);
 
     JSObject *scope = JS_GetGlobalForScopeChain(cx);
     if (scope == inner) {
         /*
          * The outer object has not moved along to a new inner object.
          * This means we qualify for the cache slot in the global.
          */
-        jsval thisv = inner->getReservedSlot(JSRESERVED_GLOBAL_THIS);
-        if (!JSVAL_IS_VOID(thisv)) {
+        const Value &thisv = inner->getReservedSlot(JSRESERVED_GLOBAL_THIS);
+        if (!thisv.isUndefined()) {
             argv[-1] = thisv;
-            return JSVAL_TO_OBJECT(thisv);
+            return thisv.asObjectOrNull();
         }
 
         JSObject *stuntThis = CallThisObjectHook(cx, inner, argv);
         JS_ALWAYS_TRUE(js_SetReservedSlot(cx, inner, JSRESERVED_GLOBAL_THIS,
-                                          OBJECT_TO_JSVAL(stuntThis)));
+                                          ObjectOrNullTag(stuntThis)));
         return stuntThis;
     }
 
     return CallThisObjectHook(cx, inner, argv);
 }
 
+namespace js {
+
 JSObject *
-js_ComputeThis(JSContext *cx, jsval *argv)
+ComputeThisFromArgv(JSContext *cx, Value *argv)
 {
-    JS_ASSERT(argv[-1] != JSVAL_HOLE);  // check for SynthesizeFrame poisoning
-    if (JSVAL_IS_NULL(argv[-1]))
-        return js_ComputeGlobalThis(cx, argv);
+    JS_ASSERT(!argv[-1].isMagic(JS_THIS_POISON));
+    if (argv[-1].isNull())
+        return ComputeGlobalThis(cx, argv);
 
     JSObject *thisp;
 
-    JS_ASSERT(!JSVAL_IS_NULL(argv[-1]));
-    if (!JSVAL_IS_OBJECT(argv[-1])) {
+    JS_ASSERT(!argv[-1].isNull());
+    if (argv[-1].isPrimitive()) {
         if (!js_PrimitiveToObject(cx, &argv[-1]))
             return NULL;
-        thisp = JSVAL_TO_OBJECT(argv[-1]);
+        thisp = argv[-1].asObjectOrNull();
         return thisp;
     } 
 
-    thisp = JSVAL_TO_OBJECT(argv[-1]);
+    thisp = &argv[-1].asObject();
     if (thisp->getClass() == &js_CallClass || thisp->getClass() == &js_BlockClass)
-        return js_ComputeGlobalThis(cx, argv);
+        return ComputeGlobalThis(cx, argv);
 
     return CallThisObjectHook(cx, thisp, argv);
 }
 
+}
+
 #if JS_HAS_NO_SUCH_METHOD
 
 const uint32 JSSLOT_FOUND_FUNCTION  = JSSLOT_PRIVATE;
 const uint32 JSSLOT_SAVED_ID        = JSSLOT_PRIVATE + 1;
 
 Class js_NoSuchMethodClass = {
     "NoSuchMethod",
     JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
@@ -425,39 +440,39 @@ class AutoPreserveEnumerators {
 
     ~AutoPreserveEnumerators()
     {
         cx->enumerators = enumerators;
     }
 };
 
 static JS_REQUIRES_STACK bool
-callJSNative(JSContext *cx, JSCallOp callOp, JSObject *thisp, uintN argc, jsval *argv, jsval *rval)
+callJSNative(JSContext *cx, CallOp callOp, JSObject *thisp, uintN argc, Value *argv, Value *rval)
 {
-    jsval *vp = argv - 2;
+    Value *vp = argv - 2;
     if (callJSFastNative(cx, callOp, argc, vp)) {
         *rval = JS_RVAL(cx, vp);
         return true;
     }
     return false;
 }
 
 template <typename T>
 static JS_REQUIRES_STACK bool
-Invoke(JSContext *cx, JSFunction *fun, JSScript *script, T native,
+InvokeCommon(JSContext *cx, JSFunction *fun, JSScript *script, T native,
        const InvokeArgsGuard &args, uintN flags)
 {
     uintN argc = args.getArgc();
-    jsval *vp = args.getvp();
+    Value *vp = args.getvp();
 
     if (native && fun && fun->isFastNative()) {
 #ifdef DEBUG_NOT_THROWING
         JSBool alreadyThrowing = cx->throwing;
 #endif
-        JSBool ok = callJSFastNative(cx, (JSFastNative) native, argc, vp);
+        JSBool ok = callJSFastNative(cx, (FastNative) native, argc, vp);
         JS_RUNTIME_METER(cx->runtime, nativeCalls);
 #ifdef DEBUG_NOT_THROWING
         if (ok && !alreadyThrowing)
             ASSERT_NOT_THROWING(cx);
 #endif
         return ok;
     }
 
@@ -488,31 +503,29 @@ Invoke(JSContext *cx, JSFunction *fun, J
      */
     JSFrameRegs regs;
     InvokeFrameGuard frame;
     if (!cx->stack().getInvokeFrame(cx, args, nmissing, nfixed, frame))
         return false;
     JSStackFrame *fp = frame.getFrame();
 
     /* Initialize missing missing arguments and new local variables. */
-    jsval *missing = vp + 2 + argc;
-    for (jsval *v = missing, *end = missing + nmissing; v != end; ++v)
-        *v = JSVAL_VOID;
-    for (jsval *v = fp->slots(), *end = v + nvars; v != end; ++v)
-        *v = JSVAL_VOID;
+    Value *missing = vp + 2 + argc;
+    SetValueRangeToUndefined(missing, nmissing);
+    SetValueRangeToUndefined(fp->slots(), nvars);
 
     /* Initialize frame. */
     fp->thisv = vp[1];
     fp->callobj = NULL;
     fp->argsobj = NULL;
     fp->script = script;
     fp->fun = fun;
     fp->argc = argc;
     fp->argv = vp + 2;
-    fp->rval = (flags & JSINVOKE_CONSTRUCT) ? fp->thisv : JSVAL_VOID;
+    fp->rval = (flags & JSINVOKE_CONSTRUCT) ? fp->thisv : Value(UndefinedTag());
     fp->annotation = NULL;
     fp->scopeChain = NULL;
     fp->blockChain = NULL;
     fp->imacpc = NULL;
     fp->flags = flags;
     fp->displaySave = NULL;
 
     /* Initialize regs. */
@@ -523,28 +536,28 @@ Invoke(JSContext *cx, JSFunction *fun, J
         regs.pc = NULL;
         regs.sp = fp->slots();
     }
 
     /* Officially push |fp|. |frame|'s destructor pops. */
     cx->stack().pushInvokeFrame(cx, args, frame, regs);
 
     /* Now that the frame has been pushed, fix up the scope chain. */
-    JSObject *parent = JSVAL_TO_OBJECT(vp[0])->getParent();
+    JSObject *parent = vp[0].asObject().getParent();
     if (native) {
         /* Slow natives and call ops expect the caller's scopeChain as their scopeChain. */
         if (JSStackFrame *down = fp->down)
             fp->scopeChain = down->scopeChain;
 
         /* Ensure that we have a scope chain. */
-        if (!fp->scopeChainObj())
-            fp->setScopeChainObj(parent);
+        if (!fp->scopeChain)
+            fp->scopeChain = parent;
     } else {
         /* Use parent scope so js_GetCallObject can find the right "Call". */
-        fp->setScopeChainObj(parent);
+        fp->scopeChain = parent;
         if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
             return false;
     }
 
     /* Call the hook if present after we fully initialized the frame. */
     JSInterpreterHook hook = cx->debugHooks->callHook;
     void *hookData = NULL;
     if (hook)
@@ -554,17 +567,17 @@ Invoke(JSContext *cx, JSFunction *fun, J
 
     /* Call the function, either a native method or an interpreted script. */
     JSBool ok;
     if (native) {
 #ifdef DEBUG_NOT_THROWING
         JSBool alreadyThrowing = cx->throwing;
 #endif
 
-        JSObject *thisp = &fp->thisv.asObject();
+        JSObject *thisp = fp->thisv.asObjectOrNull();
         ok = callJSNative(cx, native, thisp, fp->argc, fp->argv, &fp->rval);
 
         JS_ASSERT(cx->fp == fp);
         JS_RUNTIME_METER(cx->runtime, nativeCalls);
 #ifdef DEBUG_NOT_THROWING
         if (ok && !alreadyThrowing)
             ASSERT_NOT_THROWING(cx);
 #endif
@@ -588,151 +601,156 @@ Invoke(JSContext *cx, JSFunction *fun, J
 }
 
 /*
  * Find a function reference and its 'this' value implicit first parameter
  * under argc arguments on cx's stack, and call the function.  Push missing
  * required arguments, allocate declared local variables, and pop everything
  * when done.  Then push the return value.
  */
-JS_REQUIRES_STACK JS_FRIEND_API(JSBool)
-js_Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
+JS_REQUIRES_STACK bool
+Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
 {
-    jsval *vp = args.getvp();
+    Value *vp = args.getvp();
     uintN argc = args.getArgc();
     JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
 
-    jsval v = vp[0];
-    if (JSVAL_IS_PRIMITIVE(v)) {
+    const Value &v = vp[0];
+    if (v.isPrimitive()) {
         js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
         return false;
     }
 
-    JSObject *funobj = JSVAL_TO_OBJECT(v);
-    JSClass *clasp = funobj->getClass();
+    JSObject *funobj = &v.asObject();
+    Class *clasp = funobj->getClass();
 
     if (clasp == &js_FunctionClass) {
         /* Get private data and set derived locals from it. */
         JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
-        JSNative native;
+        Native native;
         JSScript *script;
         if (FUN_INTERPRETED(fun)) {
             native = NULL;
             script = fun->u.i.script;
             JS_ASSERT(script);
 
             if (script->isEmpty()) {
                 if (flags & JSINVOKE_CONSTRUCT) {
-                    JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
+                    JS_ASSERT(vp[1].isObject());
                     *vp = vp[1];
                 } else {
-                    *vp = JSVAL_VOID;
+                    vp->setUndefined();
                 }
                 return true;
             }
         } else {
             native = fun->u.n.native;
             script = NULL;
         }
 
         if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
             /* Handle bound method special case. */
-            vp[1] = OBJECT_TO_JSVAL(funobj->getParent());
-        } else if (!JSVAL_IS_OBJECT(vp[1])) {
+            vp[1].setObject(*funobj->getParent());
+        } else if (!vp[1].isObjectOrNull()) {
             JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
-            if (PRIMITIVE_THIS_TEST(fun, vp[1]))
-                return Invoke(cx, fun, script, native, args, flags);
+            if (PrimitiveThisTest(fun, vp[1]))
+                return InvokeCommon(cx, fun, script, native, args, flags);
         }
 
         if (flags & JSINVOKE_CONSTRUCT) {
-            JS_ASSERT(!JSVAL_IS_PRIMITIVE(args.getvp()[1]));
+            JS_ASSERT(args.getvp()[1].isObject());
         } else {
             /*
              * We must call js_ComputeThis in case we are not called from the
              * interpreter, where a prior bytecode has computed an appropriate
              * |this| already.
              *
              * But we need to compute |this| eagerly only for so-called "slow"
              * (i.e., not fast) native functions. Fast natives must use either
              * JS_THIS or JS_THIS_OBJECT, and scripted functions will go through
              * the appropriate this-computing bytecode, e.g., JSOP_THIS.
              */
             if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) {
-                jsval *vp = args.getvp();
-                if (!js_ComputeThis(cx, vp + 2))
+                Value *vp = args.getvp();
+                if (!ComputeThisFromVp(cx, vp))
                     return false;
                 flags |= JSFRAME_COMPUTED_THIS;
             }
         }
-        return Invoke(cx, fun, script, native, args, flags);
+        return InvokeCommon(cx, fun, script, native, args, flags);
     }
 
 #if JS_HAS_NO_SUCH_METHOD
     if (clasp == &js_NoSuchMethodClass)
         return NoSuchMethod(cx, argc, vp, flags);
 #endif
 
     /* Function is inlined, all other classes use object ops. */
     const JSObjectOps *ops = funobj->map->ops;
 
     /* Try a call or construct native object op. */
     if (flags & JSINVOKE_CONSTRUCT) {
-        if (!JSVAL_IS_OBJECT(vp[1])) {
+        if (!vp[1].isObjectOrNull()) {
             if (!js_PrimitiveToObject(cx, &vp[1]))
                 return false;
         }
-        JSNative native = ops->construct;
+        Native native = ops->construct;
         if (!native) {
             js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
             return false;
         }
-        return Invoke(cx, NULL, NULL, native, args, flags);
+        return InvokeCommon(cx, NULL, NULL, native, args, flags);
     }
-    JSCallOp callOp = ops->call;
+    CallOp callOp = ops->call;
     if (!callOp) {
         js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
         return false;
     }
-    return Invoke(cx, NULL, NULL, callOp, args, flags);
+    return InvokeCommon(cx, NULL, NULL, callOp, args, flags);
+}
+
+extern JS_REQUIRES_STACK JS_FRIEND_API(bool)
+InvokeFriendAPI(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
+{
+    return Invoke(cx, args, flags);
 }
 
 JSBool
-js_InternalInvoke(JSContext *cx, jsval thisv, jsval fval, uintN flags,
-                  uintN argc, jsval *argv, jsval *rval)
+InternalInvoke(JSContext *cx, const Value &thisv, const Value &fval, uintN flags,
+                  uintN argc, Value *argv, Value *rval)
 {
     LeaveTrace(cx);
 
     InvokeArgsGuard args;
     if (!cx->stack().pushInvokeArgs(cx, argc, args))
         return JS_FALSE;
 
     args.getvp()[0] = fval;
     args.getvp()[1] = thisv;
-    memcpy(args.getvp() + 2, argv, argc * sizeof(jsval));
+    memcpy(args.getvp() + 2, argv, argc * sizeof(Value));
 
     if (!Invoke(cx, args, flags))
         return JS_FALSE;
 
     /*
      * Store *rval in the lastInternalResult pigeon-hole GC root, solely
      * so users of js_InternalInvoke and its direct and indirect
      * (js_ValueToString for example) callers do not need to manage roots
      * for local, temporary references to such results.
      */
     *rval = *args.getvp();
-    if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL)
-        cx->weakRoots.lastInternalResult = *rval;
+    if (rval->isMarkable())
+        cx->weakRoots.lastInternalResult = rval->asGCThing();
 
     return JS_TRUE;
 }
 
 bool
 InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
-                 JSAccessMode mode, uintN argc, const Value *argv,
-                 Value *rval)
+                 JSAccessMode mode, uintN argc, Value *argv, Value *rval)
 {
     LeaveTrace(cx);
 
     /*
      * InternalInvoke could result in another try to get or set the same id
      * again, see bug 355497.
      */
     JS_CHECK_RECURSION(cx, return JS_FALSE);
@@ -790,50 +808,50 @@ Execute(JSContext *cx, JSObject *const c
     }
 #endif
 
     /* Initialize frame. */
     JSObject *initialVarObj;
     if (down) {
         /* Propagate arg state for eval and the debugger API. */
         fp->callobj = down->callobj;
-        fp->setArgsObj(down->argsObj());
+        fp->argsobj = down->argsobj;
         fp->fun = (script->staticLevel > 0) ? down->fun : NULL;
         fp->thisv = down->thisv;
         fp->flags = flags | (down->flags & JSFRAME_COMPUTED_THIS);
         fp->argc = down->argc;
         fp->argv = down->argv;
         fp->annotation = down->annotation;
-        fp->setScopeChainObj(chain);
+        fp->scopeChain = chain;
 
         /*
          * We want to call |down->varobj()|, but this requires knowing the
          * CallStack of |down|. If |down == cx->fp|, the callstack is simply
          * the context's active callstack, so we can use |down->varobj(cx)|.
          * When |down != cx->fp|, we need to do a slow linear search. Luckily,
          * this only happens with indirect eval and JS_EvaluateInStackFrame.
          */
         initialVarObj = (down == cx->fp)
                         ? down->varobj(cx)
                         : down->varobj(cx->containingCallStack(down));
     } else {
         fp->callobj = NULL;
-        fp->setArgsObj(NULL);
+        fp->argsobj = NULL;
         fp->fun = NULL;
         /* Ininitialize fp->thisv after pushExecuteFrame. */
         fp->flags = flags | JSFRAME_COMPUTED_THIS;
         fp->argc = 0;
         fp->argv = NULL;
         fp->annotation = NULL;
 
         JSObject *innerizedChain = chain;
-        Innerize(cx, &innerizedChain);
+        OBJ_TO_INNER_OBJECT(cx, innerizedChain);
         if (!innerizedChain)
             return false;
-        fp->setScopeChainObj(innerizedChain);
+        fp->scopeChain = innerizedChain;
 
         initialVarObj = (cx->options & JSOPTION_VAROBJFIX)
                         ? chain->getGlobal()
                         : chain;
     }
     JS_ASSERT(initialVarObj->map->ops->defineProperty == js_DefineProperty);
 
     fp->script = script;
@@ -1140,18 +1158,18 @@ InvokeConstructor(JSContext *cx, const I
     AutoObjectRooter tvr(cx, obj);
 
     /* Now we have an object with a constructor method; call it. */
     vp[1].setObject(*obj);
     if (!Invoke(cx, args, JSINVOKE_CONSTRUCT))
         return JS_FALSE;
 
     /* Check the return value and if it's primitive, force it to be obj. */
-    jsval rval = *vp;
-    if (JSVAL_IS_PRIMITIVE(rval)) {
+    const Value &rval = *vp;
+    if (rval.isPrimitive()) {
         if (!fun) {
             /* native [[Construct]] returning primitive is error */
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_BAD_NEW_RESULT,
                                  js_ValueToPrintableString(cx, *vp));
             return JS_FALSE;
         }
         vp->setObject(*obj);
@@ -1200,48 +1218,49 @@ js_EnterWith(JSContext *cx, jsint stackI
     Value *sp = cx->regs->sp;
     JS_ASSERT(stackIndex < 0);
     JS_ASSERT(fp->base() <= sp + stackIndex);
 
     JSObject *obj;
     if (sp[-1].isObject()) {
         obj = &sp[-1].asObject();
     } else {
-        if (!js_ValueToNonNullObject(cx, sp[-1], &sp[-1]))
+        obj = js_ValueToNonNullObject(cx, sp[-1]);
+        if (!obj)
             return JS_FALSE;
-        obj = &sp[-1].asObject();
+        sp[-1].setObject(*obj);
     }
 
     JSObject *parent = js_GetScopeChain(cx, fp);
     if (!parent)
         return JS_FALSE;
 
-    Innerize(cx, &obj);
+    OBJ_TO_INNER_OBJECT(cx, obj);
     if (!obj)
         return JS_FALSE;
 
     JSObject *withobj = js_NewWithObject(cx, obj, parent,
                                          sp + stackIndex - fp->base());
     if (!withobj)
         return JS_FALSE;
 
-    fp->setScopeChainObj(withobj);
+    fp->scopeChain = withobj;
     return JS_TRUE;
 }
 
 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
 js_LeaveWith(JSContext *cx)
 {
     JSObject *withobj;
 
-    withobj = cx->fp->scopeChainObj();
+    withobj = cx->fp->scopeChain;
     JS_ASSERT(withobj->getClass() == &js_WithClass);
     JS_ASSERT(withobj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp));
     JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
-    cx->fp->setScopeChainObj(withobj->getParent());
+    cx->fp->scopeChain = withobj->getParent();
     withobj->setPrivate(NULL);
 }
 
 JS_REQUIRES_STACK Class *
 js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
 {
     Class *clasp;
 
@@ -1271,17 +1290,17 @@ js_UnwindScope(JSContext *cx, jsint stac
     for (obj = fp->blockChain; obj; obj = obj->getParent()) {
         JS_ASSERT(obj->getClass() == &js_BlockClass);
         if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
             break;
     }
     fp->blockChain = obj;
 
     for (;;) {
-        obj = fp->scopeChainObj();
+        obj = fp->scopeChain;
         clasp = js_IsActiveWithOrBlock(cx, obj, stackDepth);
         if (!clasp)
             break;
         if (clasp == &js_BlockClass) {
             /* Don't fail until after we've updated all stacks. */
             normalUnwind &= js_PutBlockObject(cx, normalUnwind);
         } else {
             js_LeaveWith(cx);
@@ -1760,21 +1779,20 @@ namespace reprmeter {
         regs.sp--;                                                            \
     JS_END_MACRO
 
 #define VALUE_TO_OBJECT(cx, vp, obj)                                          \
     JS_BEGIN_MACRO                                                            \
         if ((vp)->isObject()) {                                               \
             obj = &(vp)->asObject();                                          \
         } else {                                                              \
-            Value v;                                                          \
-            if (!js_ValueToNonNullObject(cx, *(vp), &v))                      \
+            obj = js_ValueToNonNullObject(cx, *(vp));                         \
+            if (!obj)                                                         \
                 goto error;                                                   \
-            *(vp) = v;                                                        \
-            obj = &v.asObject();                                              \
+            (vp)->setObject(*obj);                                            \
         }                                                                     \
     JS_END_MACRO
 
 #define FETCH_OBJECT(cx, n, obj)                                              \
     JS_BEGIN_MACRO                                                            \
         Value *vp_ = &regs.sp[n];                                             \
         VALUE_TO_OBJECT(cx, vp_, obj);                                        \
     JS_END_MACRO
@@ -2004,26 +2022,26 @@ IteratorMore(JSContext *cx, JSObject *it
 }
 
 static inline bool
 IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
 {
     if (iterobj->getClass() == &js_IteratorClass.base) {
         NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
         JS_ASSERT(ni->props_cursor < ni->props_end);
-        if ((ni->flags & JSITER_FOREACH) == 0) {
-            jsid id = ni->currentId();
+        if (ni->isKeyIter()) {
+            jsid id = *ni->currentKey();
             if (JSID_IS_ATOM(id)) {
                 rval->setString(JSID_TO_STRING(id));
-                ni->incIdCursor();
+                ni->incKeyCursor();
                 return true;
             }
             /* Take the slow path if we have to stringify a numeric property name. */
         } else {
-            *rval = ni->currentValue();
+            *rval = *ni->currentValue();
             ni->incValueCursor();
             return true;
         }
     }
     return js_IteratorNext(cx, iterobj, rval);
 }
 
 
@@ -2628,17 +2646,17 @@ Interpret(JSContext *cx)
     cx->setCurrentRegs(prevContextRegs);
 
 #ifdef JS_TRACER
     if (TRACE_RECORDER(cx))
         AbortRecording(cx, "recording out of Interpret");
 #endif
 
     JS_ASSERT_IF(!fp->isGenerator(), !fp->blockChain);
-    JS_ASSERT_IF(!fp->isGenerator(), !js_IsActiveWithOrBlock(cx, fp->scopeChainObj(), 0));
+    JS_ASSERT_IF(!fp->isGenerator(), !js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
 
     /* Undo the remaining effects committed on entry to Interpret. */
     if (script->staticLevel < JS_DISPLAY_SIZE)
         cx->display[script->staticLevel] = fp->displaySave;
     if (cx->version == currentVersion && currentVersion != originalVersion)
         js_SetVersion(cx, originalVersion);
     --cx->interpLevel;
 
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -79,17 +79,17 @@ enum JSFrameFlags {
  *
  * NB: This struct is manually initialized in jsinterp.c and jsiter.c.  If you
  * add new members, update both files.
  */
 struct JSStackFrame
 {
     jsbytecode          *imacpc;        /* null or interpreter macro call pc */
     JSObject            *callobj;       /* lazily created Call object */
-    js::Value           argsval;       /* lazily created arguments object */
+    JSObject            *argsobj;       /* lazily created arguments object */
     JSScript            *script;        /* script being interpreted */
     JSFunction          *fun;           /* function being called or null */
     js::Value           thisv;          /* "this" pointer if in method */
     uintN               argc;           /* actual argument count */
     js::Value           *argv;          /* base of argument stack slots */
     js::Value           rval;           /* function return value */
     void                *annotation;    /* used by Java security */
 
@@ -133,35 +133,35 @@ struct JSStackFrame
      * never close over a lexical block, we never place a mutable
      * clone of it on scopeChain.
      *
      * This lazy cloning is implemented in js_GetScopeChain, which is
      * also used in some other cases --- entering 'with' blocks, for
      * example.
      */
     JSObject        *blockChain;
-    js::Value       scopeChain;
+    JSObject        *scopeChain;
 
     uint32          flags;          /* frame flags -- see below */
     JSStackFrame    *displaySave;   /* previous value of display entry for
                                        script->staticLevel */
 
     /* Members only needed for inline calls. */
     void            *hookData;      /* debugger call hook data */
     JSVersion       callerVersion;  /* dynamic version of calling script */
 
     void putActivationObjects(JSContext *cx) {
         /*
          * The order of calls here is important as js_PutCallObject needs to
          * access argsobj.
          */
         if (callobj) {
             js_PutCallObject(cx, this);
-            JS_ASSERT(argsval.isNull());
-        } else if (argsval.isObject()) {
+            JS_ASSERT(!argsobj);
+        } else if (argsobj) {
             js_PutArgsObject(cx, this);
         }
     }
 
     /* Get the frame's current bytecode, assuming |this| is in |cx|. */
     jsbytecode *pc(JSContext *cx) const;
 
     js::Value *argEnd() const {
@@ -180,32 +180,16 @@ struct JSStackFrame
         JS_ASSERT(argv);
         return argv[-2];
     }
 
     JSObject *callee() {
         return argv ? &argv[-2].asObject() : NULL;
     }
 
-    JSObject *argsObj() {
-        return argsval.asObjectOrNull();
-    }
-
-    void setArgsObj(JSObject *obj) {
-        argsval.setObjectOrNull(obj);
-    }
-
-    JSObject *scopeChainObj() {
-        return scopeChain.asObjectOrNull();
-    }
-
-    void setScopeChainObj(JSObject *obj) {
-        scopeChain.setObjectOrNull(obj);
-    }
-
     /*
      * Get the object associated with the Execution Context's
      * VariableEnvironment (ES5 10.3). The given CallStack must contain this
      * stack frame.
      */
     JSObject *varobj(js::CallStack *cs) const;
 
     /* Short for: varobj(cx->activeCallStack()). */
@@ -218,16 +202,17 @@ struct JSStackFrame
         JS_ASSERT_IF(flags & JSFRAME_FLOATING_GENERATOR, isGenerator());
         return !!(flags & JSFRAME_FLOATING_GENERATOR);
     }
 
     bool isDummyFrame() const { return !script && !fun; }
 };
 
 namespace js {
+
 JS_STATIC_ASSERT(sizeof(JSStackFrame) % sizeof(Value) == 0);
 static const size_t VALUES_PER_STACK_FRAME = sizeof(JSStackFrame) / sizeof(Value);
 
 JS_STATIC_ASSERT(offsetof(JSStackFrame, rval) % sizeof(Value) == 0);
 JS_STATIC_ASSERT(offsetof(JSStackFrame, thisv) % sizeof(Value) == 0);
 JS_STATIC_ASSERT(offsetof(JSStackFrame, scopeChain) % sizeof(Value) == 0);
 
 } /* namespace js */
@@ -266,30 +251,22 @@ namespace js {
 
 /*
  * For a call with arguments argv including argv[-1] (nominal |this|) and
  * argv[-2] (callee) replace null |this| with callee's parent, replace
  * primitive values with the equivalent wrapper objects and censor activation
  * objects as, per ECMA-262, they may not be referred to by |this|. argv[-1]
  * must not be a JSVAL_VOID.
  */
-extern bool
+extern JSObject *
 ComputeThisFromArgv(JSContext *cx, js::Value *argv);
 
 JS_ALWAYS_INLINE JSObject *
-ComputeThisObjectFromVp(JSContext *cx, js::Value *vp)
+ComputeThisFromVp(JSContext *cx, js::Value *vp)
 {
-    extern bool ComputeThisFromArgv(JSContext *, js::Value *);
-    return ComputeThisFromArgv(cx, vp + 2) ? &vp[1].asObject() : NULL;
-}
-
-JS_ALWAYS_INLINE bool
-ComputeThisFromVpInPlace(JSContext *cx, js::Value *vp)
-{
-    extern bool ComputeThisFromArgv(JSContext *, js::Value *);
     return ComputeThisFromArgv(cx, vp + 2);
 }
 
 JS_ALWAYS_INLINE bool
 PrimitiveThisTest(JSFunction *fun, const Value &v)
 {
     uint16 flags = fun->flags;
     return (v.isString() && !!(flags & JSFUN_THISP_STRING)) ||
@@ -326,42 +303,48 @@ InvokeFriendAPI(JSContext *cx, const Inv
  */
 #define JSINVOKE_CONSTRUCT      JSFRAME_CONSTRUCTING
 
 /*
  * Mask to isolate construct and iterator flags for use with jsfun.h functions.
  */
 #define JSINVOKE_FUNFLAGS       JSINVOKE_CONSTRUCT
 
-#error "TODO: un-js_ and re-inline"
-
 /*
  * "Internal" calls may come from C or C++ code using a JSContext on which no
  * JS is running (!cx->fp), so they may need to push a dummy JSStackFrame.
  */
-#define js_InternalCall(cx,obj,fval,argc,argv,rval)                           \
-    js_InternalInvoke(cx, OBJECT_TO_JSVAL(obj), fval, 0, argc, argv, rval)
+extern JSBool
+InternalInvoke(JSContext *cx, const Value &thisv, const Value &fval, uintN flags,
+               uintN argc, Value *argv, Value *rval);
 
-#define js_InternalConstruct(cx,obj,fval,argc,argv,rval)                      \
-    js_InternalInvoke(cx, OBJECT_TO_JSVAL(obj), fval, JSINVOKE_CONSTRUCT, argc, argv, rval)
+static JS_ALWAYS_INLINE bool
+InternalCall(JSContext *cx, JSObject *obj, const Value &fval,
+             uintN argc, Value *argv, Value *rval)
+{
+    return InternalInvoke(cx, ObjectOrNullTag(obj), fval, 0, argc, argv, rval);
+}
 
-extern JSBool
-js_InternalInvoke(JSContext *cx, jsval thisv, jsval fval, uintN flags,
-                  uintN argc, jsval *argv, jsval *rval);
+static JS_ALWAYS_INLINE bool
+InternalConstruct(JSContext *cx, JSObject *obj, const Value &fval,
+                  uintN argc, Value *argv, Value *rval)
+{
+    return InternalInvoke(cx, ObjectOrNullTag(obj), fval, JSINVOKE_CONSTRUCT, argc, argv, rval);
+}
 
 extern bool
 InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
-                 JSAccessMode mode, uintN argc, const Value *argv, Value *rval);
+                 JSAccessMode mode, uintN argc, Value *argv, Value *rval);
 
 extern JS_FORCES_STACK bool
 Execute(JSContext *cx, JSObject *chain, JSScript *script,
         JSStackFrame *down, uintN flags, Value *result);
 
 extern JS_REQUIRES_STACK bool
-InvokeConstructor(JSContext *cx, const InvokeArgsGuard &args, JSBool clampReturn);
+InvokeConstructor(JSContext *cx, const InvokeArgsGuard &args);
 
 extern JS_REQUIRES_STACK bool
 Interpret(JSContext *cx);
 
 #define JSPROP_INITIALIZER 0x100   /* NB: Not a valid property attribute. */
 
 extern bool
 CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
@@ -370,17 +353,17 @@ CheckRedeclaration(JSContext *cx, JSObje
 extern bool
 StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval);
 
 /* === 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);
 
 extern JSType
-TypeOfValue(JSContext *cx, const js::Value &v);
+TypeOfValue(JSContext *cx, const Value &v);
 
 inline bool
 InstanceOf(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
 {
     if (obj && obj->getClass() == clasp)
         return true;
     extern bool InstanceOfSlow(JSContext *, JSObject *, Class *, Value *);
     return InstanceOfSlow(cx, obj, clasp, argv);
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -100,20 +100,20 @@ ExtendedClass js_IteratorClass = {
     NULL,             NULL,            NULL,             iterator_iterator,
     NULL,
     JSCLASS_NO_RESERVED_MEMBERS
 };
 
 void
 NativeIterator::mark(JSTracer *trc)
 {
-    if ((flags & JSITER_FOREACH) == 0)
-        MarkIdRange(trc, (jsid *)props_array, (jsid *)props_end, "props");
+    if (isKeyIter())
+        MarkIdRange(trc, beginKey(), endKey(), "props");
     else
-        MarkValueRange(trc, (Value *)props_array, (Value *)props_end, "props");
+        MarkValueRange(trc, beginValue(), endValue(), "props");
     MarkObject(trc, obj, "obj");
 }
 
 /*
  * Shared code to close iterator's state either through an explicit call or
  * when GC detects that the iterator is no longer reachable.
  */
 static void
@@ -133,75 +133,59 @@ static void
 iterator_trace(JSTracer *trc, JSObject *obj)
 {
     NativeIterator *ni = obj->getNativeIterator();
 
     if (ni)
         ni->mark(trc);
 }
 
+struct IdHashPolicy {
+    typedef jsid Lookup;
+    static HashNumber hash(jsid id) {
+        return JSID_BITS(id);
+    }
+    static bool match(jsid id1, jsid id2) {
+        return id1 == id2;
+    }
+};
+
+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);
     if (!aobj)
         return false;
     rval->setObject(*aobj);
     return true;
 }
 
-template <class T>
-static inline void
-Reverse(T *beg, T *end)
+struct KeyEnumeration
 {
-    while (beg != end) {
-        if (--end == beg)
-            return;
-        T tmp = *beg;
-        *beg = *end;
-        *end = tmp;
-        ++beg;
-    }
-}
+    typedef AutoIdVector ResultVector;
 
-struct IterateKeyResults
-{
-    AutoIdVector vec;
-    IterateKeyResults(JSContext *cx) : vec(cx) {}
-
-    const jsid *begin() const { return vec.begin(); }
-    size_t length() const { return vec.length(); }
-
-    void reverse(size_t startingAt) {
-        Reverse(vec.begin() + startingAt, vec.end());
-    }
-
-    JS_ALWAYS_INLINE bool addProperty(JSContext *, JSObject *, jsid id, uintN flags)
+    static JS_ALWAYS_INLINE bool
+    append(JSContext *, AutoIdVector &vec, JSObject *, jsid id, uintN flags)
     {
         JS_ASSERT((flags & JSITER_FOREACH) == 0);
         return vec.append(id);
     }
 };
 
-struct IterateValueResults
+struct ValueEnumeration
 {
-    AutoValueVector vec;
-    IterateValueResults(JSContext *cx) : vec(cx) {}
+    typedef AutoValueVector ResultVector;
 
-    const Value *begin() const { return vec.begin(); }
-    size_t length() const { return vec.length(); }
-
-    void reverse(size_t startingAt) {
-        Reverse(vec.begin() + startingAt, vec.end());
-    }
-
-    bool addProperty(JSContext *cx, JSObject *obj, jsid id, uintN flags)
+    static JS_ALWAYS_INLINE bool
+    append(JSContext *cx, AutoValueVector &vec, JSObject *obj, jsid id, uintN flags)
     {
         JS_ASSERT(flags & JSITER_FOREACH);
 
         if (!vec.growBy(1))
             return false;
 
         /* Do the lookup on the original object instead of the prototype. */
         Value *vp = vec.end() - 1;
@@ -209,261 +193,171 @@ struct IterateValueResults
             return false;
         if ((flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *vp, vp))
             return false;
 
         return true;
     }
 };
 
-struct IdHashPolicy {
-    typedef jsid Lookup;
-    static HashNumber hash(jsid id) {
-        return JSID_BITS(id);
-    }
-    static bool match(jsid id1, jsid id2) {
-        return id1 == id2;
-    }
-};
-
-typedef HashSet<jsid, IdHashPolicy, ContextAllocPolicy> IdSet;
-
-template <class ResultSet>
+template <class EnumPolicy>
 static inline bool
 Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
-          bool enumerable, uintN flags, IdSet& seen, ResultSet &results)
+          bool enumerable, uintN flags, IdSet& ht,
+          typename EnumPolicy::ResultVector &vec)
 {
     JS_ASSERT(JSID_IS_INT(id) || JSID_IS_ATOM(id));
 
     if (JS_LIKELY(!(flags & JSITER_OWNONLY))) {
-        IdSet::AddPtr p = seen.lookupForAdd(id);
+        IdSet::AddPtr p = ht.lookupForAdd(id);
         /* property already encountered, done. */
         if (JS_UNLIKELY(!!p))
             return true;
         /* no need to add properties to the hash table at the end of the prototype chain */
-        if (pobj->getProto() && !seen.add(p, id))
+        if (pobj->getProto() && !ht.add(p, id))
             return false;
     }
 
     if (enumerable || (flags & JSITER_HIDDEN))
-        return results.addProperty(cx, obj, id, flags);
+        return EnumPolicy::append(cx, vec, obj, id, flags);
 
     return true;
 }
 
-template <class ResultSet>
+template <class EnumPolicy>
 static bool
-EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags,
-                          IdSet &seen, ResultSet &results)
+EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags, IdSet &ht,
+                          typename EnumPolicy::ResultVector &vec)
 {
     JS_LOCK_OBJ(cx, pobj);
 
-    size_t initialLength = results.length();
+    size_t initialLength = vec.length();
 
     /* Collect all unique properties from this object's scope. */
     JSScope *scope = pobj->scope();
     for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
         if (!JSID_IS_DEFAULT_XML_NAMESPACE(sprop->id) &&
             !sprop->isAlias() &&
-            !Enumerate(cx, obj, pobj, sprop->id, sprop->enumerable(), flags, seen, results)) {
+            !Enumerate<EnumPolicy>(cx, obj, pobj, sprop->id, sprop->enumerable(), flags, ht, vec)) {
             return false;
         }
     }
 
-    results.reverse(initialLength);
+    Reverse(vec.begin() + initialLength, vec.end());
 
     JS_UNLOCK_SCOPE(cx, scope);
     return true;
 }
 
-template <class ResultSet>
+template <class EnumPolicy>
 static bool
 EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags,
-                              IdSet &seen, ResultSet &results)
+                              IdSet &ht, typename EnumPolicy::ResultVector &vec)
 {
     size_t count = pobj->getDenseArrayCount();
 
     if (count) {
         size_t capacity = pobj->getDenseArrayCapacity();
         Value *vp = pobj->dslots;
         for (size_t i = 0; i < capacity; ++i, ++vp) {
             if (!vp->isMagic(JS_ARRAY_HOLE)) {
                 /* Dense arrays never get so large that i would not fit into an integer id. */
-                if (!Enumerate(cx, obj, pobj, INT_TO_JSID(i), true, flags, seen, results))
+                if (!Enumerate<EnumPolicy>(cx, obj, pobj, INT_TO_JSID(i), true, flags, ht, vec))
                     return false;
             }
         }
     }
     return true;
 }
 
-inline void
-NativeIterator::init(JSObject *obj, uintN flags, uint32 *sarray, uint32 slength, uint32 key)
-{
-    this->obj = obj;
-    this->flags = flags;
-    this->shapes_array = (uint32 *) this->props_end;
-    this->shapes_length = slength;
-    this->shapes_key = key;
-    if (slength)
-        memcpy(this->shapes_array, sarray, slength * sizeof(uint32));
-}
-
-NativeIterator *
-NativeIterator::allocateKeyIterator(JSContext *cx, uint32 slength,
-                                    const jsid *parray, uint32 plength)
+template <class EnumPolicy>
+static bool
+Snapshot(JSContext *cx, JSObject *obj, uintN flags, typename EnumPolicy::ResultVector &vec)
 {
-    NativeIterator *ni = (NativeIterator *)
-        cx->malloc(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(uint32));
-    if (!ni)
-        return NULL;
-    ni->props_array = ni->props_cursor = (jsid *) (ni + 1);
-    ni->props_end = (jsid *)ni->props_array + plength;
-    if (plength)
-        memcpy(ni->props_array, parray, plength * sizeof(jsid));
-    return ni;
-}
-
-NativeIterator *
-NativeIterator::allocateValueIterator(JSContext *cx, uint32 slength,
-                                      const Value *parray, uint32 plength)
-{
-    NativeIterator *ni = (NativeIterator *)
-        cx->malloc(sizeof(NativeIterator) + plength * sizeof(Value) + slength * sizeof(uint32));
-    if (!ni)
-        return NULL;
-    ni->props_array = ni->props_cursor = (Value *) (ni + 1);
-    ni->props_end = (Value *)ni->props_array + plength;
-    if (plength)
-        memcpy(ni->props_array, parray, plength * sizeof(Value));
-    return ni;
-}
-
-template <class ResultSet>
-static bool
-Snapshot(JSContext *cx, JSObject *obj, uintN flags, ResultSet &results)
-{
-    IdSet seen(cx);
-    if (!(flags & JSITER_OWNONLY) && !seen.init(32))
+    IdSet ht(cx);
+    if (!(flags & JSITER_OWNONLY) && !ht.init(32))
         return NULL;
 
     JSObject *pobj = obj;
     do {
         Class *clasp = pobj->getClass();
         if (pobj->isNative() &&
             pobj->map->ops->enumerate == js_Enumerate &&
             !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
             if (!clasp->enumerate(cx, pobj))
                 return false;
-            if (!EnumerateNativeProperties(cx, obj, pobj, flags, seen, results))
+            if (!EnumerateNativeProperties<EnumPolicy>(cx, obj, pobj, flags, ht, vec))
                 return false;
         } else if (pobj->isDenseArray()) {
-            if (!EnumerateDenseArrayProperties(cx, obj, pobj, flags, seen, results))
+            if (!EnumerateDenseArrayProperties<EnumPolicy>(cx, obj, pobj, flags, ht, vec))
                 return false;
         } else {
             if (pobj->isProxy()) {
                 AutoIdVector proxyProps(cx);
                 if (flags & JSITER_OWNONLY) {
                     if (!JSProxy::enumerateOwn(cx, pobj, proxyProps))
                         return false;
                 } else {
                     if (!JSProxy::enumerate(cx, pobj, proxyProps))
                         return false;
                 }
                 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
-                    if (!Enumerate(cx, obj, pobj, proxyProps[n], true, flags, seen, results))
+                    if (!Enumerate<EnumPolicy>(cx, obj, pobj, proxyProps[n], true, flags, ht, vec))
                         return false;
                 }
                 /* Proxy objects enumerate the prototype on their own, so we are done here. */
                 break;
             }
             Value state;
             if (!pobj->enumerate(cx, JSENUMERATE_INIT, &state, NULL))
                 return false;
             if (state.isMagic(JS_NATIVE_ENUMERATE)) {
-                if (!EnumerateNativeProperties(cx, obj, pobj, flags, seen, results))
+                if (!EnumerateNativeProperties<EnumPolicy>(cx, obj, pobj, flags, ht, vec))
                     return false;
             } else {
                 while (true) {
                     jsid id;
                     if (!pobj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
                         return false;
                     if (state.isNull())
                         break;
-                    if (!Enumerate(cx, obj, pobj, id, true, flags, seen, results))
+                    if (!Enumerate<EnumPolicy>(cx, obj, pobj, id, true, flags, ht, vec))
                         return false;
                 }
             }
         }
 
         if (JS_UNLIKELY(pobj->isXML() || (flags & JSITER_OWNONLY)))
             break;
     } while ((pobj = pobj->getProto()) != NULL);
 
     return true;
 }
 
-static NativeIterator *
-SnapshotValues(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, uint32 slength, uint32 key)
+bool
+VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap)
 {
-    JS_ASSERT(flags & JSITER_FOREACH);
-
-    IterateValueResults results(cx);
-    if (!Snapshot(cx, obj, flags, results))
-        return NULL;
-
-    NativeIterator *ni = NativeIterator::allocateValueIterator(cx, slength,
-                                                               results.begin(), results.length());
-    if (!ni)
-        return NULL;
-    ni->init(obj, flags, sarray, slength, key);
-    return ni;
-}
-
-static NativeIterator *
-SnapshotKeys(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, uint32 slength, uint32 key)
-{
-    JS_ASSERT(!(flags & JSITER_FOREACH));
+    JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid));
+    size_t len = props.length();
+    size_t idsz = len * sizeof(jsid);
+    size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz;
+    JSIdArray *ida = static_cast<JSIdArray *>(cx->malloc(sz));
+    if (!ida)
+        return false;
 
-    IterateKeyResults results(cx);
-    if (!Snapshot(cx, obj, flags, results))
-        return NULL;
-
-    NativeIterator *ni = NativeIterator::allocateKeyIterator(cx, slength,
-                                                             results.begin(), results.length());
-    if (!ni)
-        return NULL;
-    ni->init(obj, flags, sarray, slength, key);
-    return ni;
-}
-
-static inline bool
-NativeIteratorToJSIdArray(JSContext *cx, NativeIterator *ni, JSIdArray **idap)
-{
-    /* Morph the NativeIterator into a JSIdArray. The caller will deallocate it. */
-    JS_ASSERT(sizeof(NativeIterator) > sizeof(JSIdArray));
-    JS_ASSERT(!(ni->flags & JSITER_FOREACH));
-    JS_ASSERT(ni->props_array == (jsid *) (ni + 1));
-    size_t length = size_t((jsid *)ni->props_end - (jsid *)ni->props_array);
-    JSIdArray *ida = (JSIdArray *) (uintptr_t(ni->props_array) - (sizeof(JSIdArray) - sizeof(jsid)));
-    ida->self = ni;
-    ida->length = length;
-    JS_ASSERT(&ida->vector[0] == (jsid *)ni->props_array);
+    ida->length = static_cast<jsint>(len);
+    memcpy(ida->vector, props.begin(), idsz);
     *idap = ida;
     return true;
 }
 
 bool
 GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props)
 {
-    #error "either want to have SnapshotKeys fill props or something else...
-    NativeIterator *ni = SnapshotKeys(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), NULL, 0, true);
-    if (!ni)
-        return false;
-    return NativeIteratorToJSIdArray(cx, ni, idap);
+    return Snapshot<KeyEnumeration>(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props);
 }
 
 static inline bool
 GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
 {
     /* Check whether we have a valid __iterator__ method. */
     JSAtom *atom = cx->runtime->atomState.iteratorAtom;
     if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp))
@@ -521,64 +415,157 @@ NewIteratorObject(JSContext *cx, uintN f
          * are not stillborn, with the exception of "NoSuchMethod" internal
          * helper objects) expect it to have a non-null map pointer, so we
          * share an empty Enumerator scope in the runtime.
          */
         JSObject *obj = js_NewGCObject(cx);
         if (!obj)
             return false;
         obj->map = cx->runtime->emptyEnumeratorScope->hold();
-        obj->init(&js_IteratorClass.base, NULL, NULL, JSVAL_NULL);
+        obj->init(&js_IteratorClass.base, NULL, NULL, NullTag());
         return obj;
     }
 
     return NewBuiltinClassInstance(cx, &js_IteratorClass.base);
 }
 
+NativeIterator *
+NativeIterator::allocateKeyIterator(JSContext *cx, uint32 slength, const AutoIdVector &props)
+{
+    size_t plength = props.length();
+    NativeIterator *ni = (NativeIterator *)
+        cx->malloc(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(uint32));
+    if (!ni)
+        return NULL;
+    ni->props_array = ni->props_cursor = (jsid *) (ni + 1);
+    ni->props_end = (jsid *)ni->props_array + plength;
+    if (plength)
+        memcpy(ni->props_array, props.begin(), plength * sizeof(jsid));
+    return ni;
+}
+
+NativeIterator *
+NativeIterator::allocateValueIterator(JSContext *cx, uint32 slength, const AutoValueVector &props)
+{
+    size_t plength = props.length();
+    NativeIterator *ni = (NativeIterator *)
+        cx->malloc(sizeof(NativeIterator) + plength * sizeof(Value) + slength * sizeof(uint32));
+    if (!ni)
+        return NULL;
+    ni->props_array = ni->props_cursor = (Value *) (ni + 1);
+    ni->props_end = (Value *)ni->props_array + plength;
+    if (plength)
+        memcpy(ni->props_array, props.begin(), plength * sizeof(Value));
+    return ni;
+}
+
+inline void
+NativeIterator::init(JSObject *obj, uintN flags, const uint32 *sarray, uint32 slength, uint32 key)
+{
+    this->obj = obj;
+    this->flags = flags;
+    this->shapes_array = (uint32 *) this->props_end;
+    this->shapes_length = slength;
+    this->shapes_key = key;
+    if (slength)
+        memcpy(this->shapes_array, sarray, slength * sizeof(uint32));
+}
+
 static inline void
 RegisterEnumerator(JSContext *cx, JSObject *iterobj, NativeIterator *ni)
 {
     /* Register non-escaping native enumerators (for-in) with the current context. */
     if (ni->flags & JSITER_ENUMERATE) {
         ni->next = cx->enumerators;
         cx->enumerators = iterobj;
     }
 }
 
+static inline bool
+VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &keys,
+                    const uint32 *sarray, uint32 slength, uint32 key, Value *vp)
+{
+    JS_ASSERT(!(flags & JSITER_FOREACH));
+
+    JSObject *iterobj = NewIteratorObject(cx, flags);
+    if (!iterobj)
+        return false;
+
+
+    NativeIterator *ni = NativeIterator::allocateKeyIterator(cx, slength, keys);
+    if (!ni)
+        return NULL;
+    ni->init(obj, flags, sarray, slength, key);
+
+    iterobj->setNativeIterator(ni);
+    vp->setObject(*iterobj);
+
+    RegisterEnumerator(cx, iterobj, ni);
+    return true;
+}
+
 bool
-IdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props, Value *vp)
+VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props, Value *vp)
 {
+    return VectorToKeyIterator(cx, obj, flags, props, NULL, 0, 0, vp);
+}
+
+static inline bool
+VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, AutoValueVector &vals,
+                      const uint32 *sarray, uint32 slength, uint32 key, Value *vp)
+{
+    JS_ASSERT(flags & JSITER_FOREACH);
+
     JSObject *iterobj = NewIteratorObject(cx, flags);
     if (!iterobj)
         return false;
 
-    vp->setObject(*iterobj);
-
-    NativeIterator *ni = NativeIterator::allocateKeyIterator(cx, 0, props);
+    NativeIterator *ni = NativeIterator::allocateValueIterator(cx, slength, vals);
     if (!ni)
-        return false;
-    ni->init(obj, flags, NULL, 0, 0);
-
-    /* If this is a for-each iteration, fetch the values or key/value pairs. */
-    if (flags & JSITER_FOREACH) {
-        size_t length = props.length();
-        for (size_t n = 0; n < length; ++n) {
-            jsval *vp = &ni->begin()[n];
-            if (!IdToIteratorValue(cx, obj, *vp, flags, vp))
-                return false;
-        }
-    }
+        return NULL;
+    ni->init(obj, flags, sarray, slength, key);
 
     iterobj->setNativeIterator(ni);
+    vp->setObject(*iterobj);
 
     RegisterEnumerator(cx, iterobj, ni);
     return true;
 }
 
 bool
+VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, AutoValueVector &props, Value *vp)
+{
+    return VectorToValueIterator(cx, obj, flags, props, NULL, 0, 0, vp);
+}
+
+bool
+EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props, Value *vp)
+{
+    if (!(flags & JSITER_FOREACH))
+        return VectorToKeyIterator(cx, obj, flags, props, vp);
+
+    /* For for-each iteration, we need to look up the value of each id. */
+
+    size_t plength = props.length();
+
+    AutoValueVector vals(cx);
+    if (!vals.reserve(plength))
+        return NULL;
+
+    for (size_t i = 0; i < plength; ++i) {
+        if (!ValueEnumeration::append(cx, vals, obj, props[i], flags))
+            return false;
+    }
+
+    return VectorToValueIterator(cx, obj, flags, vals, vp);
+}
+
+typedef Vector<uint32, 8> ShapeVector;
+
+bool
 GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
 {
     uint32 hash;
     JSObject **hp;
     Vector<uint32, 8> shapes(cx);
     uint32 key = 0;
 
     bool keysOnly = (flags == JSITER_ENUMERATE);
@@ -627,34 +614,29 @@ GetIterator(JSContext *cx, JSObject *obj
         if (obj->isProxy())
             return JSProxy::iterate(cx, obj, flags, vp);
         if (!GetCustomIterator(cx, obj, flags, vp))
             return false;
         if (!vp->isUndefined())
             return true;
     }
 
-    JSObject *iterobj = NewIteratorObject(cx, flags);
-    if (!iterobj)
-        return false;
-
-    /* Store in *vp to protect it from GC (callers must root vp). */
-    vp->setObject(*iterobj);
+    /* NB: for (var p in null) succeeds by iterating over no properties. */
 
-    #error "TODO: handle null so that it succeeds with no properties... somehow"
-    NativeIterator *ni = (flags & JSITER_FOREACH)
-                         ? SnapshotValues(cx, obj, flags, shapes.begin(), shapes.length(), key)
-                         : SnapshotKeys(cx, obj, flags, shapes.begin(), shapes.length(), key);
-    if (!ni)
+    if (flags & JSITER_FOREACH) {
+        AutoValueVector vals(cx);
+        if (JS_LIKELY(obj != NULL) && !Snapshot<ValueEnumeration>(cx, obj, flags, vals))
+            return false;
+        return VectorToValueIterator(cx, obj, flags, vals, shapes.begin(), shapes.length(), key, vp);
+    }
+
+    AutoIdVector keys(cx);
+    if (JS_LIKELY(obj != NULL) && !Snapshot<KeyEnumeration>(cx, obj, flags, keys))
         return false;
-
-    iterobj->setNativeIterator(ni);
-
-    RegisterEnumerator(cx, iterobj, ni);
-    return true;
+    return VectorToKeyIterator(cx, obj, flags, keys, shapes.begin(), shapes.length(), key, vp);
 }
 
 static JSObject *
 iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
 {
     return obj;
 }
 
@@ -681,17 +663,17 @@ js_ThrowStopIteration(JSContext *cx)
     return JS_FALSE;
 }
 
 static JSBool
 iterator_next(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj;
 
-    obj = ComputeThisObjectFromVp(cx, vp);
+    obj = ComputeThisFromVp(cx, vp);
     if (!InstanceOf(cx, obj, &js_IteratorClass.base, vp + 2))
         return false;
 
     if (!js_IteratorMore(cx, obj, vp))
         return false;
     if (!vp->asBoolean()) {
         js_ThrowStopIteration(cx);
         return false;
@@ -718,40 +700,41 @@ js_ValueToIterator(JSContext *cx, uintN 
 
     /*
      * Make sure the more/next state machine doesn't get stuck. A value might be
      * left in iterValue when a trace is left due to an operation time-out after
      * JSOP_MOREITER but before the value is picked up by FOR*.
      */
     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
 
-    AutoValueRooter tvr(cx);
-
+    JSObject *obj;
     if (vp->isObject()) {
         /* Common case. */
-        *tvr.addr() = *vp;
+        obj = &vp->asObject();
     } else {
         /*
          * Enumerating over null and undefined gives an empty enumerator.
          * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
          * the first production in 12.6.4 and step 4 of the second production,
          * but it's "web JS" compatible. ES5 fixed for-in to match this de-facto
          * standard.
          */
         if ((flags & JSITER_ENUMERATE)) {
-            if (!js_ValueToObjectOrNull(cx, *vp, tvr.addr()))
+            if (!js_ValueToObjectOrNull(cx, *vp, &obj))
                 return false;
-            if (tvr.value().isNull())
+            if (!obj)
                 return GetIterator(cx, NULL, flags, vp);
         } else {
-            if (!js_ValueToNonNullObject(cx, *vp, tvr.addr()))
+            obj = js_ValueToNonNullObject(cx, *vp);
+            if (!obj)
                 return false;
         }
     }
-    JSObject *obj = &tvr.value().asObject();
+
+    AutoObjectRooter tvr(cx, obj);
 
     Class *clasp = obj->getClass();
     ExtendedClass *xclasp;
     if ((clasp->flags & JSCLASS_IS_EXTENDED) &&
         (xclasp = (ExtendedClass *) clasp)->iteratorObject) {
         /* Enumerate Iterator.prototype directly. */
         if (clasp != &js_IteratorClass.base || obj->getNativeIterator()) {
             JSObject *iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH));
@@ -819,20 +802,20 @@ js_CloseIterator(JSContext *cx, const Va
 bool
 js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
 {
     JSObject *iterobj = cx->enumerators;
     while (iterobj) {
       again:
         NativeIterator *ni = iterobj->getNativeIterator();
         /* This only works for identified surpressed keys, not values. */
-        if ((ni->flags & JSITER_FOREACH) && ni->obj == obj && ni->props_cursor < ni->props_end) {
+        if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
             /* Check whether id is still to come. */
-            jsid *props_cursor = (jsid *)ni->props_cursor;
-            jsid *props_end = (jsid *)ni->props_end;
+            jsid *props_cursor = ni->currentKey();
+            jsid *props_end = ni->endKey();
             for (jsid *idp = props_cursor; idp < props_end; ++idp) {
                 if (*idp == id) {
                     /*
                      * Check whether another property along the prototype chain
                      * became visible as a result of this deletion.
                      */
                     if (obj->getProto()) {
                         AutoObjectRooter proto(cx, obj->getProto());
@@ -861,20 +844,20 @@ js_SuppressDeletedProperty(JSContext *cx
                         goto again;
 
                     /*
                      * No property along the prototype chain steppeded in to take the
                      * property's place, so go ahead and delete id from the list.
                      * If it is the next property to be enumerated, just skip it.
                      */
                     if (idp == props_cursor) {
-                        ni->props_cursor = (jsid *)ni->props_cursor + 1;
+                        ni->incKeyCursor();
                     } else {
                         memmove(idp, idp + 1, (props_end - (idp + 1)) * sizeof(jsid));
-                        ni->props_end = (jsid *)ni->props_end - 1;
+                        ni->props_end = ni->endKey() - 1;
                     }
                     break;
                 }
             }
         }
         iterobj = ni->next;
     }
     return true;
@@ -930,25 +913,25 @@ js_IteratorNext(JSContext *cx, JSObject 
     /* Fast path for native iterators */
     if (iterobj->getClass() == &js_IteratorClass.base) {
         /*
          * Implement next directly as all the methods of the native iterator are
          * read-only and permanent.
          */
         NativeIterator *ni = iterobj->getNativeIterator();
         JS_ASSERT(ni->props_cursor < ni->props_end);
-        if ((ni->flags & JSITER_FOREACH) == 0) {
-            *rval = IdToValue(ni->currentId());
-            ni->incIdCursor();
+        if (ni->isKeyIter()) {
+            *rval = IdToValue(*ni->currentKey());
+            ni->incKeyCursor();
         } else {
-            *rval = ni->currentValue();
+            *rval = *ni->currentValue();
             ni->incValueCursor();
         }
 
-        if (rval->isString() || (ni->flags & JSITER_FOREACH))
+        if (rval->isString() || !ni->isKeyIter())
             return true;
 
         JSString *str;
         jsint i;
         if (rval->isInt32() && (jsuint(i = rval->asInt32()) < INT_STRING_LIMIT)) {
             str = JSString::intString(i);
         } else {
             str = js_ValueToString(cx, *rval);
@@ -1092,21 +1075,20 @@ js_NewGenerator(JSContext *cx)
 
     /* Copy generator's stack frame copy in from |cx->fp|. */
     newfp->imacpc = NULL;
     newfp->callobj = fp->callobj;
     if (fp->callobj) {      /* Steal call object. */
         fp->callobj->setPrivate(newfp);
         fp->callobj = NULL;
     }
-    JSObject *argsobj = fp->argsObj();
-    newfp->setArgsObj(argsobj);
-    if (argsobj) {      /* Steal args object. */
-        argsobj->setPrivate(newfp);
-        fp->setArgsObj(NULL);
+    newfp->argsobj = fp->argsobj;
+    if (fp->argsobj) {      /* Steal args object. */
+        fp->argsobj->setPrivate(newfp);
+        fp->argsobj = NULL;
     }
     newfp->script = fp->script;
     newfp->fun = fp->fun;
     newfp->thisv = fp->thisv;
     newfp->argc = fp->argc;
     newfp->argv = vp + 2;
     newfp->rval = fp->rval;
     newfp->annotation = NULL;
@@ -1212,29 +1194,29 @@ SendToGenerator(JSContext *cx, JSGenerat
         memcpy(vp, genVp, usedBefore * sizeof(Value));
         fp->flags &= ~JSFRAME_FLOATING_GENERATOR;
         fp->argv = vp + 2;
         gen->savedRegs.sp = fp->slots() + (gen->savedRegs.sp - genfp->slots());
         JS_ASSERT(uintN(gen->savedRegs.sp - fp->slots()) <= fp->script->nslots);
 
 #ifdef DEBUG
         JSObject *callobjBefore = fp->callobj;
-        JSObject *argsobjBefore = fp->argsObj();
+        JSObject *argsobjBefore = fp->argsobj;
 #endif
 
         /*
          * Repoint Call, Arguments, Block and With objects to the new live
          * frame. Call and Arguments are done directly because we have
          * pointers to them. Block and With objects are done indirectly through
          * 'liveFrame'. See js_LiveFrameToFloating comment in jsiter.h.
          */
         if (genfp->callobj)
             fp->callobj->setPrivate(fp);
-        if (genfp->argsObj())
-            fp->argsObj()->setPrivate(fp);
+        if (genfp->argsobj)
+            fp->argsobj->setPrivate(fp);
         gen->liveFrame = fp;
         (void)cx->enterGenerator(gen); /* OOM check above. */
 
         /* Officially push |fp|. |frame|'s destructor pops. */
         cx->stack().pushExecuteFrame(cx, frame, gen->savedRegs, NULL);
 
         /* Swap the enumerators stack for the generator's stack. */
         JSObject *enumerators = cx->enumerators;
@@ -1244,22 +1226,22 @@ SendToGenerator(JSContext *cx, JSGenerat
 
         /* Restore the original enumerators stack. */
         gen->enumerators = cx->enumerators;
         cx->enumerators = enumerators;
 
         /* Restore call/args/block objects. */
         cx->leaveGenerator(gen);
         gen->liveFrame = genfp;
-        if (fp->argsObj())
-            fp->argsObj()->setPrivate(genfp);
+        if (fp->argsobj)
+            fp->argsobj->setPrivate(genfp);
         if (fp->callobj)
             fp->callobj->setPrivate(genfp);
 
-        JS_ASSERT_IF(argsobjBefore, argsobjBefore == fp->argsObj());
+        JS_ASSERT_IF(argsobjBefore, argsobjBefore == fp->argsobj);
         JS_ASSERT_IF(callobjBefore, callobjBefore == fp->callobj);
 
         /* Copy and rebase stack frame/args/slots. Restore "floating" flag. */
         JS_ASSERT(uintN(gen->savedRegs.sp - fp->slots()) <= fp->script->nslots);
         uintN usedAfter = gen->savedRegs.sp - vp;
         memcpy(genVp, vp, usedAfter * sizeof(Value));
         genfp->flags |= JSFRAME_FLOATING_GENERATOR;
         genfp->argv = genVp + 2;
@@ -1315,17 +1297,17 @@ CloseGenerator(JSContext *cx, JSObject *
  * Common subroutine of generator_(next|send|throw|close) methods.
  */
 static JSBool
 generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc)
 {
     JSObject *obj;
     LeaveTrace(cx);
 
-    obj = ComputeThisObjectFromVp(cx, vp);
+    obj = ComputeThisFromVp(cx, vp);
     if (!InstanceOf(cx, obj, &js_GeneratorClass.base, vp + 2))
         return JS_FALSE;
 
     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
     if (!gen) {
         /* This happens when obj is the generator prototype. See bug 352885. */
         goto closed_generator;
     }
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -64,56 +64,96 @@ struct NativeIterator {
     void      *props_cursor;
     void      *props_end;
     uint32    *shapes_array;
     uint32    shapes_length;
     uint32    shapes_key;
     uintN     flags;
     JSObject  *next;
 
-    jsid currentId() const {
-        JS_ASSERT((flags & JSITER_FOREACH) == 0);
-        return *reinterpret_cast<jsid *>(props_cursor);
+    bool isKeyIter() const { return (flags & JSITER_FOREACH) == 0; }
+
+    inline jsid *beginKey() const {
+        JS_ASSERT(isKeyIter());
+        return (jsid *)props_array;
+    }
+
+    inline jsid *endKey() const {
+        JS_ASSERT(isKeyIter());
+        return (jsid *)props_end;
     }
 
-    void incIdCursor() {
-        JS_ASSERT((flags & JSITER_FOREACH) == 0);
+    size_t numKeys() const {
+        return endKey() - beginKey();
+    }
+
+    jsid *currentKey() const {
+        JS_ASSERT(isKeyIter());
+        return reinterpret_cast<jsid *>(props_cursor);
+    }
+
+    void incKeyCursor() {
+        JS_ASSERT(isKeyIter());
         props_cursor = reinterpret_cast<jsid *>(props_cursor) + 1;
     }
 
-    const js::Value &currentValue() const {
-        JS_ASSERT((flags & JSITER_FOREACH) != 0);
-        return *reinterpret_cast<js::Value *>(props_cursor);
+    inline js::Value *beginValue() const {
+        JS_ASSERT(!isKeyIter());
+        return (js::Value *)props_array;
+    }
+
+    inline js::Value *endValue() const {
+        JS_ASSERT(!isKeyIter());
+        return (js::Value *)props_end;
+    }
+
+    size_t numValues() const {
+        return endValue() - beginValue();
+    }
+
+    js::Value *currentValue() const {
+        JS_ASSERT(!isKeyIter());
+        return reinterpret_cast<js::Value *>(props_cursor);
     }
 
     void incValueCursor() {
-        JS_ASSERT((flags & JSITER_FOREACH) != 0);
+        JS_ASSERT(!isKeyIter());
         props_cursor = reinterpret_cast<js::Value *>(props_cursor) + 1;
     }
 
     static NativeIterator *allocateKeyIterator(JSContext *cx, uint32 slength,
-                                               const jsid *parray, uint32 plength);
+                                               const js::AutoIdVector &props);
     static NativeIterator *allocateValueIterator(JSContext *cx, uint32 slength,
-                                                 const js::Value *parray, uint32 plength);
-    void init(JSObject *obj, uintN flags, uint32 *sarray, uint32 slength, uint32 key);
+                                                 const js::AutoValueVector &props);
+    void init(JSObject *obj, uintN flags, const uint32 *sarray, uint32 slength, uint32 key);
 
     void mark(JSTracer *trc);
 };
 
 bool
-VectorToIdArray(JSContext *cx, js::AutoValueVector &props, JSIdArray **idap);
+VectorToIdArray(JSContext *cx, js::AutoIdVector &props, JSIdArray **idap);
 
 bool
-GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, js::AutoValueVector &props);
+GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, js::AutoIdVector &props);
 
 bool
 GetIterator(JSContext *cx, JSObject *obj, uintN flags, js::Value *vp);
 
 bool
-IdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, js::AutoIdVector &props, js::Value *vp);
+VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, js::AutoIdVector &props, js::Value *vp);
+
+bool
+VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, js::AutoValueVector &props, js::Value *vp);
+
+/*
+ * Creates either a key or value iterator, depending on flags. For a value
+ * iterator, performs value-lookup to convert the given list of jsids.
+ */
+bool
+EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, js::AutoIdVector &props, js::Value *vp);
 
 /*
  * Convert the value stored in *vp to its iteration object. The flags should
  * contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating
  * for-in semantics are required, and when the caller can guarantee that the
  * iterator will never be exposed to scripts.
  */
 extern JS_FRIEND_API(JSBool)
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -510,17 +510,17 @@ num_toLocaleString(JSContext *cx, uintN 
 
 static JSBool
 num_valueOf(JSContext *cx, uintN argc, Value *vp)
 {
     if (vp[1].isNumber()) {
         *vp = vp[1];
         return JS_TRUE;
     }
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!InstanceOf(cx, obj, &js_NumberClass, vp + 2))
         return JS_FALSE;
     *vp = obj->getPrimitiveThis();
     return JS_TRUE;
 }
 
 
 #define MAX_PRECISION 100
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -136,17 +136,17 @@ Class js_ObjectClass = {
 
 static JSBool
 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
 
 static JSBool
 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
 
 static JSPropertySpec object_props[] = {
-    {js_proto_str, 0, JSPROP_PERMANENT|JSPROP_SHARED, obj_getProto, obj_setProto},
+    {js_proto_str, 0, JSPROP_PERMANENT|JSPROP_SHARED, Jsvalify(obj_getProto), Jsvalify(obj_setProto)},
     {0,0,0,0,0}
 };
 
 static JSBool
 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     /* Let CheckAccess get the slot's value, based on the access mode. */
     uintN attrs;
@@ -162,17 +162,17 @@ obj_setProto(JSContext *cx, JSObject *ob
 
     JSObject *pobj = vp->asObjectOrNull();
     if (pobj) {
         /*
          * Innerize pobj here to avoid sticking unwanted properties on the
          * outer object. This ensures that any with statements only grant
          * access to the inner object.
          */
-        Innerize(cx, &pobj);
+        OBJ_TO_INNER_OBJECT(cx, pobj);
         if (!pobj)
             return JS_FALSE;
     }
 
     uintN attrs;
     id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
     if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs))
         return JS_FALSE;
@@ -491,17 +491,17 @@ obj_toSource(JSContext *cx, uintN argc, 
     JS_CHECK_RECURSION(cx, return JS_FALSE);
 
     Value localroot[4];
     PodArrayZero(localroot);
     AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(localroot), localroot);
 
     /* If outermost, we need parentheses to be an expression, not a block. */
     outermost = (cx->sharpObjectMap.depth == 0);
-    obj = ComputeThisObjectFromVp(cx, vp);
+    obj = ComputeThisFromVp(cx, vp);
     if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
         ok = JS_FALSE;
         goto out;
     }
     if (IS_SHARP(he)) {
         /*
          * We didn't enter -- obj is already "sharp", meaning we've visited it
          * already in our depth first search, and therefore chars contains a
@@ -848,46 +848,46 @@ obj_toStringHelper(JSContext *cx, JSObje
     return str;
 }
 
 }
 
 static JSBool
 obj_toString(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
         return false;
 
     JSString *str = js::obj_toStringHelper(cx, obj);
     if (!str)
         return false;
 
-    *vp = STRING_TO_JSVAL(str);
+    vp->setString(str);
     return true;
 }
 
 static JSBool
 obj_toLocaleString(JSContext *cx, uintN argc, Value *vp)
 {
-    if (!ComputeThisFromVpInPlace(cx, vp))
+    if (!ComputeThisFromVp(cx, vp))
         return JS_FALSE;
 
     JSString *str = js_ValueToString(cx, vp[1]);
     if (!str)
         return JS_FALSE;
 
     vp->setString(str);
     return JS_TRUE;
 }
 
 static JSBool
 obj_valueOf(JSContext *cx, uintN argc, Value *vp)
 {
-    if (!ComputeThisFromVpInPlace(cx, vp))
+    if (!ComputeThisFromVp(cx, vp))
         return JS_FALSE;
     *vp = vp[1];
     return JS_TRUE;
 }
 
 /*
  * Check if CSP allows new Function() or eval() to run in the current
  * principals.
@@ -939,17 +939,17 @@ js_CheckScopeChainValidity(JSContext *cx
 {
     Class *clasp;
     JSExtendedClass *xclasp;
     JSObject *inner;
 
     if (!scopeobj)
         goto bad;
 
-    Innerize(cx, &scopeobj);
+    OBJ_TO_INNER_OBJECT(cx, scopeobj);
     if (!scopeobj)
         return NULL;
 
     inner = scopeobj;
 
     /* XXX This is an awful gross hack. */
     while (scopeobj) {
         clasp = scopeobj->getClass();
@@ -1045,17 +1045,17 @@ obj_eval(JSContext *cx, uintN argc, Valu
      * checks we do below. However, the control flow below is confusing, so we
      * double check. There are two cases:
      * - Direct call: This object is never used. So unwrapping can't hurt.
      * - Indirect call: If this object isn't already the scope chain (which
      *   we're guaranteed to be allowed to access) then we do a security
      *   check.
      */
     Value *argv = JS_ARGV(cx, vp);
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
         return JS_FALSE;
     obj = obj->wrappedObject(cx);
 
     /*
      * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
      * calls that attempt to use a non-global object as the "with" object in
      * the former indirect case.
@@ -1077,19 +1077,19 @@ obj_eval(JSContext *cx, uintN argc, Valu
     if (!argv[0].isString()) {
         *vp = argv[0];
         return JS_TRUE;
     }
 
     /* Accept an optional trailing argument that overrides the scope object. */
     JSObject *scopeobj = NULL;
     if (argc >= 2) {
-        if (!js_ValueToObjectOrNull(cx, argv[1], &argv[1]))
+        if (!js_ValueToObjectOrNull(cx, argv[1], &scopeobj))
             return JS_FALSE;
-        scopeobj = argv[1].asObjectOrNull();
+        argv[1].setObjectOrNull(scopeobj);
         JSObject *obj = scopeobj;
         while (obj) {
             if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
                 return false;
             JSObject *parent = obj->getParent();
             if (!obj->isNative() ||
                 (!parent && !(obj->getClass()->flags & JSCLASS_IS_GLOBAL))) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
@@ -1124,17 +1124,17 @@ obj_eval(JSContext *cx, uintN argc, Valu
          * If we see an indirect call, then run eval in the global scope. We do
          * this so the compiler can make assumptions about what bindings may or
          * may not exist in the current frame if it doesn't see 'eval'.
          */
         if (indirectCall) {
             /* Pretend that we're top level. */
             staticLevel = 0;
 
-            Innerize(cx, &obj);
+            OBJ_TO_INNER_OBJECT(cx, obj);
             if (!obj)
                 return JS_FALSE;
 
             if (!js_CheckPrincipalsAccess(cx, obj,
                                           JS_StackFramePrincipals(cx, caller),
                                           cx->runtime->atomState.evalAtom)) {
                 return JS_FALSE;
             }
@@ -1150,17 +1150,17 @@ obj_eval(JSContext *cx, uintN argc, Valu
              * the C API) must use the two parameter form.
              */
             JS_ASSERT_IF(caller->argv, caller->callobj);
             scopeobj = callerScopeChain;
         }
 #endif
     } else {
         scopeobj = scopeobj->wrappedObject(cx);
-        Innerize(cx, &scopeobj);
+        OBJ_TO_INNER_OBJECT(cx, scopeobj);
         if (!scopeobj)
             return JS_FALSE;
 
         if (!js_CheckPrincipalsAccess(cx, scopeobj,
                                       JS_StackFramePrincipals(cx, caller),
                                       cx->runtime->atomState.evalAtom))
             return JS_FALSE;
 
@@ -1379,65 +1379,73 @@ obj_watch(JSContext *cx, uintN argc, Val
     if (!callable)
         return JS_FALSE;
 
     /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
     jsid propid;
     if (!ValueToId(cx, vp[2], &propid))
         return JS_FALSE;
 
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
-    if (!obj || !CheckAccess(cx, obj, propid, JSACC_WATCH, &value, &attrs))
+    JSObject *obj = ComputeThisFromVp(cx, vp);
+    Value tmp;
+    uintN attrs;
+    if (!obj || !CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs))
         return JS_FALSE;
 
-    *vp = JSVAL_VOID;
+    vp->setUndefined();
 
     if (attrs & JSPROP_READONLY)
         return JS_TRUE;
     if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
         return JS_FALSE;
     return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable);
 }
 
 static JSBool
 obj_unwatch(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
         return JS_FALSE;
-    *vp = JSVAL_VOID;
-    return JS_ClearWatchPoint(cx, obj, argc != 0 ? vp[2] : JSVAL_VOID,
-                              NULL, NULL);
+    vp->setUndefined();
+    jsid id;
+    if (argc != 0) {
+        if (!ValueToId(cx, vp[2], &id))
+            return JS_FALSE;
+    } else {
+        id = JSID_VOID;
+    }
+    return JS_ClearWatchPoint(cx, obj, id, NULL, NULL);
 }
 
 #endif /* JS_HAS_OBJ_WATCHPOINT */
 
 /*
  * Prototype and property query methods, to complement the 'in' and
  * 'instanceof' operators.
  */
 
 /* Proposed ECMA 15.2.4.5. */
 static JSBool
 obj_hasOwnProperty(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     return obj &&
            js_HasOwnPropertyHelper(cx, obj->map->ops->lookupProperty, argc, vp);
 }
 
 JSBool
 js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
                         Value *vp)
 {
     jsid id;
     if (!ValueToId(cx, argc != 0 ? vp[2] : Value(UndefinedTag()), &id))
         return JS_FALSE;
 
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     JSObject *obj2;
     JSProperty *prop;
     if (!obj)
         return false;
     if (obj->isProxy()) {
         bool has;
         if (!JSProxy::hasOwn(cx, obj, id, &has))
             return false;
@@ -1507,33 +1515,33 @@ js_HasOwnProperty(JSContext *cx, JSLooku
     }
     return true;
 }
 
 /* Proposed ECMA 15.2.4.6. */
 static JSBool
 obj_isPrototypeOf(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
         return JS_FALSE;
     const Value &v = argc != 0 ? vp[2] : Value(UndefinedTag());
     vp->setBoolean(js_IsDelegate(cx, obj, v));
     return JS_TRUE;
 }
 
 /* Proposed ECMA 15.2.4.7. */
 static JSBool
 obj_propertyIsEnumerable(JSContext *cx, uintN argc, Value *vp)
 {
     jsid id;
     if (!ValueToId(cx, argc != 0 ? vp[2] : Value(UndefinedTag()), &id))
         return JS_FALSE;
 
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
 }
 
 JSBool
 js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     JSObject *pobj;
     JSProperty *prop;
@@ -1592,28 +1600,29 @@ js_obj_defineGetter(JSContext *cx, uintN
                              js_getter_str);
         return JS_FALSE;
     }
     PropertyOp getter = CastAsPropertyOp(&vp[3].asObject());
 
     jsid id;
     if (!ValueToId(cx, vp[2], &id))
         return JS_FALSE;
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
         return JS_FALSE;
     /*
      * Getters and setters are just like watchpoints from an access
      * control point of view.
      */
+    Value junk;
+    uintN attrs;
     if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
         return JS_FALSE;
-    *vp = JSVAL_VOID;
-    return obj->defineProperty(cx, id, JSVAL_VOID,
-                               CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), JS_PropertyStub,
+    *vp = UndefinedTag();
+    return obj->defineProperty(cx, id, UndefinedTag(), getter, PropertyStub,
                                JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED);
 }
 
 JS_FRIEND_API(JSBool)
 js_obj_defineSetter(JSContext *cx, uintN argc, Value *vp)
 {
     if (argc <= 1 || !js_IsCallable(vp[3])) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
@@ -1621,38 +1630,39 @@ js_obj_defineSetter(JSContext *cx, uintN
                              js_setter_str);
         return JS_FALSE;
     }
     PropertyOp setter = CastAsPropertyOp(&vp[3].asObject());
 
     jsid id;
     if (!ValueToId(cx, vp[2], &id))
         return JS_FALSE;
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
         return JS_FALSE;
     /*
      * Getters and setters are just like watchpoints from an access
      * control point of view.
      */
+    Value junk;
+    uintN attrs;
     if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
         return JS_FALSE;
-    *vp = JSVAL_VOID;
-    return obj->defineProperty(cx, id, JSVAL_VOID,
-                               JS_PropertyStub, CastAsPropertyOp(JSVAL_TO_OBJECT(fval)),
+    *vp = UndefinedTag();
+    return obj->defineProperty(cx, id, UndefinedTag(), PropertyStub, setter,
                                JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED);
 }
 
 static JSBool
 obj_lookupGetter(JSContext *cx, uintN argc, Value *vp)
 {
     jsid id;
     if (!ValueToId(cx, argc != 0 ? vp[2] : Value(UndefinedTag()), &id))
         return JS_FALSE;
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     JSObject *pobj;
     JSProperty *prop;
     if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
         return JS_FALSE;
     vp->setUndefined();
     if (prop) {
         if (pobj->isNative()) {
             JSScopeProperty *sprop = (JSScopeProperty *) prop;
@@ -1665,17 +1675,17 @@ obj_lookupGetter(JSContext *cx, uintN ar
 }
 
 static JSBool
 obj_lookupSetter(JSContext *cx, uintN argc, Value *vp)
 {
     jsid id;
     if (!ValueToId(cx, argc != 0 ? vp[2] : Value(UndefinedTag()), &id))
         return JS_FALSE;
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     JSObject *pobj;
     JSProperty *prop;
     if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
         return JS_FALSE;
     vp->setUndefined();
     if (prop) {
         if (pobj->isNative()) {
             JSScopeProperty *sprop = (JSScopeProperty *) prop;
@@ -1701,17 +1711,18 @@ obj_getPrototypeOf(JSContext *cx, uintN 
         if (!bytes)
             return JS_FALSE;
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
         JS_free(cx, bytes);
         return JS_FALSE;
     }
 
-    JSObject &obj = vp[2].asObject();
+    JSObject *obj = &vp[2].asObject();
+    uintN attrs;
     return CheckAccess(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
                        JSACC_PROTO, vp, &attrs);
 }
 
 extern JSBool
 js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs,
                                const Value &getter, const Value &setter,
                                const Value &value, Value *vp)
@@ -2447,19 +2458,20 @@ obj_defineProperties(JSContext* cx, uint
     }
 
     *vp = vp[2];
     if (!vp->isObject()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
         return false;
     }
 
-    if (!js_ValueToNonNullObject(cx, vp[3], &vp[3]))
+    JSObject* props = js_ValueToNonNullObject(cx, vp[3]);
+    if (!props)
         return false;
-    JSObject *props = &vp[3].asObject();
+    vp[3].setObject(*props);
 
     JSObject *obj = &vp->asObject();
 
     return DefineProperties(cx, obj, props);
 }
 
 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
 static JSBool
@@ -2481,18 +2493,18 @@ obj_create(JSContext *cx, uintN argc, Va
         JS_free(cx, bytes);
         return JS_FALSE;
     }
 
     /*
      * Use the callee's global as the parent of the new object to avoid dynamic
      * scoping (i.e., using the caller's global).
      */
-    JSObject *obj = NewObjectWithGivenProto(cx, &js_ObjectClass, JSVAL_TO_OBJECT(v),
-                                            JSVAL_TO_OBJECT(*vp)->getGlobal());
+    JSObject *obj = NewObjectWithGivenProto(cx, &js_ObjectClass, v.asObjectOrNull(),
+                                            vp->asObject().getGlobal());
     if (!obj)
         return JS_FALSE;
     vp->setObject(*obj); /* Root and prepare for eventual return. */
 
     /* 15.2.3.5 step 4. */
     if (argc > 1 && !vp[3].isUndefined()) {
         if (vp[3].isPrimitive()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
@@ -2571,45 +2583,45 @@ static JSFunctionSpec object_static_meth
 JSBool
 js_Object(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
     if (argc == 0) {
         /* Trigger logic below to construct a blank object. */
         obj = NULL;
     } else {
         /* If argv[0] is null or undefined, obj comes back null. */
-        if (!js_ValueToObject(cx, argv[0], &obj))
+        if (!js_ValueToObjectOrNull(cx, argv[0], &obj))
             return JS_FALSE;
     }
     if (!obj) {
-        JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
+        JS_ASSERT(!argc || argv[0].isNull() || argv[0].isUndefined());
         if (JS_IsConstructing(cx))
             return JS_TRUE;
         obj = NewBuiltinClassInstance(cx, &js_ObjectClass);
         if (!obj)
             return JS_FALSE;
     }
-    *rval = OBJECT_TO_JSVAL(obj);
+    rval->setObject(*obj);
     return JS_TRUE;
 }
 
 #ifdef JS_TRACER
 
 JSObject*
 js_NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto,
                            const Value &privateSlotValue)
 {
     JS_ASSERT(!clasp->getObjectOps);
     JS_ASSERT(proto->map->ops == &js_ObjectOps);
 
     JSObject* obj = js_NewGCObject(cx);
     if (!obj)
         return NULL;
 
-    obj->initSharingEmptyScope(clasp, ObjectTag(*proto), ObjectTag(*proto->getParent()), privateSlotValue);
+    obj->initSharingEmptyScope(clasp, proto, proto->getParent(), privateSlotValue);
     return obj;
 }
 
 JSObject* FASTCALL
 js_Object_tn(JSContext* cx, JSObject* proto)
 {
     JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE));
     return js_NewObjectWithClassProto(cx, &js_ObjectClass, proto, UndefinedTag());
@@ -2659,29 +2671,29 @@ js_NewInstance(JSContext *cx, Class *cla
 #endif
     if (scope->isSharedEmpty()) {
         scope = js_GetMutableScope(cx, ctor);
         if (!scope)
             return NULL;
     }
 
     JSScopeProperty *sprop = scope->lookup(ATOM_TO_JSID(atom));
-    Value pval = sprop ? ctor->getSlot(sprop->slot) : JSWhyMagic(WRONG);
+    Value pval = sprop ? ctor->getSlot(sprop->slot) : Value(JS_GENERIC_MAGIC);
 
     JSObject *parent = ctor->getParent();
     JSObject *proto;
     if (pval.isObject()) {
         /* An object in ctor.prototype, let's use it as the new instance's proto. */
         proto = &pval.asObject();
     } else {
         /* A hole or a primitive: either way, we need to get Object.prototype. */
         if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
             return NULL;
 
-        if (pval == JSVAL_HOLE) {
+        if (pval.isMagic(JS_GENERIC_MAGIC)) {
             /*
              * No ctor.prototype was set, so we inline-expand and optimize
              * fun_resolve's prototype creation code.
              */
             proto = NewNativeClassInstance(cx, clasp, proto, parent);
             if (!proto)
                 return NULL;
             if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT))
@@ -2923,17 +2935,17 @@ JS_REQUIRES_STACK JSObject *
 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
 {
     JSObject *obj;
 
     obj = js_NewGCObject(cx);
     if (!obj)
         return NULL;
     obj->init(&js_WithClass, proto, parent,
-              reinterpret_cast<jsval>(js_FloatingFrameIfGenerator(cx, cx->fp)));
+              PrivateTag(js_FloatingFrameIfGenerator(cx, cx->fp)));
     OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
     obj->map = cx->runtime->emptyWithScope->hold();
 
     AutoObjectRooter tvr(cx, obj);
     JSObject *thisp = proto->thisObject(cx);
     if (!thisp)
         return NULL;
     obj->setWithThis(thisp);
@@ -2946,17 +2958,17 @@ js_NewBlockObject(JSContext *cx)
 {
     /*
      * Null obj's proto slot so that Object.prototype.* does not pollute block
      * scopes and to give the block object its own scope.
      */
     JSObject *blockObj = js_NewGCObject(cx);
     if (!blockObj)
         return NULL;
-    blockObj->init(&js_BlockClass, NULL, NULL, JSVAL_NULL);
+    blockObj->init(&js_BlockClass, NULL, NULL, NullTag());
     blockObj->map = cx->runtime->emptyBlockScope->hold();
     return blockObj;
 }
 
 JSObject *
 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
 {
     JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto));
@@ -2964,17 +2976,17 @@ js_CloneBlockObject(JSContext *cx, JSObj
 
     JSObject *clone = js_NewGCObject(cx);
     if (!clone)
         return NULL;
 
     Value privateValue = PrivateTag(js_FloatingFrameIfGenerator(cx, fp));
 
     /* The caller sets parent on its own. */
-    clone->init(&js_BlockClass, ObjectTag(*proto), NullTag(), privateValue);
+    clone->init(&js_BlockClass, proto, NULL, privateValue);
     clone->fslots[JSSLOT_BLOCK_DEPTH] = proto->fslots[JSSLOT_BLOCK_DEPTH];
 
     JS_ASSERT(cx->runtime->emptyBlockScope->freeslot == JSSLOT_BLOCK_DEPTH + 1);
     clone->map = cx->runtime->emptyBlockScope->hold();
     JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone));
 
     if (!js_EnsureReservedSlots(cx, clone, OBJ_BLOCK_COUNT(cx, proto)))
         return NULL;
@@ -2983,17 +2995,17 @@ js_CloneBlockObject(JSContext *cx, JSObj
 
 JS_REQUIRES_STACK JSBool
 js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
 {
     /* Blocks have one fixed slot available for the first local.*/
     JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2);
 
     JSStackFrame *const fp = cx->fp;
-    JSObject *obj = fp->scopeChainObj();
+    JSObject *obj = fp->scopeChain;
     JS_ASSERT(obj->getClass() == &js_BlockClass);
     JS_ASSERT(obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp));
     JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
 
     /*
      * Block objects should never be exposed to scripts. Thus the clone should
      * not own the property map and rather always share it with the prototype
      * object. This allows us to skip updating obj->scope()->freeslot after
@@ -3017,17 +3029,17 @@ js_PutBlockObject(JSContext *cx, JSBool 
     obj->fslots[JSSLOT_BLOCK_DEPTH + 1] = fp->slots()[depth];
     if (normalUnwind && count > 1) {
         --count;
         memcpy(obj->dslots, fp->slots() + depth + 1, count * sizeof(Value));
     }
 
     /* We must clear the private slot even with errors. */
     obj->setPrivate(NULL);
-    fp->setScopeChainObj(obj->getParent());
+    fp->scopeChain = obj->getParent();
     return normalUnwind;
 }
 
 static JSBool
 block_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     /*
      * Block objects are never exposed to script, and the engine handles them
@@ -3202,17 +3214,17 @@ js_XDRBlockObject(JSXDRState *xdr, JSObj
          * If there's a parent id, then get the parent out of our script's
          * object array. We know that we XDR block object in outer-to-inner
          * order, which means that getting the parent now will work.
          */
         if (parentId == NO_PARENT_INDEX)
             parent = NULL;
         else
             parent = xdr->script->getObject(parentId);
-        obj->setParent(ObjectOrNullTag(parent));
+        obj->setParent(parent);
     }
 
     AutoObjectRooter tvr(cx, obj);
 
     if (!JS_XDRUint32(xdr, &tmp))
         return false;
 
     if (xdr->mode == JSXDR_DECODE) {
@@ -3387,39 +3399,39 @@ js_InitClass(JSContext *cx, JSObject *ob
      * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
      *    &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
      *
      * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
      * be &js_FunctionClass (we could break compatibility easily). But fixing
      * (3) is not enough without addressing the bootstrapping dependency on (1)
      * and (2).
      */
-    proto = NewObject(cx, clasp, parent_proto, obj);
+    JSObject *proto = NewObject(cx, clasp, parent_proto, obj);
     if (!proto)
         return NULL;
 
     /* After this point, control must exit via label bad or out. */
-    AutoValueRooter tvr(cx, ObjectTag(*proto));
+    AutoObjectRooter tvr(cx, proto);
 
     JSObject *ctor;
     if (!constructor) {
         /*
          * Lacking a constructor, name the prototype (e.g., Math) unless this
          * class (a) is anonymous, i.e. for internal use only; (b) the class
          * of obj (the global object) is has a reserved slot indexed by key;
          * and (c) key is not the null key.
          */
         if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) ||
             !(obj->getClass()->flags & JSCLASS_IS_GLOBAL) ||
             key == JSProto_Null)
         {
             uint32 attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
                            ? JSPROP_READONLY | JSPROP_PERMANENT
                            : 0;
-            if (!DefineStandardSlot(cx, obj, key, atom, tvr.value(), attrs, named))
+            if (!DefineStandardSlot(cx, obj, key, atom, ObjectTag(*proto), attrs, named))
                 goto bad;
         }
 
         ctor = proto;
     } else {
         fun = js_NewFunction(cx, NULL, constructor, nargs, 0, obj, atom);
         if (!fun)
             goto bad;
@@ -3453,17 +3465,17 @@ js_InitClass(JSContext *cx, JSObject *ob
         /* Connect constructor and prototype by named properties. */
         if (!js_SetClassPrototype(cx, ctor, proto,
                                   JSPROP_READONLY | JSPROP_PERMANENT)) {
             goto bad;
         }
 
         /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
         if (ctor->getClass() == clasp)
-            ctor->setProto(ObjectOrNullTag(proto));
+            ctor->setProto(proto);
     }
 
     /* Add properties and methods to the prototype and the constructor. */
     if ((ps && !JS_DefineProperties(cx, proto, ps)) ||
         (fs && !JS_DefineFunctions(cx, proto, fs)) ||
         (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) ||
         (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) {
         goto bad;
@@ -3752,33 +3764,33 @@ js_FindClassObject(JSContext *cx, JSObje
 
     /*
      * Find the global object. Use cx->fp directly to avoid falling off
      * trace; all JIT-elided stack frames have the same global object as
      * cx->fp.
      */
     VOUCH_DOES_NOT_REQUIRE_STACK();
     if (!start && (fp = cx->fp) != NULL)
-        start = fp->scopeChainObj();
+        start = fp->scopeChain;
 
     if (start) {
         /* Find the topmost object in the scope chain. */
         do {
             obj = start;
             start = obj->getParent();
         } while (start);
     } else {
         obj = cx->globalObject;
         if (!obj) {
             vp->setUndefined();
             return JS_TRUE;
         }
     }
 
-    Innerize(cx, &obj);
+    OBJ_TO_INNER_OBJECT(cx, obj);
     if (!obj)
         return JS_FALSE;
 
     if (protoKey != JSProto_Null) {
         JS_ASSERT(JSProto_Null < protoKey);
         JS_ASSERT(protoKey < JSProto_LIMIT);
         if (!js_GetClassObject(cx, obj, protoKey, &cobj))
             return JS_FALSE;
@@ -4471,17 +4483,17 @@ js_FindPropertyHelper(JSContext *cx, jsi
                       JSObject **objp, JSObject **pobjp, JSProperty **propp)
 {
     JSObject *scopeChain, *obj, *parent, *pobj;
     PropertyCacheEntry *entry;
     int scopeIndex, protoIndex;
     JSProperty *prop;
 
     JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx));
-    scopeChain = js_GetTopStackFrame(cx)->scopeChainObj();
+    scopeChain = js_GetTopStackFrame(cx)->scopeChain;
 
     /* Scan entries on the scope chain that we can cache across. */
     entry = JS_NO_PROP_CACHE_FILL;
     obj = scopeChain;
     parent = obj->getParent();
     for (scopeIndex = 0;
          parent
          ? js_IsCacheableNonGlobalScope(obj)
@@ -4775,17 +4787,17 @@ js_GetPropertyHelper(JSContext *cx, JSOb
     aobj = js_GetProtoIfDenseArray(obj);
     protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, cx->resolveFlags,
                                             &obj2, &prop);
     if (protoIndex < 0)
         return JS_FALSE;
     if (!prop) {
         vp->setUndefined();
 
-        if (!callJSPropertyOp(cx, obj->getClass()->getProperty, obj, ID_TO_VALUE(id), vp))
+        if (!callJSPropertyOp(cx, obj->getClass()->getProperty, obj, id, vp))
             return JS_FALSE;
 
         PCMETER(getHow & JSGET_CACHE_RESULT && JS_PROPERTY_CACHE(cx).nofills++);
 
         /*
          * Give a strict warning if foo.bar is evaluated by a script for an
          * object foo with no property named 'bar'.
          */
@@ -5237,17 +5249,17 @@ js_DeleteProperty(JSContext *cx, JSObjec
                 return JS_TRUE;
         }
 
         /*
          * If no property, or the property comes unshared or impermanent from
          * a prototype, call the class's delProperty hook, passing rval as the
          * result parameter.
          */
-        return callJSPropertyOp(cx, obj->getClass()->delProperty, obj, ID_TO_VALUE(id), rval);
+        return callJSPropertyOp(cx, obj->getClass()->delProperty, obj, id, rval);
     }
 
     sprop = (JSScopeProperty *)prop;
     if (!sprop->configurable()) {
         JS_UNLOCK_OBJ(cx, obj);
         rval->setBoolean(false);
         return JS_TRUE;
     }
@@ -5397,24 +5409,24 @@ CheckAccess(JSContext *cx, JSObject *obj
     while (JS_UNLIKELY(obj->getClass() == &js_WithClass))
         obj = obj->getProto();
 
     writing = (mode & JSACC_WRITE) != 0;
     switch (mode & JSACC_TYPEMASK) {
       case JSACC_PROTO:
         pobj = obj;
         if (!writing)
-            *vp = obj->getProtoValue();
+            vp->setObjectOrNull(obj->getProto());
         *attrsp = JSPROP_PERMANENT;
         break;
 
       case JSACC_PARENT:
         JS_ASSERT(!writing);
         pobj = obj;
-        *vp = obj->getParentValue();
+        vp->setObject(*obj->getParent());
         *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
         break;
 
       default:
         if (!obj->lookupProperty(cx, id, &pobj, &prop))
             return JS_FALSE;
         if (!prop) {
             if (!writing)
@@ -5535,20 +5547,20 @@ GetCurrentExecutionContext(JSContext *cx
 
 JSBool
 js_Call(JSContext *cx, uintN argc, Value *vp)
 {
     JSStackFrame *fp = cx->fp;
     JSObject *obj = fp->getThisObject(cx);
     if (!obj)
         return false;
-    JS_ASSERT(OBJECT_TO_JSVAL(obj) == fp->thisv);
-
-    JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
-    JSClass *clasp = callee->getClass();
+    JS_ASSERT(Value(ObjectTag(*obj)) == fp->thisv);
+
+    JSObject *callee = &JS_CALLEE(cx, vp).asObject();
+    Class *clasp = callee->getClass();
     if (!clasp->call) {
 #ifdef NARCISSUS
         JSObject *args;
         Value fval, nargv[3];
         JSBool ok;
 
         if (!callee->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.__call__Atom), &fval))
             return JS_FALSE;
@@ -5654,29 +5666,29 @@ js_IsDelegate(JSContext *cx, JSObject *o
         if (obj2 == obj)
             return true;
     }
     return false;
 }
 
 bool
 js::FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop,
-                       JSClass *clasp)
-{
-    jsval v;
+                       Class *clasp)
+{
+    Value v;
     if (!js_FindClassObject(cx, scope, protoKey, &v, clasp))
         return false;
 
-    if (VALUE_IS_FUNCTION(cx, v)) {
-        JSObject *ctor = JSVAL_TO_OBJECT(v);
+    if (IsFunctionObject(v)) {
+        JSObject *ctor = &v.asObject();
         if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &v))
             return false;
     }
 
-    *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
+    *protop = v.isObject() ? &v.asObject() : NULL;
     return true;
 }
 
 /*
  * The first part of this function has been hand-expanded and optimized into
  * NewBuiltinClassInstance in jsobjinlines.h.
  */
 JSBool
@@ -5685,17 +5697,17 @@ js_GetClassPrototype(JSContext *cx, JSOb
 {
     VOUCH_DOES_NOT_REQUIRE_STACK();
     JS_ASSERT(JSProto_Null <= protoKey);
     JS_ASSERT(protoKey < JSProto_LIMIT);
 
     if (protoKey != JSProto_Null) {
         if (!scope) {
             if (cx->fp)
-                scope = cx->fp->scopeChainObj();
+                scope = cx->fp->scopeChain;
             if (!scope) {
                 scope = cx->globalObject;
                 if (!scope) {
                     *protop = NULL;
                     return true;
                 }
             }
         }
@@ -5727,24 +5739,26 @@ js_GetClassPrototype(JSContext *cx, JSOb
  * accesses with the following getter/setter pair, which we use when defining
  * 'constructor' in f.prototype for all function objects f.
  */
 static JSBool
 CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     JSAtom *atom = cx->runtime->atomState.constructorAtom;
     JS_ASSERT(id == ATOM_TO_JSID(atom));
+    uintN attrs;
     return CheckAccess(cx, obj, ATOM_TO_JSID(atom), JSACC_READ, vp, &attrs);
 }
 
 static JSBool
 CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     JSAtom *atom = cx->runtime->atomState.constructorAtom;
     JS_ASSERT(id == ATOM_TO_JSID(atom));
+    uintN attrs;
     return CheckAccess(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE, vp, &attrs);
 }
 
 JSBool
 js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, uintN attrs)
 {
     /*
      * Use the given attributes for the prototype property of the constructor,
@@ -5774,50 +5788,54 @@ js_PrimitiveToObject(JSContext *cx, Valu
     Class *clasp;
     if (v.isNumber())
         clasp = &js_NumberClass;
     else if (v.isString())
         clasp = &js_StringClass;
     else
         clasp = &js_BooleanClass;
 
-    obj = NewBuiltinClassInstance(cx, clasp);
+    JSObject *obj = NewBuiltinClassInstance(cx, clasp);
     if (!obj)
         return JS_FALSE;
 
     obj->setPrimitiveThis(v);
     vp->setObject(*obj);
     return JS_TRUE;
 }
 
 JSBool
-js_ValueToObjectOrNull(JSContext *cx, const Value &v, Value *vp)
-{
+js_ValueToObjectOrNull(JSContext *cx, const Value &v, JSObject **objp)
+{
+    JSObject *obj;
+
     if (v.isObjectOrNull()) {
-        *vp = v;
-        return JS_TRUE;
-    }
-    if (v.isUndefined()) {
-        vp->setNull();
-        return JS_TRUE;
-    }
-    *vp = v;
-    return js_PrimitiveToObject(cx, vp);
-}
-
-JSBool
-js_ValueToNonNullObject(JSContext *cx, const Value &v, js::Value *vp)
-{
-    if (!js_ValueToObjectOrNull(cx, v, vp))
-        return JS_FALSE;
-    if (vp->isNull()) {
+        obj = v.asObjectOrNull();
+    } else if (v.isUndefined()) {
+        obj = NULL;
+    } else {
+        Value tmp = v;
+        if (!js_PrimitiveToObject(cx, &tmp))
+            return JS_FALSE;
+        obj = &tmp.asObject();
+    }
+    *objp = obj;
+    return JS_TRUE;
+}
+
+JSObject *
+js_ValueToNonNullObject(JSContext *cx, const Value &v)
+{
+    JSObject *obj;
+
+    if (!js_ValueToObjectOrNull(cx, v, &obj))
+        return NULL;
+    if (!obj)
         js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, v, NULL);
-        return JS_FALSE;
-    }
-    return JS_TRUE;
+    return obj;
 }
 
 JSBool
 js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, Value *rval)
 {
     Value argv[1];
 
     argv[0].setString(ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]));
@@ -6196,37 +6214,37 @@ js_ReportGetterOnlyAssignment(JSContext 
                                         JSMSG_GETTER_ONLY);
 }
 
 JSCompartment *
 JSObject::getCompartment(JSContext *cx)
 {
     JSObject *obj = getGlobal();
 
-    JSClass *clasp = obj->getClass();
+    Class *clasp = obj->getClass();
     if (!(clasp->flags & JSCLASS_IS_GLOBAL)) {
         // The magic AnyName object is runtime-wide.
         if (clasp == &js_AnyNameClass)
             return cx->runtime->defaultCompartment;
 
         // The magic function namespace object is runtime-wide.
         if (clasp == &js_NamespaceClass.base &&
-            obj->getNameURI() == ATOM_KEY(cx->runtime->atomState.lazy.functionNamespaceURIAtom)) {
+            obj->getNameURI() == ATOM_TO_JSVAL(cx->runtime->atomState.lazy.functionNamespaceURIAtom)) {
             return cx->runtime->defaultCompartment;
         }
 
         // Compile-time Function, Block, and RegExp objects are not parented.
         if (clasp == &js_FunctionClass || clasp == &js_BlockClass || clasp == &js_RegExpClass) {
             // This is a bogus answer, but it'll do for now.
             return cx->runtime->defaultCompartment;
         }
         JS_NOT_REACHED("non-global object at end of scope chain");
     }
-    jsval v = obj->getReservedSlot(JSRESERVED_GLOBAL_COMPARTMENT);
-    return (JSCompartment *) JSVAL_TO_PRIVATE(v);
+    const Value &v = obj->getReservedSlot(JSRESERVED_GLOBAL_COMPARTMENT);
+    return (JSCompartment *)v.asPrivate();
 }
 
 JS_FRIEND_API(JSBool)
 js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_GETTER_ONLY);
     return JS_FALSE;
 }
@@ -6312,17 +6330,17 @@ dumpValue(const Value &v)
         JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
         fprintf(stderr, "<%s %s at %p (JSFunction at %p)>",
                 fun->atom ? "function" : "unnamed",
                 fun->atom ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) : "function",
                 (void *) funobj,
                 (void *) fun);
     } else if (v.isObject()) {
         JSObject *obj = &v.asObject();
-        JSClass *clasp = obj->getClass();
+        Class *clasp = obj->getClass();
         fprintf(stderr, "<%s%s at %p>",
                 clasp->name,
                 clasp == &js_ObjectClass ? "" : " object",
                 (void *) obj);
     } else if (v.isBoolean()) {
         if (v.asBoolean())
             fprintf(stderr, "true");
         else
@@ -6418,21 +6436,21 @@ js_DumpObject(JSObject *obj)
             dumpScopeProp(sprop);
         }
     } else {
         if (!obj->isNative())
             fprintf(stderr, "not native\n");
     }
 
     fprintf(stderr, "proto ");
-    dumpValue(obj->getProtoValue());
+    dumpValue(ObjectOrNullTag(obj->getProto()));
     fputc('\n', stderr);
 
     fprintf(stderr, "parent ");
-    dumpValue(obj->getParentValue());
+    dumpValue(ObjectOrNullTag(obj->getParent()));
     fputc('\n', stderr);
 
     i = JSSLOT_PRIVATE;
     if (clasp->flags & JSCLASS_HAS_PRIVATE) {
         i = JSSLOT_PRIVATE + 1;
         fprintf(stderr, "private %p\n", obj->getPrivate());
     }
 
@@ -6525,17 +6543,17 @@ js_DumpStackFrame(JSContext *cx, JSStack
             for (Value *p = fp->slots(); p < sp; p++) {
                 fprintf(stderr, "    %p: ", (void *) p);
                 dumpValue(*p);
                 fputc('\n', stderr);
             }
         }
         fprintf(stderr, "  argv:  %p (argc: %u)\n", (void *) fp->argv, (unsigned) fp->argc);
         MaybeDumpObject("callobj", fp->callobj);
-        MaybeDumpObject("argsobj", fp->argsObj());
+        MaybeDumpObject("argsobj", fp->argsobj);
         MaybeDumpValue("this", fp->thisv);
         fprintf(stderr, "  rval: ");
         dumpValue(fp->rval);
         fputc('\n', stderr);
 
         fprintf(stderr, "  flags:");
         if (fp->flags == 0)
             fprintf(stderr, " none");
@@ -6552,18 +6570,18 @@ js_DumpStackFrame(JSContext *cx, JSStack
         if (fp->flags & JSFRAME_YIELDING)
             fprintf(stderr, " yielding");
         if (fp->flags & JSFRAME_GENERATOR)
             fprintf(stderr, " generator");
         if (fp->flags & JSFRAME_OVERRIDE_ARGS)
             fprintf(stderr, " overridden_args");
         fputc('\n', stderr);
 
-        if (fp->scopeChainObj())
-            fprintf(stderr, "  scopeChain: (JSObject *) %p\n", (void *) fp->scopeChainObj());
+        if (fp->scopeChain)
+            fprintf(stderr, "  scopeChain: (JSObject *) %p\n", (void *) fp->scopeChain);
         if (fp->blockChain)
             fprintf(stderr, "  blockChain: (JSObject *) %p\n", (void *) fp->blockChain);
 
         if (fp->displaySave)
             fprintf(stderr, "  displaySave: (JSStackFrame *) %p\n", (void *) fp->displaySave);
 
         fputc('\n', stderr);
     }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -156,17 +156,17 @@ struct PropDesc {
     bool hasSet : 1;
     bool hasValue : 1;
     bool hasWritable : 1;
     bool hasEnumerable : 1;
     bool hasConfigurable : 1;
 };
 
 namespace js {
-    typedef Vector<PropertyDescriptor, 1> PropertyDescriptorArray;
+    typedef Vector<PropDesc, 1> PropDescArray;
 }
 
 /* For detailed comments on these function pointer types, see jsprvtd.h. */
 struct JSObjectOps {
     /*
      * Custom shared object map for non-native objects. For native objects
      * this should be null indicating, that JSObject.map is an instance of
      * JSScope.
@@ -183,17 +183,17 @@ struct JSObjectOps {
     js::PropertyIdOp    deleteProperty;
     js::ConvertOp       defaultValue;
     js::NewEnumerateOp  enumerate;
     JSTypeOfOp          typeOf;
     JSTraceOp           trace;
 
     /* Optionally non-null members start here. */
     JSObjectOp          thisObject;
-    JSCallOp            call;
+    js::CallOp          call;
     js::Native          construct;
     js::HasInstanceOp   hasInstance;
     JSFinalizeOp        clear;
 
     bool inline isNative() const;
 };
 
 extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps;
@@ -271,18 +271,21 @@ struct JSObject {
      * manipulates JSObjects, which requires peeking under any encapsulation.
      */
     friend class js::TraceRecorder;
 
     JSObjectMap *map;                       /* property map, see jsscope.h */
     js::Class   *clasp;                     /* class pointer */
     jsuword     flags;                      /* see above */
     JSObject    *proto;                     /* object's prototype */
+    js::Value   *dslots;                    /* dynamically allocated slots */
+#if JS_BITS_PER_WORD == 32
+    uint32      padding;
+#endif
     js::Value   fslots[JS_INITIAL_NSLOTS];  /* small number of fixed slots */
-    js::Value   *dslots;                    /* dynamically allocated slots */
 
     bool isNative() const { return map->ops->isNative(); }
 
     js::Class *getClass() const {
         return clasp;
     }
 
     JSClass *getJSClass() const {
@@ -625,17 +628,17 @@ struct JSObject {
 
     /*
      * Back to generic stuff.
      */
 
     inline bool isCallable();
 
     /* The map field is not initialized here and should be set separately. */
-    void init(js::Class *aclasp, const js::Value &proto, const js::Value &parent,
+    void init(js::Class *aclasp, JSObject *proto, JSObject *parent,
               const js::Value &privateSlotValue) {
         JS_STATIC_ASSERT(JSSLOT_PRIVATE + 3 == JS_INITIAL_NSLOTS);
 
         clasp = aclasp;
         flags = 0;
         JS_ASSERT(!isDelegate());
         JS_ASSERT(!isSystem());
 
@@ -647,18 +650,18 @@ struct JSObject {
         dslots = NULL;
     }
 
     /*
      * Like init, but also initializes map. The catch: proto must be the result
      * of a call to js_InitClass(...clasp, ...).
      */
     inline void initSharingEmptyScope(js::Class *clasp,
-                                      const js::Value &proto,
-                                      const js::Value &parent,
+                                      JSObject *proto,
+                                      JSObject *parent,
                                       const js::Value &privateSlotValue);
 
     inline bool hasSlotsArray() const { return !!dslots; }
 
     /* This method can only be called when hasSlotsArray() returns true. */
     inline void freeSlotsArray(JSContext *cx);
 
     JSBool lookupProperty(JSContext *cx, jsid id,
@@ -744,17 +747,17 @@ struct JSObject {
 
     bool isCrossCompartmentWrapper() const;
 
     inline bool unbrand(JSContext *cx);
 
     inline void initArrayClass();
 };
 
-JS_STATIC_ASSERT(offsetof(JSObject, fslots) % sizeof(Value) == 0);
+JS_STATIC_ASSERT(offsetof(JSObject, fslots) % sizeof(js::Value) == 0);
 JS_STATIC_ASSERT(sizeof(JSObject) % JS_GCTHING_ALIGN == 0);
 
 #define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE)           \
                              ? JSSLOT_PRIVATE + 1                             \
                              : JSSLOT_PRIVATE)
 
 #define JSSLOT_FREE(clasp)  (JSSLOT_START(clasp)                              \
                              + JSCLASS_RESERVED_SLOTS(clasp))
@@ -783,45 +786,53 @@ JS_STATIC_ASSERT(sizeof(JSObject) % JS_G
 #define CX_THREAD_IS_RUNNING_GC(cx)                                           \
     THREAD_IS_RUNNING_GC((cx)->runtime, (cx)->thread)
 
 #endif /* JS_THREADSAFE */
 
 inline void
 OBJ_TO_INNER_OBJECT(JSContext *cx, JSObject *&obj)
 {
-    JSClass *clasp = obj->getClass();
+    js::Class *clasp = obj->getClass();
     if (clasp->flags & JSCLASS_IS_EXTENDED) {
-        JSExtendedClass *xclasp = (JSExtendedClass *) clasp;
+        js::ExtendedClass *xclasp = (js::ExtendedClass *) clasp;
         if (xclasp->innerObject)
             obj = xclasp->innerObject(cx, obj);
     }
 }
 
 /*
  * The following function has been copied to jsd/jsd_val.c. If making changes to
  * OBJ_TO_OUTER_OBJECT, please update jsd/jsd_val.c as well.
  */
 inline void
 OBJ_TO_OUTER_OBJECT(JSContext *cx, JSObject *&obj)
 {
-    JSClass *clasp = obj->getClass();
+    js::Class *clasp = obj->getClass();
     if (clasp->flags & JSCLASS_IS_EXTENDED) {
-        JSExtendedClass *xclasp = (JSExtendedClass *) clasp;
+        js::ExtendedClass *xclasp = (js::ExtendedClass *) clasp;
         if (xclasp->outerObject)
             obj = xclasp->outerObject(cx, obj);
     }
 }
 
-class ValueArray {
+class JSValueArray {
   public:
     jsval *array;
     size_t length;
 
-    ValueArray(jsval *v, size_t c) : array(v), length(c) {}
+    JSValueArray(jsval *v, size_t c) : array(v), length(c) {}
+};
+
+class ValueArray {
+  public:
+    js::Value *array;
+    size_t length;
+
+    ValueArray(js::Value *v, size_t c) : array(v), length(c) {}
 };
 
 extern js::Class js_ObjectClass;
 extern js::Class js_WithClass;
 extern js::Class js_BlockClass;
 
 /*
  * Block scope object macros.  The slots reserved by js_BlockClass are:
@@ -1289,24 +1300,24 @@ js_SetClassPrototype(JSContext *cx, JSOb
 extern JSBool
 js_PrimitiveToObject(JSContext *cx, js::Value *vp);
 
 /*
  * v and vp may alias. On successful return, vp->isObjectOrNull(). If vp is not
  * rooted, the caller must root vp before the next possible GC.
  */
 extern JSBool
-js_ValueToObjectOrNull(JSContext *cx, const js::Value &v, js::Value *vp);
+js_ValueToObjectOrNull(JSContext *cx, const js::Value &v, JSObject **objp);
 
 /*
  * v and vp may alias. On successful return, vp->isObject(). If vp is not
  * rooted, the caller must root vp before the next possible GC.
  */
-extern JSBool
-js_ValueToNonNullObject(JSContext *cx, const js::Value &v, js::Value *vp);
+extern JSObject *
+js_ValueToNonNullObject(JSContext *cx, const js::Value &v);
 
 extern JSBool
 js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, js::Value *rval);
 
 extern JSBool
 js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
              uintN argc, js::Value *argv, js::Value *rval);
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -471,34 +471,32 @@ JSObject::setQNameLocalName(jsval name)
 {
     JS_ASSERT(isQName());
     fslots[JSSLOT_QNAME_LOCAL_NAME] = js::Valueify(name);
 }
 
 inline JSObject *
 JSObject::getWithThis() const
 {
-    return JSVAL_TO_OBJECT(fslots[JSSLOT_WITH_THIS]);
+    return &fslots[JSSLOT_WITH_THIS].asObject();
 }
 
 inline void
 JSObject::setWithThis(JSObject *thisp)
 {
-    fslots[JSSLOT_WITH_THIS] = OBJECT_TO_JSVAL(thisp);
+    fslots[JSSLOT_WITH_THIS].setObject(*thisp);
 }
 
 inline void
-JSObject::initSharingEmptyScope(js::Class *clasp,
-                                const js::Value &proto,
-                                const js::Value &parent,
+JSObject::initSharingEmptyScope(js::Class *clasp, JSObject *proto, JSObject *parent,
                                 const js::Value &privateSlotValue)
 {
     init(clasp, proto, parent, privateSlotValue);
 
-    JSEmptyScope *emptyScope = proto.asObject().scope()->emptyScope;
+    JSEmptyScope *emptyScope = proto->scope()->emptyScope;
     JS_ASSERT(emptyScope->clasp == clasp);
     map = emptyScope->hold();
 }
 
 inline void
 JSObject::freeSlotsArray(JSContext *cx)
 {
     JS_ASSERT(hasSlotsArray());
@@ -602,17 +600,17 @@ InitScopeForObject(JSContext* cx, JSObje
             goto bad;
         uint32 freeslot = JSSLOT_FREE(clasp);
         JS_ASSERT(freeslot >= scope->freeslot);
         if (freeslot > JS_INITIAL_NSLOTS && !obj->allocSlots(cx, freeslot))
             goto bad;
         scope->freeslot = freeslot;
 #ifdef DEBUG
         if (freeslot < obj->numSlots())
-            obj->setSlot(freeslot, JSVAL_VOID);
+            obj->setSlot(freeslot, UndefinedTag());
 #endif
     }
 
     obj->map = scope;
     return true;
 
   bad:
     /* The GC nulls map initially. It should still be null on error. */
@@ -622,17 +620,17 @@ InitScopeForObject(JSContext* cx, JSObje
 
 /*
  * Helper optimized for creating a native instance of the given class (not the
  * class's prototype object). Use this in preference to NewObjectWithGivenProto
  * and NewObject, but use NewBuiltinClassInstance if you need the default class
  * prototype as proto, and its parent global as parent.
  */
 static inline JSObject *
-NewNativeClassInstance(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
+NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent)
 {
     JS_ASSERT(proto);
     JS_ASSERT(proto->isNative());
     JS_ASSERT(parent);
 
     DTrace::ObjectCreationScope objectCreationScope(cx, cx->fp, clasp);
 
     /*
@@ -659,41 +657,41 @@ NewNativeClassInstance(JSContext *cx, JS
         } else {
             obj->map = scope;
 
             /*
              * Do not call debug hooks on trace, because we might be in a non-_FAIL
              * builtin. See bug 481444.
              */
             if (cx->debugHooks->objectHook && !JS_ON_TRACE(cx)) {
-                AutoValueRooter tvr(cx, obj);
+                AutoObjectRooter tvr(cx, obj);
                 AutoKeepAtoms keep(cx->runtime);
                 cx->debugHooks->objectHook(cx, obj, JS_TRUE,
                                            cx->debugHooks->objectHookData);
                 cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj;
             }
         }
     }
 
     objectCreationScope.handleCreation(obj);
     return obj;
 }
 
 bool
 FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop,
-                   JSClass *clasp);
+                   Class *clasp);
 
 /*
  * Helper used to create Boolean, Date, RegExp, etc. instances of built-in
- * classes with class prototypes of the same JSClass. See, e.g., jsdate.cpp,
+ * classes with class prototypes of the same Class. See, e.g., jsdate.cpp,
  * jsregexp.cpp, and js_PrimitiveToObject in jsobj.cpp. Use this to get the
  * right default proto and parent for clasp in cx.
  */
 static inline JSObject *
-NewBuiltinClassInstance(JSContext *cx, JSClass *clasp)
+NewBuiltinClassInstance(JSContext *cx, Class *clasp)
 {
     VOUCH_DOES_NOT_REQUIRE_STACK();
 
     JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
     JS_ASSERT(protoKey != JSProto_Null);
 
     /* NB: inline-expanded and specialized version of js_GetClassPrototype. */
     JSObject *global;
@@ -702,36 +700,36 @@ NewBuiltinClassInstance(JSContext *cx, J
         OBJ_TO_INNER_OBJECT(cx, global);
         if (!global)
             return NULL;
     } else {
         global = cx->fp->scopeChain->getGlobal();
     }
     JS_ASSERT(global->getClass()->flags & JSCLASS_IS_GLOBAL);
 
-    jsval v = global->getReservedSlot(JSProto_LIMIT + protoKey);
+    const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey);
     JSObject *proto;
-    if (!JSVAL_IS_PRIMITIVE(v)) {
-        proto = JSVAL_TO_OBJECT(v);
+    if (v.isObject()) {
+        proto = &v.asObject();
         JS_ASSERT(proto->getParent() == global);
     } else {
         if (!FindClassPrototype(cx, global, protoKey, &proto, clasp))
             return NULL;
     }
 
     return NewNativeClassInstance(cx, clasp, proto, global);
 }
 
 /*
  * Like NewObject but with exactly the given proto. A null parent defaults to
  * proto->getParent() if proto is non-null (else to null). NB: only this helper
  * and NewObject can be used to construct full-sized JSFunction instances.
  */
 static inline JSObject *
-NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
+NewObjectWithGivenProto(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent)
 {
     DTrace::ObjectCreationScope objectCreationScope(cx, cx->fp, clasp);
 
     /* Always call the class's getObjectOps hook if it has one. */
     JSObjectOps *ops = clasp->getObjectOps
                        ? clasp->getObjectOps(cx, clasp)
                        : &js_ObjectOps;
 
@@ -755,19 +753,18 @@ NewObjectWithGivenProto(JSContext *cx, J
     if (!obj)
         goto out;
 
     /*
      * Default parent to the parent of the prototype, which was set from
      * the parent of the prototype's constructor.
      */
     obj->init(clasp,
-              js::ObjectOrNullTag(proto),
-              (!parent && proto) ? proto->getParentValue()
-                                 : ObjectOrNullTag(parent),
+              proto,
+              (!parent && proto) ? proto->getParent() : parent,
               JSObject::defaultPrivate(clasp));
 
     if (ops->isNative()) {
         if (!InitScopeForObject(cx, obj, clasp, proto, ops)) {
             obj = NULL;
             goto out;
         }
     } else {
--- a/js/src/jsops.cpp
+++ b/js/src/jsops.cpp
@@ -144,17 +144,17 @@ BEGIN_CASE(JSOP_POPN)
 {
     regs.sp -= GET_UINT16(regs.pc);
 #ifdef DEBUG
     JS_ASSERT(fp->base() <= regs.sp);
     JSObject *obj = fp->blockChain;
     JS_ASSERT_IF(obj,
                  OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
                  <= (size_t) (regs.sp - fp->base()));
-    for (obj = fp->scopeChainObj(); obj; obj = obj->getParent()) {
+    for (obj = fp->scopeChain; obj; obj = obj->getParent()) {
         Class *clasp = obj->getClass();
         if (clasp != &js_BlockClass && clasp != &js_WithClass)
             continue;
         if (obj->getPrivate() != js_FloatingFrameIfGenerator(cx, fp))
             break;
         JS_ASSERT(fp->base() + OBJ_BLOCK_DEPTH(cx, obj)
                              + ((clasp == &js_BlockClass)
                                 ? OBJ_BLOCK_COUNT(cx, obj)
@@ -179,21 +179,21 @@ BEGIN_CASE(JSOP_ENTERWITH)
      * We must ensure that different "with" blocks have different stack depth
      * associated with them. This allows the try handler search to properly
      * recover the scope chain. Thus we must keep the stack at least at the
      * current level.
      *
      * We set sp[-1] to the current "with" object to help asserting the
      * enter/leave balance in [leavewith].
      */
-    regs.sp[-1] = fp->scopeChain;
+    regs.sp[-1].setObject(*fp->scopeChain);
 END_CASE(JSOP_ENTERWITH)
 
 BEGIN_CASE(JSOP_LEAVEWITH)
-    JS_ASSERT(&regs.sp[-1].asObject() == fp->scopeChainObj());
+    JS_ASSERT(&regs.sp[-1].asObject() == fp->scopeChain);
     regs.sp--;
     js_LeaveWith(cx);
 END_CASE(JSOP_LEAVEWITH)
 
 BEGIN_CASE(JSOP_RETURN)
     POP_COPY_TO(fp->rval);
     /* FALL THROUGH */
 
@@ -225,17 +225,17 @@ BEGIN_CASE(JSOP_STOP)
     if ((fp->flags & JSFRAME_CONSTRUCTING) && fp->rval.isPrimitive())
         fp->rval = fp->thisv;
 
     interpReturnOK = true;
     if (inlineCallCount)
   inline_return:
     {
         JS_ASSERT(!fp->blockChain);
-        JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChainObj(), 0));
+        JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
 
         if (JS_LIKELY(script->staticLevel < JS_DISPLAY_SIZE))
             cx->display[script->staticLevel] = fp->displaySave;
 
         void *hookData = fp->hookData;
         if (JS_UNLIKELY(hookData != NULL)) {
             if (JSInterpreterHook hook = cx->debugHooks->callHook) {
                 hook(cx, fp, JS_FALSE, &interpReturnOK, hookData);
@@ -752,31 +752,31 @@ BEGIN_CASE(JSOP_BINDNAME)
          * assignment, and not here.
          *
          * This should be transparent to the hooks because the script, instead
          * of name = rhs, could have used global.name = rhs given a global
          * object reference, which also calls the hooks only after evaluating
          * the rhs. We desire such resolve hook equivalence between the two
          * forms.
          */
-        obj = fp->scopeChainObj();
+        obj = fp->scopeChain;
         if (!obj->getParent())
             break;
 
         PropertyCacheEntry *entry;
         JSObject *obj2;
         JSAtom *atom;
         JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
         if (!atom) {
             ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
             break;
         }
 
         jsid id = ATOM_TO_JSID(atom);
-        obj = js_FindIdentifierBase(cx, fp->scopeChainObj(), id);
+        obj = js_FindIdentifierBase(cx, fp->scopeChain, id);
         if (!obj)
             goto error;
     } while (0);
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_BINDNAME)
 
 BEGIN_CASE(JSOP_IMACOP)
@@ -1352,17 +1352,17 @@ BEGIN_CASE(JSOP_PROPDEC)
         FETCH_ELEMENT_ID(obj, -1, id);
     goto do_incop;
 
 BEGIN_CASE(JSOP_INCNAME)
 BEGIN_CASE(JSOP_DECNAME)
 BEGIN_CASE(JSOP_NAMEINC)
 BEGIN_CASE(JSOP_NAMEDEC)
 {
-    obj = fp->scopeChainObj();
+    obj = fp->scopeChain;
 
     JSObject *obj2;
     PropertyCacheEntry *entry;
     JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
     if (!atom) {
         ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
         if (obj == obj2 && entry->vword.isSlot()) {
             uint32 slot = entry->vword.toSlot();
@@ -2259,24 +2259,24 @@ BEGIN_CASE(JSOP_APPLY)
             } else {
                 newfp = stack.getInlineFrame(cx, regs.sp, 0, nfixed);
                 if (!newfp)
                     goto error;
             }
 
             /* Initialize stack frame. */
             newfp->callobj = NULL;
-            newfp->setArgsObj(NULL);
+            newfp->argsobj = NULL;
             newfp->script = newscript;
             newfp->fun = fun;
             newfp->argc = argc;
             newfp->argv = vp + 2;
             newfp->rval.setUndefined();
             newfp->annotation = NULL;
-            newfp->setScopeChainObj(obj->getParent());
+            newfp->scopeChain = obj->getParent();
             newfp->flags = flags;
             newfp->blockChain = NULL;
             if (JS_LIKELY(newscript->staticLevel < JS_DISPLAY_SIZE)) {
                 JSStackFrame **disp = &cx->display[newscript->staticLevel];
                 newfp->displaySave = *disp;
                 *disp = newfp;
             }
             JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
@@ -2388,17 +2388,17 @@ BEGIN_CASE(JSOP_SETCALL)
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
     goto error;
 }
 END_CASE(JSOP_SETCALL)
 
 BEGIN_CASE(JSOP_NAME)
 BEGIN_CASE(JSOP_CALLNAME)
 {
-    JSObject *obj = fp->scopeChainObj();
+    JSObject *obj = fp->scopeChain;
 
     JSScopeProperty *sprop;
     Value rval;
 
     PropertyCacheEntry *entry;
     JSObject *obj2;
     JSAtom *atom;
     JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
@@ -2528,17 +2528,17 @@ BEGIN_CASE(JSOP_REGEXP)
      * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
      * flouted by many browser-based implementations.
      *
      * We avoid the js_GetScopeChain call here and pass fp->scopeChain as
      * js_GetClassPrototype uses the latter only to locate the global.
      */
     jsatomid index = GET_FULL_INDEX(0);
     JSObject *proto;
-    if (!js_GetClassPrototype(cx, fp->scopeChainObj(), JSProto_RegExp, &proto))
+    if (!js_GetClassPrototype(cx, fp->scopeChain, JSProto_RegExp, &proto))
         goto error;
     JS_ASSERT(proto);
     JSObject *obj = js_CloneRegExpObject(cx, script->getRegExp(index), proto);
     if (!obj)
         goto error;
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_REGEXP)
@@ -3053,26 +3053,26 @@ BEGIN_CASE(JSOP_DEFFUN)
 
     JSObject *obj2;
     if (FUN_NULL_CLOSURE(fun)) {
         /*
          * Even a null closure needs a parent for principals finding.
          * FIXME: bug 476950, although debugger users may also demand some kind
          * of scope link for debugger-assisted eval-in-frame.
          */
-        obj2 = fp->scopeChainObj();
+        obj2 = fp->scopeChain;
     } else {
         JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
 
         /*
          * Inline js_GetScopeChain a bit to optimize for the case of a
          * top-level function.
          */
         if (!fp->blockChain) {
-            obj2 = fp->scopeChainObj();
+            obj2 = fp->scopeChain;
         } else {
             obj2 = js_GetScopeChain(cx, fp);
             if (!obj2)
                 goto error;
         }
     }
 
     /*
@@ -3091,17 +3091,17 @@ BEGIN_CASE(JSOP_DEFFUN)
     }
 
     /*
      * Protect obj from any GC hiding below JSObject::setProperty or
      * JSObject::defineProperty.  All paths from here must flow through the
      * fp->scopeChain code below the parent->defineProperty call.
      */
     MUST_FLOW_THROUGH("restore_scope");
-    fp->setScopeChainObj(obj);
+    fp->scopeChain = obj;
 
     Value rval = ObjectTag(*obj);
 
     /*
      * ECMA requires functions defined when entering Eval code to be
      * impermanent.
      */
     uintN attrs = (fp->flags & JSFRAME_EVAL)
@@ -3175,17 +3175,17 @@ BEGIN_CASE(JSOP_DEFFUN)
         pobj->dropProperty(cx, prop);
     }
     ok = doSet
          ? parent->setProperty(cx, id, &rval)
          : parent->defineProperty(cx, id, rval, getter, setter, attrs);
 
   restore_scope:
     /* Restore fp->scopeChain now that obj is defined in fp->callobj. */
-    fp->setScopeChainObj(obj2);
+    fp->scopeChain = obj2;
     if (!ok)
         goto error;
 }
 END_CASE(JSOP_DEFFUN)
 
 BEGIN_CASE(JSOP_DEFFUN_FC)
 BEGIN_CASE(JSOP_DEFFUN_DBGFC)
 {
@@ -3249,17 +3249,17 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
      */
     JSFunction *fun;
     LOAD_FUNCTION(SLOTNO_LEN);
     JS_ASSERT(fun->isInterpreted());
     JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
     JSObject *obj = FUN_OBJECT(fun);
 
     if (FUN_NULL_CLOSURE(fun)) {
-        obj = CloneFunctionObject(cx, fun, fp->scopeChainObj());
+        obj = CloneFunctionObject(cx, fun, fp->scopeChain);
         if (!obj)
             goto error;
     } else {
         JSObject *parent = js_GetScopeChain(cx, fp);
         if (!parent)
             goto error;
 
         if (obj->getParent() != parent) {
@@ -3316,17 +3316,17 @@ BEGIN_CASE(JSOP_LAMBDA)
     JSFunction *fun;
     LOAD_FUNCTION(0);
     JSObject *obj = FUN_OBJECT(fun);
 
     /* do-while(0) so we can break instead of using a goto. */
     do {
         JSObject *parent;
         if (FUN_NULL_CLOSURE(fun)) {
-            parent = fp->scopeChainObj();
+            parent = fp->scopeChain;
 
             if (obj->getParent() == parent) {
                 op = JSOp(regs.pc[JSOP_LAMBDA_LENGTH]);
 
                 /*
                  * Optimize ({method: function () { ... }, ...}) and
                  * this.method = function () { ... }; bytecode sequences.
                  */
@@ -3473,16 +3473,18 @@ BEGIN_CASE(JSOP_SETTER)
                              : js_setter_str);
         goto error;
     }
 
     /*
      * Getters and setters are just like watchpoints from an access control
      * point of view.
      */
+    Value rtmp;
+    uintN attrs;
     if (!CheckAccess(cx, obj, id, JSACC_WATCH, &rtmp, &attrs))
         goto error;
 
     PropertyOp getter, setter;
     if (op == JSOP_GETTER) {
         getter = CastAsPropertyOp(&rval.asObject());
         setter = PropertyStub;
         attrs = JSPROP_GETTER;
@@ -4255,17 +4257,17 @@ BEGIN_CASE(JSOP_ENTERBLOCK)
 
     /*
      * The young end of fp->scopeChain may omit blocks if we haven't closed
      * over them, but if there are any closure blocks on fp->scopeChain, they'd
      * better be (clones of) ancestors of the block we're entering now;
      * anything else we should have popped off fp->scopeChain when we left its
      * static scope.
      */
-    JSObject *obj2 = fp->scopeChainObj();
+    JSObject *obj2 = fp->scopeChain;
     Class *clasp;
     while ((clasp = obj2->getClass()) == &js_WithClass)
         obj2 = obj2->getParent();
     if (clasp == &js_BlockClass &&
         obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, fp)) {
         JSObject *youngestProto = obj2->getProto();
         JS_ASSERT(!OBJ_IS_CLONED_BLOCK(youngestProto));
         JSObject *parent = obj;
@@ -4287,17 +4289,17 @@ BEGIN_CASE(JSOP_LEAVEBLOCK)
 
     JS_ASSERT(blockDepth <= StackDepth(script));
 #endif
     /*
      * If we're about to leave the dynamic scope of a block that has been
      * cloned onto fp->scopeChain, clear its private data, move its locals from
      * the stack into the clone, and pop it off the chain.
      */
-    JSObject *obj = fp->scopeChainObj();
+    JSObject *obj = fp->scopeChain;
     if (obj->getProto() == fp->blockChain) {
         JS_ASSERT(obj->getClass() == &js_BlockClass);
         if (!js_PutBlockObject(cx, JS_TRUE))
             goto error;
     }
 
     /* Pop the block chain, too.  */
     fp->blockChain = fp->blockChain->getParent();
@@ -4319,17 +4321,17 @@ END_CASE(JSOP_LEAVEBLOCK)
 #if JS_HAS_GENERATORS
 BEGIN_CASE(JSOP_GENERATOR)
 {
     ASSERT_NOT_THROWING(cx);
     regs.pc += JSOP_GENERATOR_LENGTH;
     JSObject *obj = js_NewGenerator(cx);
     if (!obj)
         goto error;
-    JS_ASSERT(!fp->callobj && !fp->argsObj());
+    JS_ASSERT(!fp->callobj && !fp->argsobj);
     fp->rval.setObject(*obj);
     interpReturnOK = true;
     if (inlineCallCount != 0)
         goto inline_return;
     goto exit;
 }
 
 BEGIN_CASE(JSOP_YIELD)
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -5432,17 +5432,17 @@ Parser::statement()
              * block.
              */
             stmt->flags |= SIF_SCOPE;
             stmt->downScope = tc->topScopeStmt;
             tc->topScopeStmt = stmt;
             JS_SCOPE_DEPTH_METERING(++tc->scopeDepth > tc->maxScopeDepth &&
                                     (tc->maxScopeDepth = tc->scopeDepth));
 
-            obj->setParent(ObjectOrNullTag(tc->blockChain));
+            obj->setParent(tc->blockChain);
             tc->blockChain = obj;
             stmt->blockObj = obj;
 
 #ifdef DEBUG
             pn1 = tc->blockNode;
             JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
 #endif
 
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -49,26 +49,26 @@
 #include "jsscope.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
 namespace js {
 
-static jsval
+static inline const Value &
 GetCall(JSObject *proxy) {
     JS_ASSERT(proxy->isFunctionProxy());
     return proxy->getSlot(JSSLOT_PROXY_CALL);
 }
 
-static jsval
+static inline Value
 GetConstruct(JSObject *proxy) {
     if (proxy->numSlots() <= JSSLOT_PROXY_CONSTRUCT)
-        return JSVAL_VOID;
+        return UndefinedTag();
     return proxy->getSlot(JSSLOT_PROXY_CONSTRUCT);
 }
 
 static bool
 OperationInProgress(JSContext *cx, JSObject *proxy)
 {
     JSPendingProxyOperation *op = JS_THREAD_DATA(cx)->pendingProxyOperation;
     while (op) {
@@ -177,17 +177,17 @@ JSProxyHandler::set(JSContext *cx, JSObj
     desc.attrs = 0;
     desc.getter = NULL;
     desc.setter = NULL;
     desc.shortid = 0;
     return defineProperty(cx, proxy, id, &desc);
 }
 
 bool
-JSProxyHandler::enumerateOwn(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSProxyHandler::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     JS_ASSERT(props.length() == 0);
 
     if (!getOwnPropertyNames(cx, proxy, props))
         return false;
 
     /* Select only the enumerable properties through in-place iteration. */
@@ -210,93 +210,94 @@ JSProxyHandler::enumerateOwn(JSContext *
 
 bool
 JSProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     AutoIdVector props(cx);
     if (!enumerate(cx, proxy, props))
         return false;
-    return IdVectorToIterator(cx, proxy, flags, props, vp);
+    return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
 }
  
 JSString *
 JSProxyHandler::obj_toString(JSContext *cx, JSObject *proxy)
 {
     JS_ASSERT(proxy->isProxy());
 
     return JS_NewStringCopyZ(cx, proxy->isFunctionProxy()
                                  ? "[object Function]"
                                  : "[object Object]");
 }
 
 JSString *
 JSProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
 {
     JS_ASSERT(proxy->isProxy());
-    jsval fval = GetCall(proxy);
+    Value fval = GetCall(proxy);
     if (proxy->isFunctionProxy() &&
-        (JSVAL_IS_PRIMITIVE(fval) ||
-         !JSVAL_TO_OBJECT(fval)->isFunction())) {
+        (fval.isPrimitive() || !fval.asObject().isFunction())) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_INCOMPATIBLE_PROTO,
                              js_Function_str, js_toString_str,
                              "object");
         return NULL;
     }
-    return fun_toStringHelper(cx, JSVAL_TO_OBJECT(fval), indent);
+    return fun_toStringHelper(cx, &fval.asObject(), indent);
 }
 
 bool
-JSProxyHandler::call(JSContext *cx, JSObject *proxy, uintN argc, jsval *vp)
+JSProxyHandler::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     AutoValueRooter rval(cx);
-    JSBool ok = js_InternalInvoke(cx, vp[1], GetCall(proxy), 0, argc, JS_ARGV(cx, vp),
-                                  rval.addr());
+    JSBool ok = InternalInvoke(cx, vp[1], GetCall(proxy), 0, argc, JS_ARGV(cx, vp),
+                               rval.addr());
     if (ok)
         JS_SET_RVAL(cx, vp, rval.value());
     return ok;
 }
 
 bool
 JSProxyHandler::construct(JSContext *cx, JSObject *proxy, JSObject *receiver,
-                          uintN argc, jsval *argv, jsval *rval)
+                          uintN argc, Value *argv, Value *rval)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
-    jsval fval = GetConstruct(proxy);
-    if (fval == JSVAL_VOID) {
+    const Value &fval = GetConstruct(proxy);
+    if (fval.isUndefined()) {
         /*
          * proxy is the constructor, so get proxy.prototype as the proto
          * of the new object.
          */
         if (!JSProxy::get(cx, proxy, proxy, ATOM_TO_JSID(ATOM(classPrototype)), rval))
             return false;
 
         JSObject *proto;
-        if (!JSVAL_IS_PRIMITIVE(*rval)) {
-            proto = JSVAL_TO_OBJECT(*rval);
+        if (rval->isObject()) {
+            proto = &rval->asObject();
         } else {
             if (!js_GetClassPrototype(cx, NULL, JSProto_Object, &proto))
                 return false;
         }
 
         JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent());
+        if (!newobj)
+            return false;
 
-        *rval = OBJECT_TO_JSVAL(newobj);
+        rval->setObject(*newobj);
 
         /* If the call returns an object, return that, otherwise the original newobj. */
-        if (!js_InternalCall(cx, newobj, GetCall(proxy), argc, argv, rval))
+        if (!InternalCall(cx, newobj, GetCall(proxy), argc, argv, rval))
             return false;
-        if (JSVAL_IS_PRIMITIVE(*rval))
-            *rval = OBJECT_TO_JSVAL(newobj);
+        if (rval->isPrimitive())
+            rval->setObject(*newobj);
 
         return true;
     }
-    return js_InternalCall(cx, receiver, fval, argc, argv, rval);
+    return InternalCall(cx, receiver, fval, argc, argv, rval);
 }
 
 void
 JSProxyHandler::finalize(JSContext *cx, JSObject *proxy)
 {
 }
 
 void
@@ -422,17 +423,17 @@ ArrayToIdVector(JSContext *cx, const Val
 
     AutoIdRooter idr(cx);
     AutoValueRooter tvr(cx);
     for (jsuint n = 0; n < length; ++n) {
         if (!js_IndexToId(cx, n, idr.addr()))
             return false;
         if (!obj->getProperty(cx, idr.id(), tvr.addr()))
             return false;
-        if (!JS_ValueToId(cx, tvr.value(), idr.addr()))
+        if (!ValueToId(cx, tvr.value(), idr.addr()))
             return false;
         if (!props.append(js_CheckForStringIndex(idr.id())))
             return false;
     }
 
     return true;
 }
 
@@ -444,27 +445,27 @@ class JSScriptedProxyHandler : public JS
 
     /* ES5 Harmony fundamental proxy traps. */
     virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
                                        PropertyDescriptor *desc);
     virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
                                           PropertyDescriptor *desc);
     virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id,
                                 PropertyDescriptor *desc);
-    virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoValueVector &props);
+    virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props);
     virtual bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
-    virtual bool enumerate(JSContext *cx, JSObject *proxy, AutoValueVector &props);
+    virtual bool enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props);
     virtual bool fix(JSContext *cx, JSObject *proxy, Value *vp);
 
     /* ES5 Harmony derived proxy traps. */
     virtual bool has(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
     virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
     virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp);
     virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp);
-    virtual bool enumerateOwn(JSContext *cx, JSObject *proxy, AutoValueVector &props);
+    virtual bool enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props);
     virtual bool iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp);
 
     static JSScriptedProxyHandler singleton;
 };
 
 static int sScriptedProxyHandlerFamily = 0;
 
 JSScriptedProxyHandler::JSScriptedProxyHandler() : JSProxyHandler(&sScriptedProxyHandlerFamily)
@@ -526,17 +527,17 @@ JSScriptedProxyHandler::defineProperty(J
     AutoValueRooter tvr(cx);
     AutoValueRooter fval(cx);
     return FundamentalTrap(cx, handler, ATOM(defineProperty), fval.addr()) &&
            MakePropertyDescriptorObject(cx, id, desc, tvr.addr()) &&
            Trap2(cx, handler, fval.value(), id, tvr.value(), tvr.addr());
 }
 
 bool
-JSScriptedProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSScriptedProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     JSObject *handler = GetProxyHandlerObject(cx, proxy);
     AutoValueRooter tvr(cx);
     return FundamentalTrap(cx, handler, ATOM(getOwnPropertyNames), tvr.addr()) &&
            Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
            ArrayToIdVector(cx, tvr.value(), props);
 }
 
@@ -546,17 +547,17 @@ JSScriptedProxyHandler::delete_(JSContex
     JSObject *handler = GetProxyHandlerObject(cx, proxy);
     AutoValueRooter tvr(cx);
     return FundamentalTrap(cx, handler, ATOM(delete), tvr.addr()) &&
            Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
            ValueToBool(cx, tvr.value(), bp);
 }
 
 bool
-JSScriptedProxyHandler::enumerate(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSScriptedProxyHandler::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     JSObject *handler = GetProxyHandlerObject(cx, proxy);
     AutoValueRooter tvr(cx);
     return FundamentalTrap(cx, handler, ATOM(enumerate), tvr.addr()) &&
            Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
            ArrayToIdVector(cx, tvr.value(), props);
 }
 
@@ -624,17 +625,17 @@ JSScriptedProxyHandler::set(JSContext *c
     if (!DerivedTrap(cx, handler, ATOM(set), fval.addr()))
         return false;
     if (!js_IsCallable(fval.value()))
         return JSProxyHandler::set(cx, proxy, receiver, id, vp);
     return Trap(cx, handler, fval.value(), 3, argv, tvr.addr());
 }
 
 bool
-JSScriptedProxyHandler::enumerateOwn(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSScriptedProxyHandler::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     JSObject *handler = GetProxyHandlerObject(cx, proxy);
     AutoValueRooter tvr(cx);
     if (!DerivedTrap(cx, handler, ATOM(enumerateOwn), tvr.addr()))
         return false;
     if (!js_IsCallable(tvr.value()))
         return JSProxyHandler::enumerateOwn(cx, proxy, props);
     return Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
@@ -717,31 +718,31 @@ JSProxy::defineProperty(JSContext *cx, J
 {
     AutoPendingProxyOperation pending(cx, proxy);
     AutoPropertyDescriptorRooter desc(cx);
     return ParsePropertyDescriptorObject(cx, proxy, id, v, &desc) &&
            JSProxy::defineProperty(cx, proxy, id, &desc);
 }
 
 bool
-JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->getOwnPropertyNames(cx, proxy, props);
 }
 
 bool
 JSProxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->delete_(cx, proxy, id, bp);
 }
 
 bool
-JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->enumerate(cx, proxy, props);
 }
 
 bool
 JSProxy::fix(JSContext *cx, JSObject *proxy, Value *vp)
 {
@@ -773,39 +774,39 @@ JSProxy::get(JSContext *cx, JSObject *pr
 bool
 JSProxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
 {
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->set(cx, proxy, receiver, id, vp);
 }
 
 bool
-JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->enumerateOwn(cx, proxy, props);
 }
 
 bool
 JSProxy::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
 {
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->iterate(cx, proxy, flags, vp);
 }
 
 bool
-JSProxy::call(JSContext *cx, JSObject *proxy, uintN argc, jsval *vp)
+JSProxy::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
 {
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->call(cx, proxy, argc, vp);
 }
 
 bool
 JSProxy::construct(JSContext *cx, JSObject *proxy, JSObject *receiver,
-                   uintN argc, jsval *argv, jsval *rval)
+                   uintN argc, Value *argv, Value *rval)
 {
     AutoPendingProxyOperation pending(cx, proxy);
     return proxy->getProxyHandler()->construct(cx, proxy, receiver, argc, argv, rval);
 }
 
 JSString *
 JSProxy::obj_toString(JSContext *cx, JSObject *proxy)
 {
@@ -925,17 +926,17 @@ proxy_TypeOf_obj(JSContext *cx, JSObject
 {
     return JSTYPE_OBJECT;
 }
 
 void
 proxy_Finalize(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isProxy());
-    if (obj->getSlot(JSSLOT_PROXY_HANDLER) != JSVAL_VOID)
+    if (!obj->getSlot(JSSLOT_PROXY_HANDLER).isUndefined())
         obj->getProxyHandler()->finalize(cx, obj);
 }
 
 extern JSObjectOps js_ObjectProxyObjectOps;
 
 static const JSObjectMap SharedObjectProxyMap(&js_ObjectProxyObjectOps, JSObjectMap::SHAPELESS);
 
 JSObjectOps js_ObjectProxyObjectOps = {
@@ -971,17 +972,17 @@ JS_FRIEND_API(Class) ObjectProxyClass = 
     EnumerateStub,          ResolveStub,     ConvertStub,     NULL,
     obj_proxy_getObjectOps, NULL,            NULL,            NULL,
     NULL,                   NULL,            NULL,            NULL
 };
 
 JSBool
 proxy_Call(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *proxy = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
+    JSObject *proxy = &JS_CALLEE(cx, vp).asObject();
     JS_ASSERT(proxy->isProxy());
     return JSProxy::call(cx, proxy, argc, vp);
 }
 
 JSBool
 proxy_Construct(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
     JSObject *proxy = &argv[-2].asObject();
@@ -1036,26 +1037,26 @@ JS_FRIEND_API(Class) FunctionProxyClass 
     NULL,                   NULL,            NULL,            NULL
 };
 
 JS_FRIEND_API(JSObject *)
 NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObject *proto, JSObject *parent,
                JSObject *call, JSObject *construct)
 {
     bool fun = call || construct;
-    JSClass *clasp = fun ? &FunctionProxyClass : &ObjectProxyClass;
+    Class *clasp = fun ? &FunctionProxyClass : &ObjectProxyClass;
     JSObject *obj = NewObjectWithGivenProto(cx, clasp, proto, parent);
     if (!obj || (construct && !js_EnsureReservedSlots(cx, obj, 0)))
         return NULL;
-    obj->setSlot(JSSLOT_PROXY_HANDLER, PRIVATE_TO_JSVAL(handler));
+    obj->setSlot(JSSLOT_PROXY_HANDLER, PrivateTag(handler));
     obj->setSlot(JSSLOT_PROXY_PRIVATE, priv);
     if (fun) {
-        obj->setSlot(JSSLOT_PROXY_CALL, call ? OBJECT_TO_JSVAL(call) : JSVAL_VOID);
+        obj->setSlot(JSSLOT_PROXY_CALL, call ? Value(ObjectTag(*call)) : Value(UndefinedTag()));
         if (construct)
-            obj->setSlot(JSSLOT_PROXY_CONSTRUCT, construct ? OBJECT_TO_JSVAL(construct) : JSVAL_VOID);
+            obj->setSlot(JSSLOT_PROXY_CONSTRUCT, construct ? Value(ObjectTag(*construct)) : Value(UndefinedTag()));
     }
     return obj;
 }
 
 static JSObject *
 NonNullObject(JSContext *cx, const Value &v)
 {
     if (v.isPrimitive()) {
@@ -1080,17 +1081,17 @@ proxy_create(JSContext *cx, uintN argc, 
     if (argc > 1 && vp[3].isObject()) {
         proto = &vp[3].asObject();
         parent = proto->getParent();
     } else {
         JS_ASSERT(IsFunctionObject(vp[0]));
         proto = NULL;
         parent = vp[0].asObject().getParent();
     }
-    JSObject *proxy = NewProxyObject(cx, &JSScriptedProxyHandler::singleton, OBJECT_TO_JSVAL(handler),
+    JSObject *proxy = NewProxyObject(cx, &JSScriptedProxyHandler::singleton, ObjectTag(*handler),
                                      proto, parent);
     if (!proxy)
         return false;
 
     vp->setObject(*proxy);
     return true;
 }
 
@@ -1116,17 +1117,17 @@ proxy_createFunction(JSContext *cx, uint
         return false;
     JSObject *construct = NULL;
     if (argc > 2) {
         construct = js_ValueToCallableObject(cx, &vp[4], JSV2F_SEARCH_STACK);
         if (!construct)
             return false;
     }
 
-    JSObject *proxy = NewProxyObject(cx, &JSScriptedProxyHandler::singleton, OBJECT_TO_JSVAL(handler),
+    JSObject *proxy = NewProxyObject(cx, &JSScriptedProxyHandler::singleton, ObjectTag(*handler),
                                      proto, parent, call, construct);
     if (!proxy)
         return false;
 
     vp->setObject(*proxy);
     return true;
 }
 
@@ -1206,37 +1207,40 @@ callable_Construct(JSContext *cx, JSObje
         fval = callable->fslots[JSSLOT_CALLABLE_CALL];
         JS_ASSERT(fval.isObject());
 
         /* callable is the constructor, so get callable.prototype is the proto of the new object. */
         if (!callable->getProperty(cx, ATOM_TO_JSID(ATOM(classPrototype)), rval))
             return false;
 
         JSObject *proto;
-        if (!JSVAL_IS_PRIMITIVE(*rval)) {
-            proto = JSVAL_TO_OBJECT(*rval);
+        if (rval->isObject()) {
+            proto = &rval->asObject();
         } else {
             if (!js_GetClassPrototype(cx, NULL, JSProto_Object, &proto))
                 return false;
         }
 
         JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent());
-        *rval = OBJECT_TO_JSVAL(newobj);
+        if (!newobj)
+            return false;
+
+        rval->setObject(*newobj);
 
         /* If the call returns an object, return that, otherwise the original newobj. */
-        if (!js_InternalCall(cx, newobj, callable->fslots[JSSLOT_CALLABLE_CALL],
-                             argc, argv, rval)) {
+        if (!InternalCall(cx, newobj, callable->fslots[JSSLOT_CALLABLE_CALL],
+                          argc, argv, rval)) {
             return false;
         }
-        if (JSVAL_IS_PRIMITIVE(*rval))
-            *rval = OBJECT_TO_JSVAL(newobj);
+        if (rval->isPrimitive())
+            rval->setObject(*newobj);
 
         return true;
     }
-    return js_InternalCall(cx, obj, fval, argc, argv, rval);
+    return InternalCall(cx, obj, fval, argc, argv, rval);
 }
 
 Class CallableObjectClass = {
     "Function",
     JSCLASS_HAS_RESERVED_SLOTS(2),
     PropertyStub,           PropertyStub,    PropertyStub,    PropertyStub,
     EnumerateStub,          ResolveStub,     ConvertStub,     NULL,
     NULL,                   NULL,            callable_Call,   callable_Construct,
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -57,33 +57,33 @@ class JSProxyHandler {
 
     /* ES5 Harmony fundamental proxy traps. */
     virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
                                        PropertyDescriptor *desc) = 0;
     virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
                                           PropertyDescriptor *desc) = 0;
     virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id,
                                 PropertyDescriptor *desc) = 0;
-    virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, js::AutoValueVector &props) = 0;
+    virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, js::AutoIdVector &props) = 0;
     virtual bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp) = 0;
-    virtual bool enumerate(JSContext *cx, JSObject *proxy, js::AutoValueVector &props) = 0;
+    virtual bool enumerate(JSContext *cx, JSObject *proxy, js::AutoIdVector &props) = 0;
     virtual bool fix(JSContext *cx, JSObject *proxy, Value *vp) = 0;
 
     /* ES5 Harmony derived proxy traps. */
     virtual bool has(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
     virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
     virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp);
     virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp);
-    virtual bool enumerateOwn(JSContext *cx, JSObject *proxy, js::AutoValueVector &props);
+    virtual bool enumerateOwn(JSContext *cx, JSObject *proxy, js::AutoIdVector &props);
     virtual bool iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp);
 
     /* Spidermonkey extensions. */
-    virtual bool call(JSContext *cx, JSObject *proxy, uintN argc, jsval *vp);
+    virtual bool call(JSContext *cx, JSObject *proxy, uintN argc, js::Value *vp);
     virtual bool construct(JSContext *cx, JSObject *proxy, JSObject *receiver,
-                           uintN argc, jsval *argv, jsval *rval);
+                           uintN argc, js::Value *argv, js::Value *rval);
     virtual JSString *obj_toString(JSContext *cx, JSObject *proxy);
     virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
     virtual void finalize(JSContext *cx, JSObject *proxy);
     virtual void trace(JSTracer *trc, JSObject *proxy);
 
     inline void *family() {
         return mFamily;
     }
@@ -96,33 +96,33 @@ class JSProxy {
     static bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
                                       PropertyDescriptor *desc);
     static bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, Value *vp);
     static bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
                                          PropertyDescriptor *desc);
     static bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, Value *vp);
     static bool defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc);
     static bool defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v);
-    static bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, js::AutoValueVector &props);
+    static bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, js::AutoIdVector &props);
     static bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
-    static bool enumerate(JSContext *cx, JSObject *proxy, js::AutoValueVector &props);
+    static bool enumerate(JSContext *cx, JSObject *proxy, js::AutoIdVector &props);
     static bool fix(JSContext *cx, JSObject *proxy, Value *vp);
 
     /* ES5 Harmony derived proxy traps. */
     static bool has(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
     static bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
     static bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp);
     static bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp);
-    static bool enumerateOwn(JSContext *cx, JSObject *proxy, js::AutoValueVector &props);
+    static bool enumerateOwn(JSContext *cx, JSObject *proxy, js::AutoIdVector &props);
     static bool iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp);
 
     /* Spidermonkey extensions. */
-    static bool call(JSContext *cx, JSObject *proxy, uintN argc, jsval *vp);
+    static bool call(JSContext *cx, JSObject *proxy, uintN argc, js::Value *vp);
     static bool construct(JSContext *cx, JSObject *proxy, JSObject *receiver,
-                          uintN argc, jsval *argv, jsval *rval);
+                          uintN argc, js::Value *argv, js::Value *rval);
     static JSString *obj_toString(JSContext *cx, JSObject *proxy);
     static JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
 };
 
 /* Shared between object and function proxies. */
 const uint32 JSSLOT_PROXY_HANDLER = JSSLOT_PRIVATE + 0;
 const uint32 JSSLOT_PROXY_PRIVATE = JSSLOT_PRIVATE + 1;
 /* Function proxies only. */
@@ -152,18 +152,17 @@ JSObject::isProxy() const
 {
     return isObjectProxy() || isFunctionProxy();
 }
 
 inline js::JSProxyHandler *
 JSObject::getProxyHandler() const
 {
     JS_ASSERT(isProxy());
-    jsval handler = getSlot(js::JSSLOT_PROXY_HANDLER);
-    return (js::JSProxyHandler *) JSVAL_TO_PRIVATE(handler);
+    return (js::JSProxyHandler *) getSlot(js::JSSLOT_PROXY_HANDLER).asPrivate();
 }
 
 inline const js::Value &
 JSObject::getProxyPrivate() const
 {
     JS_ASSERT(isProxy());
     return getSlot(js::JSSLOT_PROXY_PRIVATE);
 }
@@ -173,17 +172,18 @@ JSObject::setProxyPrivate(const js::Valu
 {
     JS_ASSERT(isProxy());
     setSlot(js::JSSLOT_PROXY_PRIVATE, priv);
 }
 
 namespace js {
 
 JS_FRIEND_API(JSObject *)
-NewProxyObject(JSContext *cx, JSProxyHandler *handler, jsval priv, JSObject *proto, JSObject *parent,
+NewProxyObject(JSContext *cx, JSProxyHandler *handler, const js::Value &priv,
+               JSObject *proto, JSObject *parent,
                JSObject *call = NULL, JSObject *construct = NULL);
 
 JS_FRIEND_API(JSBool)
 FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp);
 
 }
 
 JS_BEGIN_EXTERN_C
--- a/js/src/jsrecursion.cpp
+++ b/js/src/jsrecursion.cpp
@@ -589,19 +589,19 @@ TraceRecorder::slurpDownFrames(jsbytecod
     /* callee */
     slurpSlot(argv_ins, -2 * ptrdiff_t(sizeof(Value)), &fp->argv[-2], &info);
     /* this */
     slurpSlot(argv_ins, -1 * ptrdiff_t(sizeof(Value)), &fp->argv[-1], &info);
     /* args[0..n] */
     for (unsigned i = 0; i < JS_MAX(fp->argc, fp->fun->nargs); i++)
         slurpSlot(argv_ins, i * sizeof(Value), &fp->argv[i], &info);
     /* argsobj */
-    slurpSlot(fp_ins, offsetof(JSStackFrame, argsval), &fp->argsval, &info);
+    slurpFrameObjPtrSlot(fp_ins, offsetof(JSStackFrame, argsobj), &fp->argsobj, &info);
     /* scopeChain */
-    slurpSlot(fp_ins, offsetof(JSStackFrame, scopeChain), &fp->scopeChain, &info);
+    slurpFrameObjPtrSlot(fp_ins, offsetof(JSStackFrame, scopeChain), &fp->scopeChain, &info);
     /* vars */
     LIns* slots_ins = addName(lir->ins2(LIR_addp, fp_ins, INS_CONSTWORD(sizeof(JSStackFrame))),
                               "slots");
     for (unsigned i = 0; i < fp->script->nfixed; i++)
         slurpSlot(slots_ins, i * sizeof(Value), &fp->slots()[i], &info);
     /* stack vals */
     unsigned nfixed = fp->script->nfixed;
     Value* stack = fp->base();
@@ -651,20 +651,27 @@ class ImportFrameSlotsVisitor : public S
 {
     TraceRecorder &mRecorder;
 public:
     ImportFrameSlotsVisitor(TraceRecorder &recorder) : mRecorder(recorder)
     {}
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(Value *vp, size_t count, JSStackFrame* fp) {
+        /* N.B. vp may point to a JSObject*. */
         for (size_t i = 0; i < count; ++i)
             mRecorder.get(vp++);
         return true;
     }
+
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(JSObject **p, JSStackFrame* fp) {
+        /* visitStackSlots only uses the address of its argument. */
+        return visitStackSlots((Value *)p, 1, fp);
+    }
 };
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::downRecursion()
 {
     JSStackFrame* fp = cx->fp;
     if ((jsbytecode*)fragment->ip < fp->script->code ||
         (jsbytecode*)fragment->ip >= fp->script->code + fp->script->length) {
@@ -788,23 +795,56 @@ TraceRecorder::slurpSlot(LIns* addr_ins,
     if (info->curSlot < info->slurpFailSlot) {
         info->curSlot++;
         return;
     }
     VMSideExit* exit = copy(info->exit);
     exit->slurpFailSlot = info->curSlot;
     exit->slurpType = info->typeMap[info->curSlot];
 
-#if defined DEBUG
     /* Make sure that we don't try and record infinity branches */
     JS_ASSERT_IF(anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT &&
                  info->curSlot == info->slurpFailSlot,
                  anchor->slurpType != exit->slurpType);
-#endif
 
     LIns* val = slurpSlot(addr_ins, offset, vp, exit);
     lir->insStore(val,
-                   lirbuf->sp,
-                   -tree->nativeStackBase + ptrdiff_t(info->curSlot) * sizeof(double),
-                   ACC_STACK);
+                  lirbuf->sp,
+                  -tree->nativeStackBase + ptrdiff_t(info->curSlot) * sizeof(double),
+                  ACC_STACK);
     info->curSlot++;
 }
 
+JS_REQUIRES_STACK void
+TraceRecorder::slurpFrameObjPtrSlot(LIns* addr_ins, ptrdiff_t offset, JSObject** p, SlurpInfo* info)
+{
+    /* Don't re-read slots that aren't needed. */
+    if (info->curSlot < info->slurpFailSlot) {
+        info->curSlot++;
+        return;
+    }
+    VMSideExit* exit = copy(info->exit);
+    exit->slurpFailSlot = info->curSlot;
+    exit->slurpType = info->typeMap[info->curSlot];
+
+    /* Make sure that we don't try and record infinity branches */
+    JS_ASSERT_IF(anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT &&
+                 info->curSlot == info->slurpFailSlot,
+                 anchor->slurpType != exit->slurpType);
+
+    LIns *val;
+    LIns *ptr_val = lir->insLoad(LIR_ldp, addr_ins, offset, ACC_OTHER);
+    LIns *ptr_is_null_ins = lir->insEqP_0(ptr_val);
+    if (exit->slurpType == JSVAL_TYPE_NULL) {
+        guard(true, ptr_is_null_ins, exit);
+        val = INS_NULL();
+    } else {
+        JS_ASSERT(exit->slurpType == JSVAL_TYPE_NONFUNOBJ);
+        guard(false, ptr_is_null_ins, exit);
+        val = ptr_val;
+    }
+
+    lir->insStore(val,
+                  lirbuf->sp,
+                  -tree->nativeStackBase + ptrdiff_t(info->curSlot) * sizeof(double),
+                  ACC_STACK);
+    info->curSlot++;
+}
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -5092,89 +5092,89 @@ static void
 SetRegExpLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex)
 {
     JS_ASSERT(obj->isRegExp());
     obj->setRegExpLastIndex(NumberTag(lastIndex));
 }
 
 #define DEFINE_GETTER(name, code)                                              \
     static JSBool                                                              \
-    name(JSContext *cx, JSObject *obj, jsid id, jsval *vp)                     \
+    name(JSContext *cx, JSObject *obj, jsid id, Value *vp)                     \
     {                                                                          \
         while (obj->getClass() != &js_RegExpClass) {                           \
             obj = obj->getProto();                                             \
             if (!obj)                                                          \
                 return true;                                                   \
         }                                                                      \
         JS_LOCK_OBJ(cx, obj);                                                  \
         JSRegExp *re = (JSRegExp *) obj->getPrivate();                         \
         code;                                                                  \
         JS_UNLOCK_OBJ(cx, obj);                                                \
         return true;                                                           \
     }
 
 /* lastIndex is stored in the object, re = re silences the compiler warning. */
-DEFINE_GETTER(lastIndex_getter,  re = re; *vp = Jsvalify(obj->getRegExpLastIndex()))
-DEFINE_GETTER(source_getter,     *vp = STRING_TO_JSVAL(re->source))
-DEFINE_GETTER(global_getter,     *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_GLOB) != 0))
-DEFINE_GETTER(ignoreCase_getter, *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0))
-DEFINE_GETTER(multiline_getter,  *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_MULTILINE) != 0))
-DEFINE_GETTER(sticky_getter,     *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_STICKY) != 0))
+DEFINE_GETTER(lastIndex_getter,  re = re; *vp = obj->getRegExpLastIndex())
+DEFINE_GETTER(source_getter,     vp->setString(re->source))
+DEFINE_GETTER(global_getter,     vp->setBoolean((re->flags & JSREG_GLOB) != 0))
+DEFINE_GETTER(ignoreCase_getter, vp->setBoolean((re->flags & JSREG_FOLD) != 0))
+DEFINE_GETTER(multiline_getter,  vp->setBoolean((re->flags & JSREG_MULTILINE) != 0))
+DEFINE_GETTER(sticky_getter,     vp->setBoolean((re->flags & JSREG_STICKY) != 0))
 
 static JSBool
-lastIndex_setter(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+lastIndex_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     while (obj->getClass() != &js_RegExpClass) {
         obj = obj->getProto();
         if (!obj)
             return true;
     }
     jsdouble lastIndex;
-    if (!JS_ValueToNumber(cx, *vp, &lastIndex))
+    if (!ValueToNumber(cx, *vp, &lastIndex))
         return false;
     lastIndex = js_DoubleToInteger(lastIndex);
     SetRegExpLastIndex(cx, obj, lastIndex);
     return true;
 }
 
 static JSBool
 regexp_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
 {
     JS_ASSERT(obj->isRegExp());
 
     if (!JSID_IS_ATOM(id))
         return JS_TRUE;
 
     if (id == ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom)) {
-        if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID,
+        if (!js_DefineNativeProperty(cx, obj, id, UndefinedTag(),
                                      lastIndex_getter, lastIndex_setter,
                                      JSPROP_PERMANENT | JSPROP_SHARED, 0, 0, NULL)) {
             return JS_FALSE;
         }
         *objp = obj;
         return JS_TRUE;
     }
 
     static const struct LazyProp {
         const char *name;
         uint16 atomOffset;
-        JSPropertyOp getter;
+        PropertyOp getter;
     } props[] = {
         { js_source_str,     ATOM_OFFSET(source),     source_getter },
         { js_global_str,     ATOM_OFFSET(global),     global_getter },
         { js_ignoreCase_str, ATOM_OFFSET(ignoreCase), ignoreCase_getter },
         { js_multiline_str,  ATOM_OFFSET(multiline),  multiline_getter },
         { js_sticky_str,     ATOM_OFFSET(sticky),     sticky_getter }
     };
 
     for (size_t i = 0; i < JS_ARRAY_LENGTH(props); i++) {
         const LazyProp &lazy = props[i];
         JSAtom *atom = OFFSET_TO_ATOM(cx->runtime, lazy.atomOffset);
         if (id == ATOM_TO_JSID(atom)) {
-            if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID,
+            if (!js_DefineNativeProperty(cx, obj, id, UndefinedTag(),
                                          lazy.getter, NULL,
                                          JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY,
                                          0, 0, NULL)) {
                 return JS_FALSE;
             }
             *objp = obj;
             return JS_TRUE;
         }
@@ -5346,17 +5346,17 @@ regexp_finalize(JSContext *cx, JSObject 
 /* Forward static prototype. */
 static JSBool
 regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv,
                 JSBool test, Value *rval);
 
 static JSBool
 regexp_call(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
-    return regexp_exec_sub(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, JS_FALSE, rval);
+    return regexp_exec_sub(cx, &argv[-2].asObject(), argc, argv, JS_FALSE, rval);
 }
 
 #if JS_HAS_XDR
 
 #include "jsxdrapi.h"
 
 JSBool
 js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp)
@@ -5482,17 +5482,17 @@ js_regexp_toString(JSContext *cx, JSObje
     }
     vp->setString(str);
     return JS_TRUE;
 }
 
 static JSBool
 regexp_toString(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     return obj && js_regexp_toString(cx, obj, vp);
 }
 
 static JSBool
 regexp_compile_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv,
                    Value *rval)
 {
     JSString *opt, *str;
@@ -5603,17 +5603,17 @@ created:
         js_DestroyRegExp(cx, oldre);
     rval->setObject(*obj);
     return JS_TRUE;
 }
 
 static JSBool
 regexp_compile(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     return obj && regexp_compile_sub(cx, obj, argc, vp + 2, vp);
 }
 
 static JSBool
 regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv,
                 JSBool test, Value *rval)
 {
     JSBool ok, sticky;
@@ -5687,24 +5687,24 @@ regexp_exec_sub(JSContext *cx, JSObject 
 out:
     DROP_REGEXP(cx, re);
     return ok;
 }
 
 static JSBool
 regexp_exec(JSContext *cx, uintN argc, Value *vp)
 {
-    return regexp_exec_sub(cx, ComputeThisObjectFromVp(cx, vp),
+    return regexp_exec_sub(cx, ComputeThisFromVp(cx, vp),
                            argc, vp + 2, JS_FALSE, vp);
 }
 
 static JSBool
 regexp_test(JSContext *cx, uintN argc, Value *vp)
 {
-    if (!regexp_exec_sub(cx, ComputeThisObjectFromVp(cx, vp),
+    if (!regexp_exec_sub(cx, ComputeThisFromVp(cx, vp),
                          argc, vp + 2, JS_TRUE, vp))
         return JS_FALSE;
     if (!vp->isTrue())
         vp->setBoolean(false);
     return JS_TRUE;
 }
 
 static JSFunctionSpec regexp_methods[] = {
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -129,17 +129,17 @@ js_GetMutableScope(JSContext *cx, JSObje
         obj->map = scope;
         return NULL;
     }
 
     if (nslots > JS_INITIAL_NSLOTS && nslots > newscope->freeslot)
         newscope->freeslot = nslots;
 #ifdef DEBUG
     if (newscope->freeslot < nslots)
-        obj->setSlot(newscope->freeslot, JSVAL_VOID);
+        obj->setSlot(newscope->freeslot, UndefinedTag());
 #endif
 
     JS_DROP_ALL_EMPTY_SCOPE_LOCKS(cx, scope);
     static_cast<JSEmptyScope *>(scope)->drop(cx);
     return newscope;
 }
 
 /*
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -265,56 +265,56 @@ JSScopeProperty::matchesParamsAfterId(js
            rawSetter == asetter &&
            slot == aslot &&
            attrs == aattrs &&
            ((flags ^ aflags) & PUBLIC_FLAGS) == 0 &&
            shortid == ashortid;
 }
 
 inline bool
-JSScopeProperty::get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp)
+JSScopeProperty::get(JSContext* cx, JSObject* obj, JSObject *pobj, js::Value* vp)
 {
-    JS_ASSERT(!JSVAL_IS_NULL(this->id));
+    JS_ASSERT(!JSID_IS_VOID(this->id));
     JS_ASSERT(!hasDefaultGetter());
 
     if (hasGetterValue()) {
         JS_ASSERT(!isMethod());
-        jsval fval = getterValue();
-        return js_InternalGetOrSet(cx, obj, id, fval, JSACC_READ, 0, 0, vp);
+        js::Value fval = getterValue();
+        return js::InternalGetOrSet(cx, obj, id, fval, JSACC_READ, 0, 0, vp);
     }
 
     if (isMethod()) {
-        *vp = methodValue();
+        vp->setObject(methodObject());
 
         JSScope *scope = pobj->scope();
         JS_ASSERT(scope->object == pobj);
         return scope->methodReadBarrier(cx, this, vp);
     }
 
     /*
      * |with (it) color;| ends up here, as do XML filter-expressions.
      * Avoid exposing the With object to native getters.
      */
     if (obj->getClass() == &js_WithClass)
         obj = js_UnwrapWithObject(cx, obj);
     return js::callJSPropertyOp(cx, getterOp(), obj, SPROP_USERID(this), vp);
 }
 
 inline bool
-JSScopeProperty::set(JSContext* cx, JSObject* obj, jsval* vp)
+JSScopeProperty::set(JSContext* cx, JSObject* obj, js::Value* vp)
 {
     JS_ASSERT_IF(hasDefaultSetter(), hasGetterValue());
 
     if (attrs & JSPROP_SETTER) {
-        jsval fval = setterValue();
-        return js_InternalGetOrSet(cx, obj, id, fval, JSACC_WRITE, 1, vp, vp);
+        js::Value fval = setterValue();
+        return js::InternalGetOrSet(cx, obj, id, fval, JSACC_WRITE, 1, vp, vp);
     }
 
     if (attrs & JSPROP_GETTER)
-        return !!js_ReportGetterOnlyAssignment(cx);
+        return js_ReportGetterOnlyAssignment(cx);
 
     /* See the comment in JSScopeProperty::get as to why we check for With. */
     if (obj->getClass() == &js_WithClass)
         obj = js_UnwrapWithObject(cx, obj);
     return js::callJSPropertyOpSetter(cx, setterOp(), obj, SPROP_USERID(this), vp);
 }
 
 #endif /* jsscopeinlines_h___ */
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -439,17 +439,17 @@ js_str_escape(JSContext *cx, JSObject *o
     rval->setString(str);
     return JS_TRUE;
 }
 #undef IS_OK
 
 static JSBool
 str_escape(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    JSObject *obj = ComputeThisFromVp(cx, vp);
     return obj && js_str_escape(cx, obj, argc, vp + 2, vp);
 }
 
 /* See ECMA-262 Edition 3 B.2.2 */
 static JSBool
 str_unescape(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *str;
@@ -628,17 +628,17 @@ Class js_StringClass = {
             if (!str)                                                         \
                 return JS_FALSE;                                              \
         }                                                                     \
     JS_END_MACRO
 
 static JSString *
 NormalizeThis(JSContext *cx, Value *vp)
 {
-    if (vp[1].isNull() && (!ComputeThisFromVpInPlace(cx, vp) || vp[1].isNull()))
+    if (vp[1].isNull() && (!ComputeThisFromVp(cx, vp) || vp[1].isNull()))
         return NULL;
 
     /*
      * js_GetPrimitiveThis seems to do a bunch of work (like calls to
      * JS_THIS_OBJECT) which we don't need in the common case (where
      * vp[1] is a String object) here.  Note that vp[1] can still be a
      * primitive value at this point.
      */
--- a/js/src/jstl.h
+++ b/js/src/jstl.h
@@ -438,11 +438,25 @@ class AlignedPtrAndFlag
     }
 
     void set(T *t, bool flag) {
         JS_ASSERT((uintptr_t(t) & 1) == 0);
         bits = uintptr_t(t) | flag;
     }
 };
 
+template <class T>
+static inline void
+Reverse(T *beg, T *end)
+{
+    while (beg != end) {
+        if (--end == beg)
+            return;
+        T tmp = *beg;
+        *beg = *end;
+        *end = tmp;
+        ++beg;
+    }
+}
+
 } /* namespace js */
 
 #endif /* jstl_h_ */
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -197,18 +197,52 @@ using namespace nanojit;
 
 #define RETURN_IF_XML_A(val) RETURN_VALUE_IF_XML(val, ARECORD_STOP)
 #define RETURN_IF_XML(val)   RETURN_VALUE_IF_XML(val, RECORD_STOP)
 
 JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
 JS_STATIC_ASSERT(offsetof(TraceNativeStorage, stack_global_buf) % 16 == 0);
 
 /* Map to translate a type tag into a printable representation. */
-static const char typeChar[] = "OIDXSNBUF";
-static const char tagChar[]  = "OIDISIBI";
+#ifdef DEBUG
+static char
+TypeToChar(JSValueType type)
+{
+    switch (type) {
+      case JSVAL_TYPE_DOUBLE: return 'D';
+      case JSVAL_TYPE_INT32: return 'I';
+      case JSVAL_TYPE_STRING: return 'S';
+      case JSVAL_TYPE_OBJECT: return '!';
+      case JSVAL_TYPE_BOOLEAN: return 'B';
+      case JSVAL_TYPE_NULL: return 'N';
+      case JSVAL_TYPE_UNDEFINED: return 'U';
+      case JSVAL_TYPE_MAGIC: return 'M';
+      case JSVAL_TYPE_FUNOBJ: return 'F';
+      case JSVAL_TYPE_NONFUNOBJ: return 'O';
+      case JSVAL_TYPE_BOXED: return '#';
+      case JSVAL_TYPE_UNINITIALIZED: return '*';
+    }
+    return '?';
+}
+
+static char
+ValueToTypeChar(const Value &v)
+{
+    if (v.isInt32()) return 'I';
+    if (v.isDouble()) return 'D';
+    if (v.isString()) return 'S';
+    if (v.isObject()) return v.asObject().isFunction() ? 'F' : 'O';
+    if (v.isBoolean()) return 'B';
+    if (v.isNull()) return 'N';
+    if (v.isUndefined()) return 'U';
+    if (v.isMagic()) return 'M';
+    return '?';
+}
+#endif
+
 
 /* Blacklist parameters. */
 
 /*
  * Number of iterations of a loop where we start tracing.  That is, we don't
  * start tracing until the beginning of the HOTLOOP-th iteration.
  */
 #define HOTLOOP 2
@@ -1056,16 +1090,29 @@ getCoercedType(const Value &v)
                ? JSVAL_TYPE_INT32
                : JSVAL_TYPE_DOUBLE;
     }
     if (v.isObject())
         return v.asObject().isFunction() ? JSVAL_TYPE_FUNOBJ : JSVAL_TYPE_NONFUNOBJ;
     return v.extractNonDoubleObjectTraceType();
 }
 
+static inline JSValueType
+getFrameObjPtrTraceType(JSObject *obj)
+{
+    JS_ASSERT_IF(obj, !obj->isFunction());
+    return obj ? JSVAL_TYPE_NONFUNOBJ : JSVAL_TYPE_NULL;
+}
+
+static inline bool
+IsFrameObjPtrTraceType(JSValueType t)
+{
+    return t == JSVAL_TYPE_NULL || t == JSVAL_TYPE_NONFUNOBJ || t == JSVAL_TYPE_FUNOBJ;
+}
+
 /* Constant seed and accumulate step borrowed from the DJB hash. */
 
 const uintptr_t ORACLE_MASK = ORACLE_SIZE - 1;
 JS_STATIC_ASSERT((ORACLE_MASK & ORACLE_SIZE) == 0);
 
 const uintptr_t FRAGMENT_TABLE_MASK = FRAGMENT_TABLE_SIZE - 1;
 JS_STATIC_ASSERT((FRAGMENT_TABLE_MASK & FRAGMENT_TABLE_SIZE) == 0);
 
@@ -1092,17 +1139,17 @@ GlobalSlotHash(JSContext* cx, unsigned s
 {
     uintptr_t h = HASH_SEED;
     JSStackFrame* fp = cx->fp;
 
     while (fp->down)
         fp = fp->down;
 
     HashAccum(h, uintptr_t(fp->script), ORACLE_MASK);
-    HashAccum(h, uintptr_t(fp->scopeChainObj()->getGlobal()->shape()), ORACLE_MASK);
+    HashAccum(h, uintptr_t(fp->scopeChain->getGlobal()->shape()), ORACLE_MASK);
     HashAccum(h, uintptr_t(slot), ORACLE_MASK);
     return int(h);
 }
 
 static inline int
 PCHash(jsbytecode* pc)
 {
     return int(uintptr_t(pc) & ORACLE_MASK);
@@ -1706,23 +1753,23 @@ VisitFrameSlots(Visitor &visitor, JSCont
 
     if (fp->argv) {
         if (depth == 0) {
             visitor.setStackSlotKind("args");
             if (!visitor.visitStackSlots(&fp->argv[-2], argSlots(fp) + 2, fp))
                 return false;
         }
         visitor.setStackSlotKind("arguments");
-        if (!visitor.visitStackSlots(&fp->argsval, 1, fp))
+        if (!visitor.visitFrameObjPtr(&fp->argsobj, fp))
             return false;
         // We want to import and track |JSObject *scopeChain|, but the tracker
         // requires type |Value|. But the bits are the same, so we can import
         // it with a cast and the (identity function) unboxing will be OK.
         visitor.setStackSlotKind("scopeChain");
-        if (!visitor.visitStackSlots(&fp->scopeChain, 1, fp))
+        if (!visitor.visitFrameObjPtr(&fp->scopeChain, fp))
             return false;
         visitor.setStackSlotKind("var");
         if (!visitor.visitStackSlots(fp->slots(), fp->script->nfixed, fp))
             return false;
     }
 
     visitor.setStackSlotKind("stack");
     Value *base = fp->base();
@@ -1773,17 +1820,17 @@ VisitGlobalSlots(Visitor &visitor, JSCon
 }
 
 class AdjustCallerTypeVisitor;
 
 template <typename Visitor>
 static JS_REQUIRES_STACK JS_ALWAYS_INLINE void
 VisitGlobalSlots(Visitor &visitor, JSContext *cx, SlotList &gslots)
 {
-    VisitGlobalSlots(visitor, cx, cx->fp->scopeChainObj()->getGlobal(),
+    VisitGlobalSlots(visitor, cx, cx->fp->scopeChain->getGlobal(),
                      gslots.length(), gslots.data());
 }
 
 
 template <typename Visitor>
 static JS_REQUIRES_STACK JS_ALWAYS_INLINE void
 VisitSlots(Visitor& visitor, JSContext* cx, JSObject* globalObj,
            unsigned callDepth, unsigned ngslots, uint16* gslots)
@@ -1792,17 +1839,17 @@ VisitSlots(Visitor& visitor, JSContext* 
         VisitGlobalSlots(visitor, cx, globalObj, ngslots, gslots);
 }
 
 template <typename Visitor>
 static JS_REQUIRES_STACK JS_ALWAYS_INLINE void
 VisitSlots(Visitor& visitor, JSContext* cx, unsigned callDepth,
            unsigned ngslots, uint16* gslots)
 {
-    VisitSlots(visitor, cx, cx->fp->scopeChainObj()->getGlobal(),
+    VisitSlots(visitor, cx, cx->fp->scopeChain->getGlobal(),
                callDepth, ngslots, gslots);
 }
 
 template <typename Visitor>
 static JS_REQUIRES_STACK JS_ALWAYS_INLINE void
 VisitSlots(Visitor &visitor, JSContext *cx, JSObject *globalObj,
            unsigned callDepth, const SlotList& slots)
 {
@@ -1810,17 +1857,17 @@ VisitSlots(Visitor &visitor, JSContext *
                slots.data());
 }
 
 template <typename Visitor>
 static JS_REQUIRES_STACK JS_ALWAYS_INLINE void
 VisitSlots(Visitor &visitor, JSContext *cx, unsigned callDepth,
            const SlotList& slots)
 {
-    VisitSlots(visitor, cx, cx->fp->scopeChainObj()->getGlobal(),
+    VisitSlots(visitor, cx, cx->fp->scopeChain->getGlobal(),
                callDepth, slots.length(), slots.data());
 }
 
 
 class SlotVisitorBase {
 #if defined JS_JIT_SPEW
 protected:
     char const *mStackSlotKind;
@@ -1836,37 +1883,49 @@ public:
     JS_ALWAYS_INLINE void setStackSlotKind(char const *k) {}
 #endif
 };
 
 struct CountSlotsVisitor : public SlotVisitorBase
 {
     unsigned mCount;
     bool mDone;
-    const Value* mStop;
+    const void* mStop;
 public:
-    JS_ALWAYS_INLINE CountSlotsVisitor(const Value* stop = NULL) :
+    JS_ALWAYS_INLINE CountSlotsVisitor(const void* stop = NULL) :
         mCount(0),
         mDone(false),
         mStop(stop)
     {}
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(Value *vp, size_t count, JSStackFrame* fp) {
         if (mDone)
             return false;
-        if (mStop && size_t(mStop - vp) < count) {
-            mCount += size_t(mStop - vp);
+        if (mStop && size_t(((const Value *)mStop) - vp) < count) {
+            mCount += size_t(((const Value *)mStop) - vp);
             mDone = true;
             return false;
         }
         mCount += count;
         return true;
     }
 
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(JSObject **p, JSStackFrame* fp) {
+        if (mDone)
+            return false;
+        if (mStop && mStop == p) {
+            mDone = true;
+            return false;
+        }
+        mCount++;
+        return true;
+    }
+
     JS_ALWAYS_INLINE unsigned count() {
         return mCount;
     }
 
     JS_ALWAYS_INLINE bool stopped() {
         return mDone;
     }
 };
@@ -1923,36 +1982,46 @@ public:
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     visitGlobalSlot(Value *vp, unsigned n, unsigned slot) {
             JSValueType type = getCoercedType(*vp);
             if (type == JSVAL_TYPE_INT32 && (!mOracle || mOracle->isGlobalSlotUndemotable(mCx, slot)))
                 type = JSVAL_TYPE_DOUBLE;
             JS_ASSERT(type != JSVAL_TYPE_BOXED);
             debug_only_printf(LC_TMTracer,
-                              "capture type global%d: %d=%c\n",
-                              n, type, typeChar[type]);
+                              "capture type global%d: %c\n",
+                              n, TypeToChar(type));
             *mPtr++ = type;
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(Value *vp, int count, JSStackFrame* fp) {
         for (int i = 0; i < count; ++i) {
             JSValueType type = getCoercedType(vp[i]);
             if (type == JSVAL_TYPE_INT32 && (!mOracle || mOracle->isStackSlotUndemotable(mCx, length())))
                 type = JSVAL_TYPE_DOUBLE;
             JS_ASSERT(type != JSVAL_TYPE_BOXED);
             debug_only_printf(LC_TMTracer,
-                              "capture type %s%d: %d=%c\n",
-                              stackSlotKind(), i, type, typeChar[type]);
+                              "capture type %s%d: %c\n",
+                              stackSlotKind(), i, TypeToChar(type));
             *mPtr++ = type;
         }
         return true;
     }
 
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(JSObject **p, JSStackFrame* fp) {
+        JSValueType type = getFrameObjPtrTraceType(*p);
+        debug_only_printf(LC_TMTracer,
+                          "capture type %s%d: %c\n",
+                          stackSlotKind(), 0, TypeToChar(type));
+        *mPtr++ = type;
+        return true;
+    }
+
     JS_ALWAYS_INLINE uintptr_t length() {
         return mPtr - mTypeMap;
     }
 };
 
 void
 TypeMap::set(unsigned stackSlots, unsigned ngslots,
              const JSValueType* stackTypeMap, const JSValueType* globalTypeMap)
@@ -2133,17 +2202,17 @@ TraceRecorder::TraceRecorder(JSContext* 
     newobj_ins(NULL),
     pendingSpecializedNative(NULL),
     pendingUnboxSlot(NULL),
     pendingGuardCondition(NULL),
     pendingLoop(true),
     generatedSpecializedNative(),
     tempTypeMap(cx)
 {
-    JS_ASSERT(globalObj == cx->fp->scopeChainObj()->getGlobal());
+    JS_ASSERT(globalObj == cx->fp->scopeChain->getGlobal());
     JS_ASSERT(globalObj->scope()->hasOwnShape());
     JS_ASSERT(cx->regs->pc == (jsbytecode*)fragment->ip);
 
     fragment->lirbuf = lirbuf;
 #ifdef DEBUG
     lirbuf->printer = new (tempAlloc()) LInsPrinter(tempAlloc());
 #endif
 
@@ -2462,68 +2531,93 @@ TraceRecorder::nativeGlobalSlot(const Va
     JS_ASSERT(isGlobal(p));
     if (size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS)
         return ptrdiff_t(p - globalObj->fslots);
     return ptrdiff_t((p - globalObj->dslots) + JS_INITIAL_NSLOTS);
 }
 
 /* Determine the offset in the native global frame for a jsval we track. */
 ptrdiff_t
-TraceRecorder::nativeGlobalOffset(Value* p) const
+TraceRecorder::nativeGlobalOffset(const Value* p) const
 {
     return nativeGlobalSlot(p) * sizeof(double);
 }
 
 /* Determine whether a value is a global stack slot. */
 bool
 TraceRecorder::isGlobal(const Value* p) const
 {
     return ((size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) ||
             (size_t(p - globalObj->dslots) < (globalObj->numSlots() - JS_INITIAL_NSLOTS)));
 }
 
+bool
+TraceRecorder::isVoidPtrGlobal(const void* p) const
+{
+    return isGlobal((const Value *)p);
+}
+
 /*
  * Return the offset in the native stack for the given jsval. More formally,
  * |p| must be the address of a jsval that is represented in the native stack
  * area. The return value is the offset, from TracerState::stackBase, in bytes,
  * where the native representation of |*p| is stored. To get the offset
  * relative to TracerState::sp, subtract TreeFragment::nativeStackBase.
  */
 JS_REQUIRES_STACK ptrdiff_t
-TraceRecorder::nativeStackOffset(const Value* p) const
+TraceRecorder::nativeStackOffsetImpl(const void* p) const
 {
     CountSlotsVisitor visitor(p);
     VisitStackSlots(visitor, cx, callDepth);
     size_t offset = visitor.count() * sizeof(double);
 
     /*
      * If it's not in a pending frame, it must be on the stack of the current
      * frame above sp but below fp->slots() + script->nslots.
      */
     if (!visitor.stopped()) {
-        JS_ASSERT(size_t(p - cx->fp->slots()) < cx->fp->script->nslots);
-        offset += size_t(p - cx->regs->sp) * sizeof(double);
+        const Value *vp = (const Value *)p;
+        JS_ASSERT(size_t(vp - cx->fp->slots()) < cx->fp->script->nslots);
+        offset += size_t(vp - cx->regs->sp) * sizeof(double);
     }
     return offset;
 }
 
-JS_REQUIRES_STACK ptrdiff_t
+JS_REQUIRES_STACK inline ptrdiff_t
+TraceRecorder::nativeStackOffset(const Value* p) const
+{
+    return nativeStackOffsetImpl(p);
+}
+
+JS_REQUIRES_STACK inline ptrdiff_t
+TraceRecorder::nativeStackSlotImpl(const void* p) const
+{
+    return nativeStackOffsetImpl(p) / sizeof(double);
+}
+
+JS_REQUIRES_STACK inline ptrdiff_t
 TraceRecorder::nativeStackSlot(const Value* p) const
 {
-    return nativeStackOffset(p) / sizeof(double);
+    return nativeStackSlotImpl(p);
 }
 
 /*
  * Return the offset, from TracerState:sp, for the given jsval. Shorthand for:
  *  -TreeFragment::nativeStackBase + nativeStackOffset(p).
  */
 inline JS_REQUIRES_STACK ptrdiff_t
-TraceRecorder::nativespOffset(Value* p) const
-{
-    return -tree->nativeStackBase + nativeStackOffset(p);
+TraceRecorder::nativespOffsetImpl(const void* p) const
+{
+    return -tree->nativeStackBase + nativeStackOffsetImpl(p);
+}
+
+inline JS_REQUIRES_STACK ptrdiff_t
+TraceRecorder::nativespOffset(const Value* p) const
+{
+    return nativespOffsetImpl(p);
 }
 
 /* Track the maximum number of native frame slots we need during execution. */
 inline void
 TraceRecorder::trackNativeStackUse(unsigned slots)
 {
     if (slots > tree->maxNativeStackSlots)
         tree->maxNativeStackSlots = slots;
@@ -2705,28 +2799,16 @@ TraceMonitor::mark(JSTracer* trc)
  */
 static inline void
 NativeToValue(JSContext* cx, Value& v, JSValueType type, double* slot)
 {
     if (type == JSVAL_TYPE_DOUBLE) {
         v.setNumber(*slot);
     } else if (JS_LIKELY(type <= JSVAL_UPPER_INCL_TYPE_OF_BOXABLE_SET)) {
         v.boxNonDoubleFrom(type, (uint64 *)slot);
-    } else if (type == JSVAL_TYPE_STRORNULL) {
-        JSString *str = *(JSString**) slot;
-        if (str)
-            v.setString(str);
-        else
-            v.setNull();
-    } else if (type == JSVAL_TYPE_OBJORNULL) {
-        JSObject *obj = *(JSObject**) slot;
-        if (obj)
-            v.setObject(*obj);
-        else
-            v.setNull();
     } else {
         JS_ASSERT(type == JSVAL_TYPE_BOXED);
         JS_STATIC_ASSERT(sizeof(Value) == sizeof(double));
         v = *(Value *)slot;
     }
 
 #ifdef DEBUG
     switch (type) {
@@ -2763,22 +2845,16 @@ NativeToValue(JSContext* cx, Value& v, J
         JSFunction* fun = GET_FUNCTION_PRIVATE(cx, &v.asObject());
         debug_only_printf(LC_TMTracer,
                           "function<%p:%s> ", (void*) &v.asObject(),
                           fun->atom
                           ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom))
                           : "unnamed");
         break;
       }
-      case JSVAL_TYPE_STRORNULL:
-        debug_only_printf(LC_TMTracer, "nullablestr<%p> ", v.isNull() ? NULL : (void*)v.asString());
-        break;
-      case JSVAL_TYPE_OBJORNULL:
-        debug_only_printf(LC_TMTracer, "nullableobj<%p> ", v.isNull() ? NULL : (void*)&v.asObject());
-        break;
       case JSVAL_TYPE_BOXED:
         debug_only_printf(LC_TMTracer, "box<%llx> ", v.asRawBits());
         break;
       default:
         JS_NOT_REACHED("unexpected type");
         break;
     }
 #endif
@@ -2816,16 +2892,37 @@ public:
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(Value *vp, int count, JSStackFrame* fp) {
         for (int i = 0; i < count; ++i) {
             debug_only_printf(LC_TMTracer, "%s%d: ", stackSlotKind(), i);
             ValueToNative(*vp++, *mTypeMap++, mStack++);
         }
         return true;
     }
+
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(JSObject **p, JSStackFrame* fp) {
+        debug_only_printf(LC_TMTracer, "%s%d: ", stackSlotKind(), 0);
+        *(JSObject **)mStack = *p;
+#ifdef DEBUG
+        if (*mTypeMap == JSVAL_TYPE_NULL) {
+            JS_ASSERT(*p == NULL);
+            debug_only_print0(LC_TMTracer, "null ");
+        } else {
+            JS_ASSERT(*mTypeMap == JSVAL_TYPE_NONFUNOBJ);
+            JS_ASSERT(!(*p)->isFunction());
+            debug_only_printf(LC_TMTracer,
+                              "object<%p:%s> ", (void*)*p,
+                              (*p)->getClass()->name);
+        }
+#endif
+        mTypeMap++;
+        mStack++;
+        return true;
+    }
 };
 
 static JS_REQUIRES_STACK void
 BuildNativeFrame(JSContext *cx, JSObject *globalObj, unsigned callDepth,
                  unsigned ngslots, uint16 *gslots,
                  JSValueType *typeMap, double *global, double *stack)
 {
     BuildNativeFrameVisitor visitor(cx, typeMap, global, stack);
@@ -2892,16 +2989,43 @@ public:
             if (unsigned(mTypeMap - mInitTypeMap) >= mIgnoreSlots)
                 NativeToValue(mCx, *vp, *mTypeMap, mStack);
             vp++;
             mTypeMap++;
             mStack++;
         }
         return true;
     }
+
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(JSObject **p, JSStackFrame* fp) {
+        JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota);
+        if ((Value *)p == mStop)
+            return false;
+        debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), 0);
+        if (unsigned(mTypeMap - mInitTypeMap) >= mIgnoreSlots) {
+            *p = *(JSObject **)mStack;
+#ifdef DEBUG
+            JSValueType type = *mTypeMap;
+            if (type == JSVAL_TYPE_NULL) {
+                debug_only_print0(LC_TMTracer, "null ");
+            } else {
+                JS_ASSERT(type == JSVAL_TYPE_NONFUNOBJ);
+                JS_ASSERT(!(*p)->isFunction());
+                debug_only_printf(LC_TMTracer,
+                                  "object<%p:%s> ",
+                                  (void*) *p,
+                                  (*p)->getClass()->name);
+            }
+#endif
+        }
+        mTypeMap++;
+        mStack++;
+        return true;
+    }
 };
 
 /* Box the given native frame into a JS frame. This is infallible. */
 static JS_REQUIRES_STACK void
 FlushNativeGlobalFrame(JSContext *cx, JSObject *globalObj, double *global, unsigned ngslots,
                        uint16 *gslots, JSValueType *typemap)
 {
     FlushNativeGlobalFrameVisitor visitor(cx, typemap, global);
@@ -3250,68 +3374,69 @@ FlushNativeStackFrame(JSContext* cx, uns
             // Skip over stopFrame itself.
             JS_ASSERT(n != 0);
             --n;
             fp = fp->down;
         }
         for (; n != 0; fp = fp->down) {
             --n;
             if (fp->argv) {
-                if (fp->argsObj() && GetArgsPrivateNative(fp->argsObj()))
-                    fp->argsObj()->setPrivate(fp);
+                if (fp->argsobj && GetArgsPrivateNative(fp->argsobj))
+                    fp->argsobj->setPrivate(fp);
 
                 JS_ASSERT(fp->argv[-1].isObjectOrNull());
                 JS_ASSERT(fp->callee()->isFunction());
                 JS_ASSERT(GET_FUNCTION_PRIVATE(cx, fp->callee()) == fp->fun);
 
                 if (FUN_INTERPRETED(fp->fun) &&
                     (fp->fun->flags & JSFUN_HEAVYWEIGHT)) {
                     // Iff these fields are NULL, then |fp| was synthesized on trace exit, so
                     // we need to update the frame fields.
                     if (!fp->callobj)
-                        fp->callobj = fp->scopeChainObj();
+                        fp->callobj = fp->scopeChain;
 
                     // Iff scope chain's private is NULL, then |fp->scopeChain| was created
                     // on trace for a call, so we set the private field now. (Call objects
                     // that correspond to returned frames also have a NULL private, but such
                     // a call object would not occur as the |scopeChain| member of a frame,
                     // so we cannot be in that case here.)
-                    if (!fp->scopeChainObj()->getPrivate())
-                        fp->scopeChainObj()->setPrivate(fp);
+                    if (!fp->scopeChain->getPrivate())
+                        fp->scopeChain->setPrivate(fp);
                 }
                 fp->thisv = fp->argv[-1];
                 if (fp->flags & JSFRAME_CONSTRUCTING) // constructors always compute 'this'
                     fp->flags |= JSFRAME_COMPUTED_THIS;
             }
         }
     }
     debug_only_print0(LC_TMTracer, "\n");
     return visitor.getTypeMap() - mp;
 }
 
 /* Emit load instructions onto the trace that read the initial stack state. */
 JS_REQUIRES_STACK void
-TraceRecorder::import(LIns* base, ptrdiff_t offset, const Value* p, JSValueType t,
-                      const char *prefix, uintN index, JSStackFrame *fp)
+TraceRecorder::importImpl(LIns* base, ptrdiff_t offset, const void* p, JSValueType t,
+                          const char *prefix, uintN index, JSStackFrame *fp)
 {
     LIns* ins;
     AccSet accSet = base == lirbuf->sp ? ACC_STACK : ACC_OTHER;
     if (t == JSVAL_TYPE_INT32) { /* demoted */
-        JS_ASSERT(hasInt32Repr(*p));
+        JS_ASSERT(hasInt32Repr(*(const Value *)p));
 
         /*
          * Ok, we have a valid demotion attempt pending, so insert an integer
          * read and promote it to double since all arithmetic operations expect
          * to see doubles on entry. The first op to use this slot will emit a
          * d2i cast which will cancel out the i2d we insert here.
          */
         ins = lir->insLoad(LIR_ldi, base, offset + sPayloadOffset, accSet);
         ins = lir->ins1(LIR_i2d, ins);
     } else {
-        JS_ASSERT_IF(t != JSVAL_TYPE_BOXED, p->isNumber() == (t == JSVAL_TYPE_DOUBLE));
+        JS_ASSERT_IF(t != JSVAL_TYPE_BOXED && !IsFrameObjPtrTraceType(t),
+                     ((const Value *)p)->isNumber() == (t == JSVAL_TYPE_DOUBLE));
         if (t == JSVAL_TYPE_DOUBLE) {
             ins = lir->insLoad(LIR_ldd, base, offset, accSet);
         } else if (t == JSVAL_TYPE_BOOLEAN) {
             ins = lir->insLoad(LIR_ldi, base, offset + sPayloadOffset, accSet);
         } else if (t == JSVAL_TYPE_UNDEFINED) {
             ins = INS_UNDEFINED();
         } else if (t == JSVAL_TYPE_MAGIC) {
             ins = lir->insLoad(LIR_ldi, base, offset + sPayloadOffset, accSet);
@@ -3347,22 +3472,26 @@ TraceRecorder::import(LIns* base, ptrdif
     } else {
         JS_snprintf(name, sizeof name, "$%s%d", prefix, index);
     }
 
     if (mark)
         JS_ARENA_RELEASE(&cx->tempPool, mark);
     addName(ins, name);
 
-    static const char* typestr[] = {
-        "object", "int", "double", "jsval", "string", "null", "boolean", "function"
-    };
-    debug_only_printf(LC_TMTracer, "import vp=%p name=%s type=%s flags=%d\n",
-                      (void*)p, name, typestr[t & 7], t >> 3);
-#endif
+    debug_only_printf(LC_TMTracer, "import vp=%p name=%s type=%c\n",
+                      p, name, TypeToChar(t));
+#endif
+}
+
+JS_REQUIRES_STACK void
+TraceRecorder::import(LIns* base, ptrdiff_t offset, const Value* p, JSValueType t,
+                          const char *prefix, uintN index, JSStackFrame *fp)
+{
+    return importImpl(base, offset, p, t, prefix, index, fp);
 }
 
 class ImportBoxedStackSlotVisitor : public SlotVisitorBase
 {
     TraceRecorder &mRecorder;
     LIns *mBase;
     ptrdiff_t mStackOffset;
     JSValueType *mTypemap;
@@ -3389,16 +3518,24 @@ public:
                 mRecorder.set(vp, vp_ins);
             }
             vp++;
             mTypemap++;
             mStackOffset += sizeof(double);
         }
         return true;
     }
+
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(JSObject **p, JSStackFrame *fp) {
+        JS_ASSERT(*mTypemap != JSVAL_TYPE_BOXED);
+        mTypemap++;
+        mStackOffset += sizeof(double);
+        return true;
+    }
 };
 
 JS_REQUIRES_STACK void
 TraceRecorder::import(TreeFragment* tree, LIns* sp, unsigned stackSlots, unsigned ngslots,
                       unsigned callDepth, JSValueType* typeMap)
 {
     /*
      * If we get a partial list that doesn't have all the types (i.e. recording
@@ -3531,34 +3668,34 @@ TraceRecorder::writeBack(LIns* i, LIns* 
      */
     if (shouldDemote && isPromoteInt(i))
         i = demote(lir, i);
     return lir->insStore(i, base, offset, (base == lirbuf->sp) ? ACC_STACK : ACC_OTHER);
 }
 
 /* Update the tracker, then issue a write back store. */
 JS_REQUIRES_STACK void
-TraceRecorder::set(Value* p, LIns* i, bool demote)
+TraceRecorder::setImpl(void* p, LIns* i, bool demote)
 {
     JS_ASSERT(i != NULL);
     checkForGlobalObjectReallocation();
     tracker.set(p, i);
 
     /*
      * If we are writing to this location for the first time, calculate the
      * offset into the native frame manually. Otherwise just look up the last
      * load or store associated with the same source address (p) and use the
      * same offset/base.
      */
     LIns* x = nativeFrameTracker.get(p);
     if (!x) {
-        if (isGlobal(p))
-            x = writeBack(i, eos_ins, nativeGlobalOffset(p), demote);
+        if (isVoidPtrGlobal(p))
+            x = writeBack(i, eos_ins, nativeGlobalOffset((Value *)p), demote);
         else
-            x = writeBack(i, lirbuf->sp, nativespOffset(p), demote);
+            x = writeBack(i, lirbuf->sp, nativespOffsetImpl(p), demote);
         nativeFrameTracker.set(p, x);
     } else {
 #if defined NANOJIT_64BIT
         JS_ASSERT( x->isop(LIR_stq) || x->isop(LIR_sti) || x->isop(LIR_std));
 #else
         JS_ASSERT( x->isop(LIR_sti) || x->isop(LIR_std));
 #endif
 
@@ -3569,83 +3706,140 @@ TraceRecorder::set(Value* p, LIns* i, bo
             disp = base->oprnd2()->immI();
             base = base->oprnd1();
         } else
 #endif
         disp = x->disp();
 
         JS_ASSERT(base == lirbuf->sp || base == eos_ins);
         JS_ASSERT(disp == ((base == lirbuf->sp)
-                            ? nativespOffset(p)
-                            : nativeGlobalOffset(p)));
+                            ? nativespOffsetImpl(p)
+                            : nativeGlobalOffset((Value *)p)));
 
         writeBack(i, base, disp, demote);
     }
 }
 
+JS_REQUIRES_STACK inline void
+TraceRecorder::set(Value* p, LIns* i, bool demote)
+{
+    return setImpl(p, i, demote);
+}
+
+JS_REQUIRES_STACK void
+TraceRecorder::setFrameObjPtr(JSObject** p, LIns* i, bool demote)
+{
+    JS_ASSERT(isValidFrameObjPtr(p));
+    return setImpl(p, i, demote);
+}
+
 JS_REQUIRES_STACK LIns*
 TraceRecorder::attemptImport(const Value* p)
 {
     if (LIns* i = getFromTracker(p))
         return i;
 
     /* If the variable was not known, it could require a lazy import. */
     CountSlotsVisitor countVisitor(p);
     VisitStackSlots(countVisitor, cx, callDepth);
 
     if (countVisitor.stopped() || size_t(p - cx->fp->slots()) < cx->fp->script->nslots)
         return get(p);
 
     return NULL;
 }
 
-nanojit::LIns*
-TraceRecorder::getFromTracker(const Value* p)
+inline nanojit::LIns*
+TraceRecorder::getFromTrackerImpl(const void* p)
 {
     checkForGlobalObjectReallocation();
     return tracker.get(p);
 }
 
+inline nanojit::LIns*
+TraceRecorder::getFromTracker(const Value* p)
+{
+    return getFromTrackerImpl(p);
+}
+
+JS_REQUIRES_STACK LIns*
+TraceRecorder::getImpl(const void *p)
+{
+    LIns* x = getFromTrackerImpl(p);
+    if (x)
+        return x;
+    if (isVoidPtrGlobal(p)) {
+        unsigned slot = nativeGlobalSlot((const Value *)p);
+        JS_ASSERT(tree->globalSlots->offsetOf(uint16(slot)) != -1);
+        importGlobalSlot(slot);
+    } else {
+        unsigned slot = nativeStackSlotImpl(p);
+        JSValueType type = importTypeMap[slot];
+        JS_ASSERT(type != JSVAL_TYPE_UNINITIALIZED);
+        importImpl(lirbuf->sp, -tree->nativeStackBase + slot * sizeof(jsdouble),
+                   p, type, "stack", slot, cx->fp);
+    }
+    JS_ASSERT(knownImpl(p));
+    return tracker.get(p);
+}
+
 JS_REQUIRES_STACK LIns*
-TraceRecorder::get(const Value* p)
-{
-    LIns* x = getFromTracker(p);
-    if (x)
-        return x;
-    if (isGlobal(p)) {
-        unsigned slot = nativeGlobalSlot(p);
-        JS_ASSERT(tree->globalSlots->offsetOf(uint16(slot)) != -1);
-        importGlobalSlot(slot);
-    } else {
-        unsigned slot = nativeStackSlot(p);
-        JSValueType type = importTypeMap[slot];
-        JS_ASSERT(type != JSVAL_TYPE_UNINITIALIZED);
-        import(lirbuf->sp, -tree->nativeStackBase + slot * sizeof(jsdouble),
-               p, type, "stack", slot, cx->fp);
-    }
-    JS_ASSERT(known(p));
-    return tracker.get(p);
+TraceRecorder::get(const Value *p)
+{
+    return getImpl(p);
+}
+
+#ifdef DEBUG
+bool
+TraceRecorder::isValidFrameObjPtr(JSObject **p)
+{
+    JSStackFrame *fp = cx->fp;
+    for (; fp; fp = fp->down) {
+        if (&fp->scopeChain == p || &fp->argsobj == p)
+            return true;
+    }
+    return false;
+}
+#endif
+
+JS_REQUIRES_STACK LIns*
+TraceRecorder::getFrameObjPtr(JSObject **p)
+{
+    JS_ASSERT(isValidFrameObjPtr(p));
+    return getImpl(p);
 }
 
 JS_REQUIRES_STACK LIns*
 TraceRecorder::addr(Value* p)
 {
     return isGlobal(p)
            ? lir->ins2(LIR_addp, eos_ins, INS_CONSTWORD(nativeGlobalOffset(p)))
            : lir->ins2(LIR_addp, lirbuf->sp,
                        INS_CONSTWORD(nativespOffset(p)));
 }
 
-JS_REQUIRES_STACK bool
-TraceRecorder::known(const Value* p)
+JS_REQUIRES_STACK inline bool
+TraceRecorder::knownImpl(const void* p)
 {
     checkForGlobalObjectReallocation();
     return tracker.has(p);
 }
 
+JS_REQUIRES_STACK inline bool
+TraceRecorder::known(const Value* vp)
+{
+    return knownImpl(vp);
+}
+
+JS_REQUIRES_STACK inline bool
+TraceRecorder::known(JSObject** p)
+{
+    return knownImpl(p);
+}
+
 /*
  * The dslots of the global object are sometimes reallocated by the interpreter.
  * This function check for that condition and re-maps the entries of the tracker
  * accordingly.
  */
 JS_REQUIRES_STACK void
 TraceRecorder::checkForGlobalObjectReallocation()
 {
@@ -3746,36 +3940,45 @@ public:
 
     JSValueType* getTypeMap()
     {
         return mTypeMap;
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(Value *vp, size_t count, JSStackFrame* fp) {
+        /* N.B. vp may actually point to a JSObject*. */
         for (size_t i = 0; i < count; ++i) {
             LIns *ins = mRecorder.get(vp);
             bool isPromote = isPromoteInt(ins);
             if (isPromote && *mTypeMap == JSVAL_TYPE_DOUBLE) {
-                mLir->insStore(mRecorder.get(vp), mLirbuf->sp,
-                                mRecorder.nativespOffset(vp), ACC_STACK);
+                mLir->insStore(ins, mLirbuf->sp,
+                               mRecorder.nativespOffset(vp), ACC_STACK);
 
                 /*
                  * Aggressively undo speculation so the inner tree will compile
                  * if this fails.
                  */
                 JS_TRACE_MONITOR(mCx).oracle->markStackSlotUndemotable(mCx, mSlotnum);
             }
             JS_ASSERT(!(!isPromote && *mTypeMap == JSVAL_TYPE_INT32));
             ++vp;
             ++mTypeMap;
             ++mSlotnum;
         }
         return true;
     }
+
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(JSObject **p, JSStackFrame* fp) {
+        JS_ASSERT(*mTypeMap != JSVAL_TYPE_BOXED);
+        ++mTypeMap;
+        ++mSlotnum;
+        return true;
+    }
 };
 
 /*
  * Promote slots if necessary to match the called tree's type map. This
  * function is infallible and must only be called if we are certain that it is
  * possible to reconcile the types for each slot in the inner and outer trees.
  */
 JS_REQUIRES_STACK void
@@ -3833,36 +4036,42 @@ public:
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(Value *vp, size_t count, JSStackFrame* fp) {
         for (size_t i = 0; i < count; ++i)
             *mTypeMap++ = mRecorder.determineSlotType(vp++);
         return true;
     }
 
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(JSObject **p, JSStackFrame* fp) {
+        *mTypeMap++ = getFrameObjPtrTraceType(*p);
+        return true;
+    }
+
     JSValueType* getTypeMap()
     {
         return mTypeMap;
     }
 };
 
 #if defined JS_JIT_SPEW
 JS_REQUIRES_STACK static void
 TreevisLogExit(JSContext* cx, VMSideExit* exit)
 {
     debug_only_printf(LC_TMTreeVis, "TREEVIS ADDEXIT EXIT=%p TYPE=%s FRAG=%p PC=%p FILE=\"%s\""
                       " LINE=%d OFFS=%d", (void*)exit, getExitName(exit->exitType),
                       (void*)exit->from, (void*)cx->regs->pc, cx->fp->script->filename,
                       js_FramePCToLineNumber(cx, cx->fp), FramePCOffset(cx, cx->fp));
     debug_only_print0(LC_TMTreeVis, " STACK=\"");
     for (unsigned i = 0; i < exit->numStackSlots; i++)
-        debug_only_printf(LC_TMTreeVis, "%c", typeChar[exit->stackTypeMap()[i]]);
+        debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(exit->stackTypeMap()[i]));
     debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\"");
     for (unsigned i = 0; i < exit->numGlobalSlots; i++)
-        debug_only_printf(LC_TMTreeVis, "%c", typeChar[exit->globalTypeMap()[i]]);
+        debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(exit->globalTypeMap()[i]));
     debug_only_print0(LC_TMTreeVis, "\"\n");
 }
 #endif
 
 JS_REQUIRES_STACK VMSideExit*
 TraceRecorder::snapshot(ExitType exitType)
 {
     JSStackFrame* const fp = cx->fp;
@@ -3931,22 +4140,16 @@ TraceRecorder::snapshot(ExitType exitTyp
      * (also at the beginning of a trace branched from such a type guard).
      */
     if (pendingUnboxSlot ||
         (pendingSpecializedNative && (pendingSpecializedNative->flags & JSTN_UNBOX_AFTER))) {
         unsigned pos = stackSlots - 1;
         if (pendingUnboxSlot == cx->regs->sp - 2)
             pos = stackSlots - 2;
         typemap[pos] = JSVAL_TYPE_BOXED;
-    } else if (pendingSpecializedNative && 
-               (pendingSpecializedNative->flags & JSTN_RETURN_NULLABLE_STR)) {
-        typemap[stackSlots - 1] = JSVAL_TYPE_STRORNULL;
-    } else if (pendingSpecializedNative && 
-               (pendingSpecializedNative->flags & JSTN_RETURN_NULLABLE_OBJ)) {
-        typemap[stackSlots - 1] = JSVAL_TYPE_OBJORNULL;
     }
 
     /* Now restore the the original pc (after which early returns are ok). */
     if (resumeAfter) {
         MUST_FLOW_LABEL(restore_pc);
         regs->pc = pc - cs.length;
     } else {
         /*
@@ -4273,20 +4476,23 @@ class SlotMap : public SlotVisitorBase
     struct SlotInfo
     {
         SlotInfo()
           : vp(NULL), promoteInt(false), lastCheck(TypeCheck_Bad)
         {}
         SlotInfo(Value* vp, bool promoteInt)
           : vp(vp), promoteInt(promoteInt), lastCheck(TypeCheck_Bad), type(getCoercedType(*vp))
         {}
+        SlotInfo(JSObject** p)
+          : vp(vp), promoteInt(false), lastCheck(TypeCheck_Bad), type(getFrameObjPtrTraceType(*p))
+        {}
         SlotInfo(Value* vp, JSValueType t)
           : vp(vp), promoteInt(t == JSVAL_TYPE_INT32), lastCheck(TypeCheck_Bad), type(t)
         {}
-        Value           *vp;
+        void            *vp;
         bool            promoteInt;
         TypeCheckResult lastCheck;
         JSValueType     type;
     };
 
     SlotMap(TraceRecorder& rec)
         : mRecorder(rec),
           mCx(rec.cx),
@@ -4368,16 +4574,22 @@ class SlotMap : public SlotVisitorBase
                 promoteInt = mRecorder.importTypeMap[mRecorder.nativeStackSlot(vp)] ==
                              JSVAL_TYPE_INT32;
             }
         }
         slots.add(SlotInfo(vp, promoteInt));
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
+    addSlot(JSObject** p)
+    {
+        slots.add(SlotInfo(p));
+    }
+
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     addSlot(JSValueType t)
     {
         slots.add(SlotInfo(NULL, t));
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     addSlot(Value *vp, JSValueType t)
     {
@@ -4417,49 +4629,49 @@ class SlotMap : public SlotVisitorBase
              * isPromoteInt() is true.  We check this.  
              *
              * Note that getFromTracker() will return NULL if the slot was
              * never used, in which case we don't do the check.  We could
              * instead called mRecorder.get(info.vp) and always check, but
              * get() has side-effects, which is not good in an assertion.
              * Not checking unused slots isn't so bad.
              */
-            LIns* ins = mRecorder.getFromTracker(info.vp);
+            LIns* ins = mRecorder.getFromTrackerImpl(info.vp);
             JS_ASSERT_IF(ins, isPromoteInt(ins));
         } else 
 #endif
         if (info.lastCheck == TypeCheck_Demote) {
             JS_ASSERT(info.type == JSVAL_TYPE_INT32 || info.type == JSVAL_TYPE_DOUBLE);
-            JS_ASSERT(mRecorder.get(info.vp)->isD());
+            JS_ASSERT(mRecorder.getImpl(info.vp)->isD());
 
             /* Never demote this final i2d. */
-            mRecorder.set(info.vp, mRecorder.get(info.vp), false);
+            mRecorder.setImpl(info.vp, mRecorder.getImpl(info.vp), false);
         }
     }
 
   private:
     TypeCheckResult
     checkType(unsigned i, JSValueType t)
     {
         debug_only_printf(LC_TMTracer,
                           "checkType slot %d: interp=%c typemap=%c isNum=%d promoteInt=%d\n",
                           i,
-                          typeChar[slots[i].type],
-                          typeChar[t],
+                          TypeToChar(slots[i].type),
+                          TypeToChar(t),
                           slots[i].type == JSVAL_TYPE_INT32 || slots[i].type == JSVAL_TYPE_DOUBLE,
                           slots[i].promoteInt);
         switch (t) {
           case JSVAL_TYPE_INT32:
             if (slots[i].type != JSVAL_TYPE_INT32 && slots[i].type != JSVAL_TYPE_DOUBLE)
                 return TypeCheck_Bad; /* Not a number? Type mismatch. */
             /* This is always a type mismatch, we can't close a double to an int. */
             if (!slots[i].promoteInt)
                 return TypeCheck_Undemote;
             /* Looks good, slot is an int32, the last instruction should be promotable. */
-            JS_ASSERT_IF(slots[i].vp, hasInt32Repr(*slots[i].vp) && slots[i].promoteInt);
+            JS_ASSERT_IF(slots[i].vp, hasInt32Repr(*(const Value *)slots[i].vp) && slots[i].promoteInt);
             return slots[i].vp ? TypeCheck_Promote : TypeCheck_Okay;
           case JSVAL_TYPE_DOUBLE:
             if (slots[i].type != JSVAL_TYPE_INT32 && slots[i].type != JSVAL_TYPE_DOUBLE)
                 return TypeCheck_Bad; /* Not a number? Type mismatch. */
             if (slots[i].promoteInt)
                 return slots[i].vp ? TypeCheck_Demote : TypeCheck_Bad;
             return TypeCheck_Okay;
           default:
@@ -4486,16 +4698,23 @@ class DefaultSlotMap : public SlotMap
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(Value *vp, size_t count, JSStackFrame* fp)
     {
         for (size_t i = 0; i < count; i++)
             addSlot(&vp[i]);
         return true;
     }
+
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(JSObject** p, JSStackFrame* fp)
+    {
+        addSlot(p);
+        return true;
+    }
 };
 
 JS_REQUIRES_STACK TypeConsensus
 TraceRecorder::selfTypeStability(SlotMap& slotMap)
 {
     debug_only_printf(LC_TMTracer, "Checking type stability against self=%p\n", (void*)fragment);
     TypeConsensus consensus = slotMap.checkTypes(tree);
 
@@ -5398,29 +5617,29 @@ SynthesizeFrame(JSContext* cx, const Fra
         for (Value *v = argv + argc, *end = v + missing; v != end; ++v)
             v->setUndefined();
     } else {
         newfp = stack.getInlineFrame(cx, sp, 0, nslots);
     }
 
     /* Initialize the new stack frame. */
     newfp->callobj = NULL;
-    newfp->setArgsObj(NULL);
+    newfp->argsobj = NULL;
     newfp->script = newscript;
     newfp->fun = fun;
     newfp->argc = argc;
     newfp->argv = argv;
 #ifdef DEBUG
     // Initialize argv[-1] to a known-bogus value so we'll catch it if
     // someone forgets to initialize it later.
-    newfp->argv[-1].setMagic(JS_ARGS_HOLE);
+    newfp->argv[-1].setMagic(JS_THIS_POISON);
 #endif
     newfp->rval.setUndefined();
     newfp->annotation = NULL;
-    newfp->setScopeChainObj(NULL); // will be updated in FlushNativeStackFrame
+    newfp->scopeChain = NULL; // will be updated in FlushNativeStackFrame
     newfp->flags = fi.is_constructing() ? JSFRAME_CONSTRUCTING : 0;
     newfp->blockChain = NULL;
     newfp->thisv.setNull(); // will be updated in FlushNativeStackFrame
     newfp->imacpc = NULL;
     if (newscript->staticLevel < JS_DISPLAY_SIZE) {
         JSStackFrame **disp = &cx->display[newscript->staticLevel];
         newfp->displaySave = *disp;
         *disp = newfp;
@@ -5479,25 +5698,25 @@ SynthesizeSlowNativeFrame(TracerState& s
     JSObject *callee = &state.nativeVp[0].asObject();
     JSFunction *fun = GET_FUNCTION_PRIVATE(cx, callee);
     JS_ASSERT(!fun->isInterpreted() && !fun->isFastNative());
     JS_ASSERT(fun->u.n.extra == 0);
 #endif
 
     fp->imacpc = NULL;
     fp->callobj = NULL;
-    fp->setArgsObj(NULL);
+    fp->argsobj = NULL;
     fp->script = NULL;
     fp->thisv = state.nativeVp[1];
     fp->argc = state.nativeVpLen - 2;
     fp->argv = state.nativeVp + 2;
     fp->fun = GET_FUNCTION_PRIVATE(cx, fp->callee());
     fp->rval.setUndefined();
     fp->annotation = NULL;
-    JS_ASSERT(cx->fp->scopeChainObj());
+    JS_ASSERT(cx->fp->scopeChain);
     fp->scopeChain = cx->fp->scopeChain;
     fp->blockChain = NULL;
     fp->flags = exit->constructing() ? JSFRAME_CONSTRUCTING : 0;
     fp->displaySave = NULL;
 
     state.bailedSlowNativeRegs = *cx->regs;
 
     cx->stack().pushSynthesizedSlowNativeFrame(cx, cs, fp, state.bailedSlowNativeRegs);
@@ -5554,20 +5773,20 @@ RecordTree(JSContext* cx, TreeFragment* 
     AssertTreeIsUnique(tm, f);
 #endif
 #ifdef JS_JIT_SPEW
     debug_only_printf(LC_TMTreeVis, "TREEVIS CREATETREE ROOT=%p PC=%p FILE=\"%s\" LINE=%d OFFS=%d",
                       (void*)f, f->ip, f->treeFileName, f->treeLineNumber,
                       FramePCOffset(cx, cx->fp));
     debug_only_print0(LC_TMTreeVis, " STACK=\"");
     for (unsigned i = 0; i < f->nStackTypes; i++)
-        debug_only_printf(LC_TMTreeVis, "%c", typeChar[f->typeMap[i]]);
+        debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(f->typeMap[i]));
     debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\"");
     for (unsigned i = 0; i < f->nGlobalTypes(); i++)
-        debug_only_printf(LC_TMTreeVis, "%c", typeChar[f->typeMap[f->nStackTypes + i]]);
+        debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(f->typeMap[f->nStackTypes + i]));
     debug_only_print0(LC_TMTreeVis, "\"\n");
 #endif
 
     /* Recording primary trace. */
     return TraceRecorder::startRecorder(cx, NULL, f, f->nStackTypes,
                                         f->globalSlots->length(),
                                         f->typeMap.data(), NULL,
                                         outer, outerArgc, reason,
@@ -5834,17 +6053,17 @@ RecordingIfTrue(bool b)
 /*
  * A postcondition of recordLoopEdge is that if recordLoopEdge does not return
  * MONITOR_RECORDING, the recording has been aborted.
  */
 JS_REQUIRES_STACK MonitorResult
 TraceRecorder::recordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCallCount)
 {
 #ifdef JS_THREADSAFE
-    if (cx->fp->scopeChainObj()->getGlobal()->scope()->title.ownercx != cx) {
+    if (cx->fp->scopeChain->getGlobal()->scope()->title.ownercx != cx) {
         AbortRecording(cx, "Global object not owned by this context");
         return MONITOR_NOT_RECORDING; /* we stay away from shared global objects */
     }
 #endif
 
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
 
     /* Process needFlush and deep abort requests. */
@@ -5857,17 +6076,17 @@ TraceRecorder::recordLoopEdge(JSContext*
     TreeFragment* root = r->fragment->root;
     TreeFragment* first = LookupOrAddLoop(tm, cx->regs->pc, root->globalObj,
                                         root->globalShape, cx->fp->argc);
 
     /*
      * Make sure the shape of the global object still matches (this might flush
      * the JIT cache).
      */
-    JSObject* globalObj = cx->fp->scopeChainObj()->getGlobal();
+    JSObject* globalObj = cx->fp->scopeChain->getGlobal();
     uint32 globalShape = -1;
     SlotList* globalSlots = NULL;
     if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) {
         JS_ASSERT(!tm->recorder);
         return MONITOR_NOT_RECORDING;
     }
 
     debug_only_printf(LC_TMTracer,
@@ -6011,57 +6230,38 @@ TraceRecorder::attemptTreeCall(TreeFragm
           JS_NOT_REACHED("NESTED_EXIT should be replaced by innermost side exit");
       default:
         debug_only_printf(LC_TMTracer, "exit_type=%s\n", getExitName(lr->exitType));
         AbortRecording(cx, "Inner tree not suitable for calling");
         return ARECORD_ABORTED;
     }
 }
 
-#ifdef DEBUG
-static char
-ValueTypeTag(const Value &v)
-{
-    if (v.isInt32()) return 'I';
-    if (v.isDouble()) return 'D';
-    if (v.isString()) return 'S';
-    if (v.isObject()) return v.asObject().isFunction() ? 'F' : 'O';
-    if (v.isBoolean()) return 'B';
-    if (v.isNull()) return 'N';
-    if (v.isUndefined()) return 'U';
-    if (v.isMagic()) return 'M';
-    return '?';
-}
-#endif
-
 static inline bool
-IsEntryTypeCompatible(Value* vp, JSValueType* typep)
+IsEntryTypeCompatible(const Value &v, JSValueType type)
 {
 #ifdef DEBUG
-    char tag = ValueTypeTag(*vp);
-    debug_only_printf(LC_TMTracer, "%c/%c ", tag, typeChar[*typep]);
-#endif
-
-    JSValueType type = *typep;
-    Value v = *vp;
+    char tag = ValueToTypeChar(v);
+    debug_only_printf(LC_TMTracer, "%c/%c ", tag, TypeToChar(type));
+#endif
 
     JS_ASSERT(type <= JSVAL_UPPER_INCL_TYPE_OF_BOXABLE_SET);
-    int32_t _;
-
     switch (type) {
       case JSVAL_TYPE_DOUBLE:
         if (v.isNumber())
             return true;
         debug_only_printf(LC_TMTracer, "double != tag%c ", tag);
         break;
-      case JSVAL_TYPE_INT32:
+      case JSVAL_TYPE_INT32: {
+        int32_t _;
         if (v.isInt32() || (v.isDouble() && JSDOUBLE_IS_INT32(v.asDouble(), &_)))
             return true;
         debug_only_printf(LC_TMTracer, "int != tag%c ", tag);
         break;
+      }
       case JSVAL_TYPE_UNDEFINED:
         if (v.isUndefined())
             return true;
         debug_only_printf(LC_TMTracer, "undefined != tag%c ", tag);
         break;
       case JSVAL_TYPE_BOOLEAN:
         if (v.isBoolean())
             return true;
@@ -6118,46 +6318,56 @@ public:
         mTypeMap(typeMap),
         mStackSlotNum(0),
         mOk(true)
     {}
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     visitGlobalSlot(Value *vp, unsigned n, unsigned slot) {
         debug_only_printf(LC_TMTracer, "global%d=", n);
-        if (!IsEntryTypeCompatible(vp, mTypeMap)) {
+        if (!IsEntryTypeCompatible(*vp, *mTypeMap)) {
             mOk = false;
         } else if (!isPromoteInt(mRecorder.get(vp)) && *mTypeMap == JSVAL_TYPE_INT32) {
             mOracle->markGlobalSlotUndemotable(mCx, slot);
             mOk = false;
         } else if (vp->isInt32() && *mTypeMap == JSVAL_TYPE_DOUBLE) {
             mOracle->markGlobalSlotUndemotable(mCx, slot);
         }
         mTypeMap++;
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(Value *vp, size_t count, JSStackFrame* fp) {
         for (size_t i = 0; i < count; ++i) {
             debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), unsigned(i));
-            if (!IsEntryTypeCompatible(vp, mTypeMap)) {
+            if (!IsEntryTypeCompatible(*vp, *mTypeMap)) {
                 mOk = false;
             } else if (!isPromoteInt(mRecorder.get(vp)) && *mTypeMap == JSVAL_TYPE_INT32) {
                 mOracle->markStackSlotUndemotable(mCx, mStackSlotNum);
                 mOk = false;
             } else if (vp->isInt32() && *mTypeMap == JSVAL_TYPE_DOUBLE) {
                 mOracle->markStackSlotUndemotable(mCx, mStackSlotNum);
             }
             vp++;
             mTypeMap++;
             mStackSlotNum++;
         }
         return true;
     }
 
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(JSObject **p, JSStackFrame* fp) {
+        debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), 0);
+        if (!IsEntryTypeCompatible(ObjectOrNullTag(*p), *mTypeMap))
+            mOk = false;
+        mTypeMap++;
+        mStackSlotNum++;
+        return true;
+    }
+
     bool isOk() {
         return mOk;
     }
 };
 
 JS_REQUIRES_STACK TreeFragment*
 TraceRecorder::findNestedCompatiblePeer(TreeFragment* f)
 {
@@ -6200,35 +6410,41 @@ class CheckEntryTypeVisitor : public Slo
     bool mOk;
     JSValueType *mTypeMap;
 public:
     CheckEntryTypeVisitor(JSValueType *typeMap) :
         mOk(true),
         mTypeMap(typeMap)
     {}
 
-    JS_ALWAYS_INLINE void checkSlot(Value *vp, char const *name, int i) {
+    JS_ALWAYS_INLINE void checkSlot(const Value &v, char const *name, int i) {
         debug_only_printf(LC_TMTracer, "%s%d=", name, i);
         JS_ASSERT(*(uint8_t*)mTypeMap != 0xCD);
-        mOk = IsEntryTypeCompatible(vp, mTypeMap++);
+        mOk = IsEntryTypeCompatible(v, *mTypeMap++);
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     visitGlobalSlot(Value *vp, unsigned n, unsigned slot) {
         if (mOk)
-            checkSlot(vp, "global", n);
+            checkSlot(*vp, "global", n);
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(Value *vp, size_t count, JSStackFrame* fp) {
         for (size_t i = 0; i < count; ++i) {
             if (!mOk)
                 break;
-            checkSlot(vp++, stackSlotKind(), i);
-        }
+            checkSlot(*vp++, stackSlotKind(), i);
+        }
+        return mOk;
+    }
+
+    JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(JSObject **p, JSStackFrame *fp) {
+        checkSlot(ObjectOrNullTag(*p), stackSlotKind(), 0);
         return mOk;
     }
 
     bool isOk() {
         return mOk;
     }
 };
 
@@ -6366,17 +6582,17 @@ ExecuteTrace(JSContext* cx, Fragment* f,
     JS_ASSERT(!cx->bailExit);
     return (VMSideExit*)rec->exit;
 }
 
 /* Check whether our assumptions about the incoming scope-chain are upheld. */
 static JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
 ScopeChainCheck(JSContext* cx, TreeFragment* f)
 {
-    JS_ASSERT(f->globalObj == cx->fp->scopeChainObj()->getGlobal());
+    JS_ASSERT(f->globalObj == cx->fp->scopeChain->getGlobal());
 
     /*
      * The JIT records and expects to execute with two scope-chain
      * assumptions baked-in:
      *
      *   1. That the bottom of the scope chain is global, in the sense of
      *      JSCLASS_IS_GLOBAL.
      *
@@ -6384,17 +6600,17 @@ ScopeChainCheck(JSContext* cx, TreeFragm
      *      "unusual" native objects such as HTML forms or other funny
      *      things.
      *
      * #2 is checked here while following the scope-chain links, via
      * js_IsCacheableNonGlobalScope, which consults a whitelist of known
      * class types; once a global is found, it's checked for #1. Failing
      * either check causes an early return from execution.
      */
-    JSObject* child = cx->fp->scopeChainObj();
+    JSObject* child = cx->fp->scopeChain;
     while (JSObject* parent = child->getParent()) {
         if (!js_IsCacheableNonGlobalScope(child)) {
             debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n");
             Blacklist((jsbytecode*) f->root->ip);
             return false;
         }
         child = parent;
     }
@@ -6873,17 +7089,17 @@ MonitorLoopEdge(JSContext* cx, uintN& in
         }
     }
     JS_ASSERT(!tm->recorder);
 
     /*
      * Make sure the shape of the global object still matches (this might flush
      * the JIT cache).
      */
-    JSObject* globalObj = cx->fp->scopeChainObj()->getGlobal();
+    JSObject* globalObj = cx->fp->scopeChain->getGlobal();
     uint32 globalShape = -1;
     SlotList* globalSlots = NULL;
 
     if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) {
         Backoff(cx, cx->regs->pc);
         return MONITOR_NOT_RECORDING;
     }
 
@@ -7789,17 +8005,17 @@ TraceRecorder::updateAtoms(JSScript *scr
 
 /*
  * Generate LIR to compute the scope chain.
  */
 JS_REQUIRES_STACK LIns*
 TraceRecorder::scopeChain()
 {
     return cx->fp->callee()
-           ? get(&cx->fp->scopeChain)
+           ? getFrameObjPtr(&cx->fp->scopeChain)
            : entryScopeChain();
 }
 
 /*
  * Generate LIR to compute the scope chain on entry to the trace. This is
  * generally useful only for getting to the global object, because only
  * the global object is guaranteed to be present.
  */
@@ -7845,17 +8061,17 @@ JS_DEFINE_CALLINFO_4(extern, UINT32, Get
  *
  *     vp           the address of the current property value
  *     ins          LIR instruction representing the property value on trace
  *     NameResult   describes how to look up name; see comment for NameResult in jstracer.h
  */
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::scopeChainProp(JSObject* chainHead, Value*& vp, LIns*& ins, NameResult& nr)
 {
-    JS_ASSERT(chainHead == cx->fp->scopeChainObj());
+    JS_ASSERT(chainHead == cx->fp->scopeChain);
     JS_ASSERT(chainHead != globalObj);
 
     TraceMonitor &localtm = *traceMonitor;
 
     JSAtom* atom = atoms[GET_INDEX(cx->regs->pc)];
     JSObject* obj2;
     JSProperty* prop;
     JSObject *obj = chainHead;
@@ -9726,16 +9942,34 @@ TraceRecorder::box_value_into_alloc(cons
     LIns *alloc_ins = lir->insAlloc(sizeof(Value));
     box_value_into(v, v_ins, alloc_ins, 0, ACC_OTHER);
     return alloc_ins;
 }
 
 #endif  /* JS_BITS_PER_WORD */
 
 LIns*
+TraceRecorder::stobj_get_parent(nanojit::LIns* obj_ins)
+{
+    return stobj_get_fslot_ptr(obj_ins, JSSLOT_PARENT);
+}
+
+LIns*
+TraceRecorder::stobj_get_private(nanojit::LIns* obj_ins)
+{
+    return stobj_get_fslot_ptr(obj_ins, JSSLOT_PRIVATE);
+}
+
+LIns*
+TraceRecorder::stobj_get_proto(nanojit::LIns* obj_ins)
+{
+    return lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, proto), ACC_OTHER);
+}
+
+LIns*
 TraceRecorder::is_string_id(LIns *id_ins)
 {
     return lir->insEqP_0(lir->ins2(LIR_andp, id_ins, INS_CONSTWORD(JSID_TYPE_MASK)));
 }
 
 LIns *
 TraceRecorder::unbox_string_id(LIns *id_ins)
 {
@@ -9947,17 +10181,17 @@ TraceRecorder::clearFrameSlotsFromTracke
      * This doesn't do layout arithmetic, but it must clear out all the slots defined as
      * imported by VisitFrameSlots.
      */
     if (fp->argv) {
         vp = &fp->argv[-2];
         vpstop = &fp->argv[argSlots(fp)];
         while (vp < vpstop)
             which.set(vp++, (LIns*)0);
-        which.set(&fp->argsval, (LIns*)0);
+        which.set(&fp->argsobj, (LIns*)0);
         which.set(&fp->scopeChain, (LIns*)0);
     }
     vp = &fp->slots()[0];
     vpstop = &fp->slots()[nslots];
     while (vp < vpstop)
         which.set(vp++, (LIns*)0);
 }
 
@@ -9989,17 +10223,17 @@ TraceRecorder::clearCurrentFrameSlotsFro
 /*
  * If we have created an |arguments| object for the frame, we must copy the
  * argument values into the object as properties in case it is used after
  * this frame returns.
  */
 JS_REQUIRES_STACK void
 TraceRecorder::putActivationObjects()
 {
-    bool have_args = cx->fp->argsObj() && cx->fp->argc;
+    bool have_args = cx->fp->argsobj && cx->fp->argc;
     bool have_call = cx->fp->fun && JSFUN_HEAVYWEIGHT_TEST(cx->fp->fun->flags) && cx->fp->fun->countArgsAndVars();
 
     if (!have_args && !have_call)
         return;
 
     int nargs = have_args ? argSlots(cx->fp) : cx->fp->fun->nargs;
 
     LIns* args_ins;
@@ -10008,34 +10242,34 @@ TraceRecorder::putActivationObjects()
         for (int i = 0; i < nargs; ++i) {
             box_value_into(cx->fp->argv[i], get(&cx->fp->argv[i]), args_ins, i * sizeof(Value), ACC_OTHER);
         }
     } else {
         args_ins = INS_CONSTPTR(0);
     }
 
     if (have_args) {
-        LIns* argsobj_ins = get(&cx->fp->argsval);
+        LIns* argsobj_ins = getFrameObjPtr(&cx->fp->argsobj);
         LIns* args[] = { args_ins, argsobj_ins, cx_ins };
         lir->insCall(&js_PutArguments_ci, args);
     }
 
     if (have_call) {
         int nslots = cx->fp->fun->countVars();
         LIns* slots_ins;
         if (nslots) {
             slots_ins = lir->insAlloc(sizeof(Value) * nslots);
             for (int i = 0; i < nslots; ++i) {
                 box_value_into(cx->fp->slots()[i], get(&cx->fp->slots()[i]), slots_ins, i * sizeof(Value), ACC_OTHER);
             }
         } else {
             slots_ins = INS_CONSTPTR(0);
         }
 
-        LIns* scopeChain_ins = get(&cx->fp->scopeChain);
+        LIns* scopeChain_ins = getFrameObjPtr(&cx->fp->scopeChain);
         LIns* args[] = { slots_ins, INS_CONST(nslots), args_ins,
                          INS_CONST(cx->fp->fun->nargs), scopeChain_ins, cx_ins };
         lir->insCall(&js_PutCallObjectOnTrace_ci, args);
     }
 }
 
 static JS_REQUIRES_STACK inline bool
 IsTraceableRecursion(JSContext *cx)
@@ -10095,18 +10329,18 @@ TraceRecorder::record_EnterFrame(uintN& 
 
     Value* vp = &fp->argv[fp->argc];
     Value* vpstop = vp + ptrdiff_t(fp->fun->nargs) - ptrdiff_t(fp->argc);
     for (; vp < vpstop; ++vp) {
         nativeFrameTracker.set(vp, NULL);
         set(vp, void_ins);
     }
 
-    nativeFrameTracker.set(&fp->argsval, NULL);
-    set(&fp->argsval, INS_NULL());
+    nativeFrameTracker.set(&fp->argsobj, NULL);
+    setFrameObjPtr(&fp->argsobj, INS_NULL());
     nativeFrameTracker.set(&fp->scopeChain, NULL);
 
     vp = fp->slots();
     vpstop = vp + fp->script->nfixed;
     for (; vp < vpstop; ++vp) {
         nativeFrameTracker.set(vp, NULL);
         set(vp, void_ins);
     }
@@ -10117,30 +10351,30 @@ TraceRecorder::record_EnterFrame(uintN& 
         nativeFrameTracker.set(vp, NULL);
 
     LIns* callee_ins = get(&cx->fp->argv[-2]);
     LIns* scopeChain_ins = stobj_get_parent(callee_ins);
 
     if (cx->fp->fun && JSFUN_HEAVYWEIGHT_TEST(cx->fp->fun->flags)) {
         // We need to make sure every part of the frame is known to the tracker
         // before taking a snapshot.
-        set(&fp->scopeChain, INS_NULL());
+        setFrameObjPtr(&fp->scopeChain, INS_NULL());
 
         if (js_IsNamedLambda(cx->fp->fun))
             RETURN_STOP_A("can't call named lambda heavyweight on trace");
 
         LIns* fun_ins = INS_CONSTPTR(cx->fp->fun);
 
         LIns* args[] = { scopeChain_ins, callee_ins, fun_ins, cx_ins };
         LIns* call_ins = lir->insCall(&js_CreateCallObjectOnTrace_ci, args);
         guard(false, lir->insEqP_0(call_ins), snapshot(OOM_EXIT));
 
-        set(&fp->scopeChain, call_ins);
+        setFrameObjPtr(&fp->scopeChain, call_ins);
     } else {
-        set(&fp->scopeChain, scopeChain_ins);
+        setFrameObjPtr(&fp->scopeChain, scopeChain_ins);
     }
 
     /*
      * Check for recursion. This is a special check for recursive cases that can be
      * a trace-tree, just like a loop. If recursion acts weird, for example
      * differing argc or existence of an imacpc, it's not something this code is
      * concerned about. That should pass through below to not regress pre-recursion
      * functionality.
@@ -10186,17 +10420,17 @@ TraceRecorder::record_EnterFrame(uintN& 
             }
         }
         return ARECORD_CONTINUE;
     } else if (f) {
         /*
          * Make sure the shape of the global object still matches (this might
          * flush the JIT cache).
          */
-        JSObject* globalObj = cx->fp->scopeChainObj()->getGlobal();
+        JSObject* globalObj = cx->fp->scopeChain->getGlobal();
         uint32 globalShape = -1;
         SlotList* globalSlots = NULL;
         if (!CheckGlobalObjectShape(cx, traceMonitor, globalObj, &globalShape, &globalSlots))
             return ARECORD_ABORTED;
         return attemptTreeCall(f, inlineCallCount);
     }
 
    return ARECORD_CONTINUE;
@@ -10342,20 +10576,20 @@ TraceRecorder::newArguments(LIns* callee
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_ARGUMENTS()
 {
     if (cx->fp->flags & JSFRAME_OVERRIDE_ARGS)
         RETURN_STOP_A("Can't trace |arguments| if |arguments| is assigned to");
 
-    LIns* a_ins = get(&cx->fp->argsval);
+    LIns* a_ins = getFrameObjPtr(&cx->fp->argsobj);
     LIns* args_ins;
     LIns* callee_ins = get(&cx->fp->argv[-2]);
-    if (a_ins->opcode() == LIR_immi) {
+    if (a_ins->isImmP()) {
         // |arguments| is set to 0 by EnterFrame on this trace, so call to create it.
         args_ins = newArguments(callee_ins);
     } else {
         // Generate LIR to create arguments only if it has not already been created.
 
         LIns* mem_ins = lir->insAlloc(sizeof(JSObject *));
 
         LIns* br1 = lir->insBranch(LIR_jt, lir->insEqP_0(a_ins), NULL);
@@ -10370,17 +10604,17 @@ TraceRecorder::record_JSOP_ARGUMENTS()
 
         LIns* label2 = lir->ins0(LIR_label);
         br2->setTarget(label2);
 
         args_ins = lir->insLoad(LIR_ldp, mem_ins, 0, ACC_OTHER);
     }
 
     stack(0, args_ins);
-    set(&cx->fp->argsval, args_ins);
+    setFrameObjPtr(&cx->fp->argsobj, args_ins);
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_DUP()
 {
     stack(0, get(&stackval(-1)));
     return ARECORD_CONTINUE;
@@ -11160,17 +11394,17 @@ TraceRecorder::callNative(uintN argc, JS
         /*
          * For fast natives, 'null' or primitives are fine as as 'this' value.
          * For slow natives we have to ensure the object is substituted for the
          * appropriate global object or boxed object value. JSOP_NEW allocates its
          * own object so it's guaranteed to have a valid 'this' value.
          */
         if (!(fun->flags & JSFUN_FAST_NATIVE)) {
             if (vp[1].isNull()) {
-                JSObject* thisObj = ComputeThisObjectFromVp(cx, vp + 2);
+                JSObject* thisObj = ComputeThisFromVp(cx, vp + 2);
                 if (!thisObj)
                     RETURN_ERROR("error in js_ComputeGlobalThis");
                 this_ins = INS_CONSTOBJ(thisObj);
             } else if (!vp[1].isObject()) {
                 RETURN_STOP("slow native(primitive, args)");
             } else {
                 if (vp[1].asObject().hasClass(&js_WithClass))
                     RETURN_STOP("can't trace slow native invocation on With object");
@@ -11373,17 +11607,17 @@ DeleteStrKey(JSContext* cx, JSObject* ob
 JS_DEFINE_CALLINFO_3(extern, BOOL_FAIL, DeleteStrKey, CONTEXT, OBJECT, STRING, 0, ACC_STORE_ANY)
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_DELPROP()
 {
     Value& lval = stackval(-1);
     if (lval.isPrimitive())
         RETURN_STOP_A("JSOP_DELPROP on primitive base expression");
-    if (JSVAL_TO_OBJECT(lval) == globalObj)
+    if (&lval.asObject() == globalObj)
         RETURN_STOP_A("JSOP_DELPROP on global property");
 
     JSAtom* atom = atoms[GET_INDEX(cx->regs->pc)];
 
     enterDeepBailCall();
     LIns* args[] = { INS_ATOM(atom), get(&lval), cx_ins };
     LIns* rval_ins = lir->insCall(&DeleteStrKey_ci, args);
 
@@ -11398,17 +11632,17 @@ TraceRecorder::record_JSOP_DELPROP()
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_DELELEM()
 {
     Value& lval = stackval(-2);
     if (lval.isPrimitive())
         RETURN_STOP_A("JSOP_DELELEM on primitive base expression");
-    if (JSVAL_TO_OBJECT(lval) == globalObj)
+    if (&lval.asObject() == globalObj)
         RETURN_STOP_A("JSOP_DELELEM on global property");
 
     Value& idx = stackval(-1);
     LIns* rval_ins;
 
     enterDeepBailCall();
     if (hasInt32Repr(idx)) {
         LIns* args[] = { makeNumberInt32(get(&idx)), get(&lval), cx_ins };
@@ -11438,16 +11672,18 @@ TraceRecorder::record_JSOP_TYPEOF()
     if (r.isString()) {
         type = INS_ATOM(cx->runtime->atomState.typeAtoms[JSTYPE_STRING]);
     } else if (r.isNumber()) {
         type = INS_ATOM(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
     } else if (r.isUndefined()) {
         type = INS_ATOM(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
     } else if (r.isBoolean()) {
         type = INS_ATOM(cx->runtime->atomState.typeAtoms[JSTYPE_BOOLEAN]);
+    } else if (r.isNull()) {
+        type = INS_ATOM(cx->runtime->atomState.typeAtoms[JSTYPE_OBJECT]);
     } else {
         if (r.asObject().isFunction()) {
             type = INS_ATOM(cx->runtime->atomState.typeAtoms[JSTYPE_FUNCTION]);
         } else {
             LIns* args[] = { get(&r), cx_ins };
             type = lir->insCall(&js_TypeOfObject_ci, args);
         }
     }
@@ -12654,17 +12890,17 @@ JS_REQUIRES_STACK AbortableRecordingStat
 TraceRecorder::record_JSOP_SETELEM()
 {
     return setElem(-3, -2, -1);
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_CALLNAME()
 {
-    JSObject* obj = cx->fp->scopeChainObj();
+    JSObject* obj = cx->fp->scopeChain;
     if (obj != globalObj) {
         Value* vp;
         LIns* ins;
         NameResult nr;
         CHECK_STATUS_A(scopeChainProp(obj, vp, ins, nr));
         stack(0, ins);
         stack(1, INS_CONSTOBJ(globalObj));
         return ARECORD_CONTINUE;
@@ -12910,17 +13146,17 @@ TraceRecorder::guardArguments(JSObject *
 
     JSStackFrame *afp = frameIfInRange(obj, depthp);
     if (!afp)
         return NULL;
 
     VMSideExit *exit = snapshot(MISMATCH_EXIT);
     guardClass(obj_ins, &js_ArgumentsClass, exit, ACC_READONLY);
 
-    LIns* args_ins = get(&afp->argsval);
+    LIns* args_ins = getFrameObjPtr(&afp->argsobj);
     LIns* cmp = lir->ins2(LIR_eqp, args_ins, obj_ins);
     lir->insGuard(LIR_xf, cmp, createGuardRecord(exit));
     return afp;
 }
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, bool constructing)
 {
@@ -12972,17 +13208,17 @@ TraceRecorder::interpretedFunctionCall(V
     if (!fi)
         RETURN_STOP("out of memory");
     lir->insStore(INS_CONSTPTR(fi), lirbuf->rp, callDepth * sizeof(FrameInfo*), ACC_RSTACK);
 
 #if defined JS_JIT_SPEW
     debug_only_printf(LC_TMTracer, "iFC frameinfo=%p, stack=%d, map=", (void*)fi,
                       fi->callerHeight);
     for (unsigned i = 0; i < fi->callerHeight; i++)
-        debug_only_printf(LC_TMTracer, "%c", typeChar[fi->get_typemap()[i]]);
+        debug_only_printf(LC_TMTracer, "%c", TypeToChar(fi->get_typemap()[i]));
     debug_only_print0(LC_TMTracer, "\n");
 #endif
 
     updateAtoms(fun->u.i.script);
     return RECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
@@ -13171,21 +13407,16 @@ TraceRecorder::record_NativeCallComplete
     if (pendingSpecializedNative->flags & JSTN_UNBOX_AFTER) {
         /*
          * If we side exit on the unboxing code due to a type change, make sure that the boxed
          * value is actually currently associated with that location, and that we are talking
          * about the top of the stack here, which is where we expected boxed values.
          */
         JS_ASSERT(&v == &cx->regs->sp[-1] && get(&v) == v_ins);
         set(&v, unbox_value(v, native_rval_ins, 0, snapshot(BRANCH_EXIT)));
-    } else if (pendingSpecializedNative->flags &
-               (JSTN_RETURN_NULLABLE_STR | JSTN_RETURN_NULLABLE_OBJ)) {
-            guard(v.isNull(),
-                  addName(lir->insEqP_0(v_ins), "guard(nullness)"),
-                  BRANCH_EXIT);
     } else if (JSTN_ERRTYPE(pendingSpecializedNative) == FAIL_NEG) {
         /* Already added i2d in functionCall. */
         JS_ASSERT(v.isNumber());
     } else {
         /* Convert the result to double if the builtin returns int32. */
         if (v.isNumber() &&
             pendingSpecializedNative->builtin->returnType() == ARGTYPE_I) {
             set(&v, lir->ins1(LIR_i2d, v_ins));
@@ -13195,17 +13426,17 @@ TraceRecorder::record_NativeCallComplete
     // We'll null pendingSpecializedNative in monitorRecording, on the next op
     // cycle.  There must be a next op since the stack is non-empty.
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::name(Value*& vp, LIns*& ins, NameResult& nr)
 {
-    JSObject* obj = cx->fp->scopeChainObj();
+    JSObject* obj = cx->fp->scopeChain;
     if (obj != globalObj)
         return scopeChainProp(obj, vp, ins, nr);
 
     /* Can't use prop here, because we don't want unboxing from global slots. */
     LIns* obj_ins = INS_CONSTOBJ(globalObj);
     uint32 slot;
 
     JSObject* obj2;
@@ -14062,17 +14293,17 @@ TraceRecorder::unboxNextValue(LIns* &v_i
         NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
 
         LIns *ni_ins = stobj_get_const_private_ptr(iterobj_ins);
         LIns *cursor_ins = addName(lir->insLoad(LIR_ldp, ni_ins, offsetof(NativeIterator, props_cursor), ACC_OTHER), "cursor");
 
         /* Emit code to stringify the id if necessary. */
         if (!(((NativeIterator *) iterobj->getPrivate())->flags & JSITER_FOREACH)) {
             /* Read the next id from the iterator. */
-            jsid id = ni->currentId();
+            jsid id = *ni->currentKey();
             LIns *id_ins = addName(lir->insLoad(LIR_ldp, cursor_ins, 0, ACC_OTHER), "id");
 
             /*
              * Most iterations over object properties never have to actually deal with
              * any numeric properties, so we guard here instead of branching.
              */
             guard(JSID_IS_STRING(id), is_string_id(id_ins), snapshot(BRANCH_EXIT));
 
@@ -14086,17 +14317,17 @@ TraceRecorder::unboxNextValue(LIns* &v_i
                 v_ins = lir->insCall(&js_IntToString_ci, args);
                 guard(false, lir->insEqP_0(v_ins), OOM_EXIT);
             }
 
             /* Increment the cursor by one jsid and store it back. */
             cursor_ins = lir->ins2(LIR_addp, cursor_ins, INS_CONSTWORD(sizeof(jsid)));
         } else {
             /* Read the next value from the iterator. */
-            Value v = ni->currentValue();
+            Value v = *ni->currentValue();
             v_ins = unbox_value(v, cursor_ins, 0, snapshot(BRANCH_EXIT));
 
             /* Increment the cursor by one Value and store it back. */
             cursor_ins = lir->ins2(LIR_addp, cursor_ins, INS_CONSTWORD(sizeof(Value)));
         }
 
         lir->insStore(LIR_stp, cursor_ins, ni_ins, offsetof(NativeIterator, props_cursor), ACC_OTHER);
     } else {
@@ -14269,17 +14500,17 @@ TraceRecorder::traverseScopeChain(JSObje
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_BINDNAME()
 {
     JSStackFrame* const fp = cx->fp;
     JSObject *obj;
 
     if (!fp->fun) {
-        obj = fp->scopeChainObj();
+        obj = fp->scopeChain;
 
 #ifdef DEBUG
         JSStackFrame *fp2 = fp;
 #endif
 
         // In global code, fp->scopeChain can only contain blocks whose values
         // are still on the stack.  We never use BINDNAME to refer to these.
         while (obj->getClass() == &js_BlockClass) {
@@ -14330,17 +14561,17 @@ TraceRecorder::record_JSOP_BINDNAME()
         return ARECORD_CONTINUE;
     }
     LIns *obj_ins = stobj_get_parent(get(callee));
 
     // Find the target object.
     JSAtom *atom = atoms[GET_INDEX(cx->regs->pc)];
     jsid id = ATOM_TO_JSID(atom);
     JSContext *localCx = cx;
-    JSObject *obj2 = js_FindIdentifierBase(cx, fp->scopeChainObj(), id);
+    JSObject *obj2 = js_FindIdentifierBase(cx, fp->scopeChain, id);
     if (!obj2)
         RETURN_ERROR_A("error in js_FindIdentifierBase");
     if (!TRACE_RECORDER(localCx))
         return ARECORD_ABORTED;
     if (obj2 != globalObj && obj2->getClass() != &js_CallClass)
         RETURN_STOP_A("BINDNAME on non-global, non-call object");
 
     // Generate LIR to get to the target object from the start object.
@@ -14750,19 +14981,19 @@ TraceRecorder::record_JSOP_ARGCNT()
         RETURN_STOP_A("can't trace heavyweight JSOP_ARGCNT");
 
     // argc is fixed on trace, so ideally we would simply generate LIR for
     // constant argc. But the user can mutate arguments.length in the
     // interpreter, so we have to check for that in the trace entry frame.
     // We also have to check that arguments.length has not been mutated
     // at record time, because if so we will generate incorrect constant
     // LIR, which will assert in alu().
-    if (cx->fp->argsObj() && cx->fp->argsObj()->isArgsLengthOverridden())
+    if (cx->fp->argsobj && cx->fp->argsobj->isArgsLengthOverridden())
         RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
-    LIns *a_ins = get(&cx->fp->argsval);
+    LIns *a_ins = getFrameObjPtr(&cx->fp->argsobj);
     if (callDepth == 0) {
         LIns *br = lir->insBranch(LIR_jt, lir->insEqP_0(a_ins), NULL);
         guardArgsLengthNotAssigned(a_ins);
         LIns *label = lir->ins0(LIR_label);
         br->setTarget(label);
     }
     stack(0, lir->insImmD(cx->fp->argc));
     return ARECORD_CONTINUE;
@@ -15731,32 +15962,32 @@ DumpPeerStability(TraceMonitor* tm, cons
 
     for (f = LookupLoop(tm, ip, globalObj, globalShape, argc); f != NULL; f = f->peer) {
         if (!f->code())
             continue;
         debug_only_printf(LC_TMRecorder, "Stability of fragment %p:\nENTRY STACK=", (void*)f);
         if (looped)
             JS_ASSERT(f->nStackTypes == length);
         for (unsigned i = 0; i < f->nStackTypes; i++)
-            debug_only_printf(LC_TMRecorder, "%c", typeChar[f->stackTypeMap()[i]]);
+            debug_only_printf(LC_TMRecorder, "%c", TypeToChar(f->stackTypeMap()[i]));
         debug_only_print0(LC_TMRecorder, " GLOBALS=");
         for (unsigned i = 0; i < f->nGlobalTypes(); i++)
-            debug_only_printf(LC_TMRecorder, "%c", typeChar[f->globalTypeMap()[i]]);
+            debug_only_printf(LC_TMRecorder, "%c", TypeToChar(f->globalTypeMap()[i]));
         debug_only_print0(LC_TMRecorder, "\n");
         UnstableExit* uexit = f->unstableExits;
         while (uexit != NULL) {
             debug_only_print0(LC_TMRecorder, "EXIT  ");
             JSValueType* m = uexit->exit->fullTypeMap();
             debug_only_print0(LC_TMRecorder, "STACK=");
             for (unsigned i = 0; i < uexit->exit->numStackSlots; i++)
-                debug_only_printf(LC_TMRecorder, "%c", typeChar[m[i]]);
+                debug_only_printf(LC_TMRecorder, "%c", TypeToChar(m[i]));
             debug_only_print0(LC_TMRecorder, " GLOBALS=");
             for (unsigned i = 0; i < uexit->exit->numGlobalSlots; i++) {
                 debug_only_printf(LC_TMRecorder, "%c",
-                                  typeChar[m[uexit->exit->numStackSlots + i]]);
+                                  TypeToChar(m[uexit->exit->numStackSlots + i]));
             }
             debug_only_print0(LC_TMRecorder, "\n");
             uexit = uexit->next;
         }
         length = f->nStackTypes;
         looped = true;
     }
 }
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1044,22 +1044,28 @@ class TraceRecorder
 
     JS_REQUIRES_STACK unsigned findUndemotesInTypemaps(const TypeMap& typeMap, LinkableFragment* f,
                             Queue<unsigned>& undemotes);
 
     JS_REQUIRES_STACK void assertDownFrameIsConsistent(VMSideExit* anchor, FrameInfo* fi);
 
     JS_REQUIRES_STACK void captureStackTypes(unsigned callDepth, JSValueType* typeMap);
 
+    bool isVoidPtrGlobal(const void* p) const;
     bool isGlobal(const Value* p) const;
     ptrdiff_t nativeGlobalSlot(const Value *p) const;
-    ptrdiff_t nativeGlobalOffset(Value* p) const;
+    ptrdiff_t nativeGlobalOffset(const Value* p) const;
+    JS_REQUIRES_STACK ptrdiff_t nativeStackOffsetImpl(const void* p) const;
     JS_REQUIRES_STACK ptrdiff_t nativeStackOffset(const Value* p) const;
+    JS_REQUIRES_STACK ptrdiff_t nativeStackSlotImpl(const void* p) const;
     JS_REQUIRES_STACK ptrdiff_t nativeStackSlot(const Value* p) const;
-    JS_REQUIRES_STACK ptrdiff_t nativespOffset(Value* p) const;
+    JS_REQUIRES_STACK ptrdiff_t nativespOffsetImpl(const void* p) const;
+    JS_REQUIRES_STACK ptrdiff_t nativespOffset(const Value* p) const;
+    JS_REQUIRES_STACK void importImpl(nanojit::LIns* base, ptrdiff_t offset, const void* p, JSValueType t,
+                                      const char *prefix, uintN index, JSStackFrame *fp);
     JS_REQUIRES_STACK void import(nanojit::LIns* base, ptrdiff_t offset, const Value* p, JSValueType t,
                                   const char *prefix, uintN index, JSStackFrame *fp);
     JS_REQUIRES_STACK void import(TreeFragment* tree, nanojit::LIns* sp, unsigned stackSlots,
                                   unsigned callDepth, unsigned ngslots, JSValueType* typeMap);
     void trackNativeStackUse(unsigned slots);
 
     JS_REQUIRES_STACK bool isValidSlot(JSScope* scope, JSScopeProperty* sprop);
     JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot);
@@ -1072,31 +1078,44 @@ class TraceRecorder
     JS_REQUIRES_STACK nanojit::LIns* slurpNonDoubleObjectSlot(nanojit::LIns* val_ins, ptrdiff_t offset,
                                                               JSValueType type, VMSideExit* exit);
     JS_REQUIRES_STACK nanojit::LIns* slurpObjectSlot(nanojit::LIns* val_ins, ptrdiff_t offset,
                                                      JSValueType type, VMSideExit* exit);
     JS_REQUIRES_STACK nanojit::LIns* slurpDoubleSlot(nanojit::LIns* val_ins, ptrdiff_t offset,
                                                      VMSideExit* exit);
     JS_REQUIRES_STACK nanojit::LIns* slurpSlot(nanojit::LIns* val_ins, ptrdiff_t offset, Value* vp, VMSideExit* exit);
     JS_REQUIRES_STACK void slurpSlot(nanojit::LIns* val_ins, ptrdiff_t offset, Value* vp, SlurpInfo* info);
+    JS_REQUIRES_STACK void slurpFrameObjPtrSlot(nanojit::LIns* val_ins, ptrdiff_t offset, JSObject** p, SlurpInfo* info);
     JS_REQUIRES_STACK AbortableRecordingStatus slurpDownFrames(jsbytecode* return_pc);
     JS_REQUIRES_STACK AbortableRecordingStatus upRecursion();
     JS_REQUIRES_STACK AbortableRecordingStatus downRecursion();
 
     nanojit::LIns* addName(nanojit::LIns* ins, const char* name);
 
     nanojit::LIns* writeBack(nanojit::LIns* i, nanojit::LIns* base, ptrdiff_t offset,
                              bool demote);
+
+#ifdef DEBUG
+    bool isValidFrameObjPtr(JSObject **obj);
+#endif
+
+    JS_REQUIRES_STACK void setImpl(void* p, nanojit::LIns* l, bool demote = true);
     JS_REQUIRES_STACK void set(Value* p, nanojit::LIns* l, bool demote = true);
+    JS_REQUIRES_STACK void setFrameObjPtr(JSObject** p, nanojit::LIns* l, bool demote = true);
+    nanojit::LIns* getFromTrackerImpl(const void *p);
     nanojit::LIns* getFromTracker(const Value* p);
+    JS_REQUIRES_STACK nanojit::LIns* getImpl(const void* p);
     JS_REQUIRES_STACK nanojit::LIns* get(const Value* p);
+    JS_REQUIRES_STACK nanojit::LIns* getFrameObjPtr(JSObject** p);
     JS_REQUIRES_STACK nanojit::LIns* attemptImport(const Value* p);
     JS_REQUIRES_STACK nanojit::LIns* addr(Value* p);
 
+    JS_REQUIRES_STACK bool knownImpl(const void* p);
     JS_REQUIRES_STACK bool known(const Value* p);
+    JS_REQUIRES_STACK bool known(JSObject** p);
     JS_REQUIRES_STACK void checkForGlobalObjectReallocation();
 
     JS_REQUIRES_STACK TypeConsensus selfTypeStability(SlotMap& smap);
     JS_REQUIRES_STACK TypeConsensus peerTypeStability(SlotMap& smap, const void* ip,
                                                       TreeFragment** peer);
 
     JS_REQUIRES_STACK Value& argval(unsigned n) const;
     JS_REQUIRES_STACK Value& varval(unsigned n) const;
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -821,19 +821,19 @@ class TypedArrayTemplate
     /* slice(start[, end]) */
     static JSBool
     fun_slice(JSContext *cx, uintN argc, Value *vp)
     {
         Value *argv;
         JSObject *obj;
 
         argv = JS_ARGV(cx, vp);
-        obj = ComputeThisObjectFromVp(cx, vp);
+        obj = ComputeThisFromVp(cx, vp);
 
-        if (!JS_InstanceOf(cx, obj, ThisTypeArray::fastClass(), vp+2))
+        if (!InstanceOf(cx, obj, ThisTypeArray::fastClass(), vp+2))
             return false;
 
         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
         if (!tarray)
             return true;
 
         // these are the default values
         int32_t begin = 0, end = tarray->length;
@@ -1550,20 +1550,20 @@ js_ReparentTypedArrayToScope(JSContext *
     JS_ASSERT(js_IsArrayBuffer(buffer));
 
     JSObject *proto;
     JSProtoKey key =
         JSCLASS_CACHED_PROTO_KEY(&TypedArray::slowClasses[typedArray->type]);
     if (!js_GetClassPrototype(cx, scope, key, &proto))
         return JS_FALSE;
 
-    obj->setProto(ObjectTag(*proto));
-    obj->setParent(ObjectTag(*scope));
+    obj->setProto(proto);
+    obj->setParent(scope);
 
     key = JSCLASS_CACHED_PROTO_KEY(&ArrayBuffer::jsclass);
     if (!js_GetClassPrototype(cx, scope, key, &proto))
         return JS_FALSE;
 
-    buffer->setProto(ObjectTag(*proto));
-    buffer->setParent(ObjectTag(*scope));
+    buffer->setProto(proto);
+    buffer->setParent(scope);
 
     return JS_TRUE;
 }
--- a/js/src/jsval.h
+++ b/js/src/jsval.h
@@ -94,18 +94,16 @@ JS_ENUM_HEADER(JSValueType, uint8)
     JSVAL_TYPE_NULL                = 0x06,
     JSVAL_TYPE_OBJECT              = 0x07,
 
     /* The below types never appear in a jsval; they are only used in tracing. */
 
     JSVAL_TYPE_NONFUNOBJ           = 0x57,
     JSVAL_TYPE_FUNOBJ              = 0x67,
 
-    JSVAL_TYPE_STRORNULL           = 0x97,
-    JSVAL_TYPE_OBJORNULL           = 0x98,
     JSVAL_TYPE_BOXED               = 0x99,
     JSVAL_TYPE_UNINITIALIZED       = 0xcd
 } JS_ENUM_FOOTER(JSValueType);
 
 #if JS_BITS_PER_WORD == 32
 
 /* Remember to propagate changes to the C defines below. */
 JS_ENUM_HEADER(JSValueTag, uint32)
@@ -225,17 +223,19 @@ typedef enum JSWhyMagic
 {
     JS_ARRAY_HOLE,               /* a hole in a dense array */
     JS_ARGS_HOLE,                /* a hole in the args object's array */
     JS_NATIVE_ENUMERATE,         /* indicates that a custom enumerate hook forwarded
                                   * to js_Enumerate, which really means the object can be
                                   * enumerated like a native object. */
     JS_NO_ITER_VALUE,            /* there is not a pending iterator value */
     JS_GENERATOR_CLOSING,        /* exception value thrown when closing a generator */
-    JS_NO_CONSTANT               /* compiler sentinel value */
+    JS_NO_CONSTANT,              /* compiler sentinel value */
+    JS_THIS_POISON,              /* used in debug builds to catch tracing errors */
+    JS_GENERIC_MAGIC             /* for local use */
 } JSWhyMagic;
 
 typedef struct JSString JSString;
 typedef struct JSObject JSObject;
 
 #if defined(IS_LITTLE_ENDIAN)
 # if JS_BITS_PER_WORD == 32
 typedef union jsval_layout
--- a/js/src/jsvalue.h
+++ b/js/src/jsvalue.h
@@ -428,16 +428,17 @@ class Value
         return data.asDouble;
     }
 
     void setString(JSString *str) {
         data = STRING_TO_JSVAL_IMPL(str);
     }
 
     void setObject(JSObject &obj) {
+        JS_ASSERT(&obj != NULL);
         data = OBJECT_TO_JSVAL_IMPL(&obj);
     }
 
     void setBoolean(bool b) {
         data = BOOLEAN_TO_JSVAL_IMPL(b);
     }
 
     void setMagic(JSWhyMagic why) {
@@ -777,20 +778,19 @@ typedef JSBool
 typedef JSObjectOps *
 (* GetObjectOps)(JSContext *cx, Class *clasp);
 typedef JSBool
 (* EqualityOp)(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp);
 typedef JSBool
 (* DefinePropOp)(JSContext *cx, JSObject *obj, jsid id, const Value *value,
                  PropertyOp getter, PropertyOp setter, uintN attrs);
 typedef JSBool
-(* CheckAccessIdOp)(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
-                    Value *vp, uintN *attrsp);
+(* PropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, Value *vp);
 typedef JSBool
-(* PropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, Value *vp);
+(* CallOp)(JSContext *cx, uintN argc, Value *vp);
 
 static inline Native            Valueify(JSNative f)          { return (Native)f; }
 static inline JSNative          Jsvalify(Native f)            { return (JSNative)f; }
 static inline FastNative        Valueify(JSFastNative f)      { return (FastNative)f; }
 static inline JSFastNative      Jsvalify(FastNative f)        { return (JSFastNative)f; }
 static inline PropertyOp        Valueify(JSPropertyOp f)      { return (PropertyOp)f; }
 static inline JSPropertyOp      Jsvalify(PropertyOp f)        { return (JSPropertyOp)f; }
 static inline ConvertOp         Valueify(JSConvertOp f)       { return (ConvertOp)f; }
@@ -802,20 +802,20 @@ static inline JSHasInstanceOp   Jsvalify
 static inline CheckAccessOp     Valueify(JSCheckAccessOp f)   { return (CheckAccessOp)f; }
 static inline JSCheckAccessOp   Jsvalify(CheckAccessOp f)     { return (JSCheckAccessOp)f; }
 static inline GetObjectOps      Valueify(JSGetObjectOps f)    { return (GetObjectOps)f; }
 static inline JSGetObjectOps    Jsvalify(GetObjectOps f)      { return (JSGetObjectOps)f; }
 static inline EqualityOp        Valueify(JSEqualityOp f);     /* Same type as JSHasInstanceOp */
 static inline JSEqualityOp      Jsvalify(EqualityOp f);       /* Same type as HasInstanceOp */
 static inline DefinePropOp      Valueify(JSDefinePropOp f)    { return (DefinePropOp)f; }
 static inline JSDefinePropOp    Jsvalify(DefinePropOp f)      { return (JSDefinePropOp)f; }
-static inline CheckAccessIdOp   Valueify(JSCheckAccessIdOp f) { return (CheckAccessIdOp)f; }
-static inline JSCheckAccessIdOp Jsvalify(CheckAccessIdOp f)   { return (JSCheckAccessIdOp)f; }
 static inline PropertyIdOp      Valueify(JSPropertyIdOp f);   /* Same type as JSPropertyOp */
 static inline JSPropertyIdOp    Jsvalify(PropertyIdOp f);     /* Same type as PropertyOp */
+static inline CallOp            Valueify(JSCallOp f);         /* Same type as JSFastNative */
+static inline JSCallOp          Jsvalify(CallOp f);           /* Same type as FastNative */
 
 static const PropertyOp    PropertyStub  = (PropertyOp)JS_PropertyStub;
 static const JSEnumerateOp EnumerateStub = JS_EnumerateStub;
 static const JSResolveOp   ResolveStub   = JS_ResolveStub;
 static const ConvertOp     ConvertStub   = (ConvertOp)JS_ConvertStub;
 static const JSFinalizeOp  FinalizeStub  = JS_FinalizeStub;
 
 struct Class {
@@ -835,43 +835,76 @@ struct Class {
     /* Optionally non-null members start here. */
     GetObjectOps        getObjectOps;
     CheckAccessOp       checkAccess;
     Native              call;
     Native              construct;
     JSXDRObjectOp       xdrObject;
     HasInstanceOp       hasInstance;
     JSMarkOp            mark;
-    JSReserveSlotsOp    reserveSlots;
+    void                (*reserved0)(void);
 };
+JS_STATIC_ASSERT(offsetof(JSClass, name) == offsetof(Class, name));
+JS_STATIC_ASSERT(offsetof(JSClass, flags) == offsetof(Class, flags));
+JS_STATIC_ASSERT(offsetof(JSClass, addProperty) == offsetof(Class, addProperty));
+JS_STATIC_ASSERT(offsetof(JSClass, delProperty) == offsetof(Class, delProperty));
+JS_STATIC_ASSERT(offsetof(JSClass, getProperty) == offsetof(Class, getProperty));
+JS_STATIC_ASSERT(offsetof(JSClass, setProperty) == offsetof(Class, setProperty));
+JS_STATIC_ASSERT(offsetof(JSClass, enumerate) == offsetof(Class, enumerate));
+JS_STATIC_ASSERT(offsetof(JSClass, resolve) == offsetof(Class, resolve));
+JS_STATIC_ASSERT(offsetof(JSClass, convert) == offsetof(Class, convert));
+JS_STATIC_ASSERT(offsetof(JSClass, finalize) == offsetof(Class, finalize));
+JS_STATIC_ASSERT(offsetof(JSClass, getObjectOps) == offsetof(Class, getObjectOps));
+JS_STATIC_ASSERT(offsetof(JSClass, checkAccess) == offsetof(Class, checkAccess));
+JS_STATIC_ASSERT(offsetof(JSClass, call) == offsetof(Class, call));
+JS_STATIC_ASSERT(offsetof(JSClass, construct) == offsetof(Class, construct));
+JS_STATIC_ASSERT(offsetof(JSClass, xdrObject) == offsetof(Class, xdrObject));
+JS_STATIC_ASSERT(offsetof(JSClass, hasInstance) == offsetof(Class, hasInstance));
+JS_STATIC_ASSERT(offsetof(JSClass, mark) == offsetof(Class, mark));
+JS_STATIC_ASSERT(offsetof(JSClass, reserved0) == offsetof(Class, reserved0));
 JS_STATIC_ASSERT(sizeof(JSClass) == sizeof(Class));
 
 struct ExtendedClass {
     Class               base;
     EqualityOp          equality;
     JSObjectOp          outerObject;
     JSObjectOp          innerObject;
     JSIteratorOp        iteratorObject;
     JSObjectOp          wrappedObject;          /* NB: infallible, null
                                                    returns are treated as
                                                    the original object */
     void                (*reserved0)(void);
     void                (*reserved1)(void);
     void                (*reserved2)(void);
 };
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, base) == offsetof(ExtendedClass, base));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, equality) == offsetof(ExtendedClass, equality));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, outerObject) == offsetof(ExtendedClass, outerObject));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, innerObject) == offsetof(ExtendedClass, innerObject));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, iteratorObject) == offsetof(ExtendedClass, iteratorObject));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, wrappedObject) == offsetof(ExtendedClass, wrappedObject));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, reserved0) == offsetof(ExtendedClass, reserved0));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, reserved1) == offsetof(ExtendedClass, reserved1));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, reserved2) == offsetof(ExtendedClass, reserved2));
 JS_STATIC_ASSERT(sizeof(JSExtendedClass) == sizeof(ExtendedClass));
 
 struct PropertyDescriptor {
     JSObject     *obj;
     uintN        attrs;
     PropertyOp   getter;
     PropertyOp   setter;
     Value        value;
     uintN        shortid;
 };
+JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, obj) == offsetof(PropertyDescriptor, obj));
+JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, attrs) == offsetof(PropertyDescriptor, attrs));
+JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, getter) == offsetof(PropertyDescriptor, getter));
+JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, setter) == offsetof(PropertyDescriptor, setter));
+JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, value) == offsetof(PropertyDescriptor, value));
+JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, shortid) == offsetof(PropertyDescriptor, shortid));
 JS_STATIC_ASSERT(sizeof(JSPropertyDescriptor) == sizeof(PropertyDescriptor));
 
 static JS_ALWAYS_INLINE JSClass *              Jsvalify(Class *c)                { return (JSClass *)c; }
 static JS_ALWAYS_INLINE Class *                Valueify(JSClass *c)              { return (Class *)c; }
 static JS_ALWAYS_INLINE JSExtendedClass *      Jsvalify(ExtendedClass *c)        { return (JSExtendedClass *)c; }
 static JS_ALWAYS_INLINE ExtendedClass *        Valueify(JSExtendedClass *c)      { return (ExtendedClass *)c; }
 static JS_ALWAYS_INLINE JSPropertyDescriptor * Jsvalify(PropertyDescriptor *p) { return (JSPropertyDescriptor *) p; }
 static JS_ALWAYS_INLINE PropertyDescriptor *   Valueify(JSPropertyDescriptor *p) { return (PropertyDescriptor *) p; }
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -58,17 +58,17 @@ JSObject::isWrapper() const
     return isProxy() && getProxyHandler()->family() == &sWrapperFamily;
 }
 
 JSObject *
 JSObject::unwrap()
 {
     JSObject *wrapped = this;
     while (wrapped->isWrapper())
-        wrapped = JSVAL_TO_OBJECT(wrapped->getProxyPrivate());
+        wrapped = wrapped->getProxyPrivate().asObjectOrNull();
     return wrapped;
 }
 
 JSWrapper::JSWrapper(void *kind) : JSProxyHandler(&sWrapperFamily), mKind(kind)
 {
 }
 
 JSWrapper::~JSWrapper()
@@ -97,33 +97,33 @@ JSWrapper::defineProperty(JSContext *cx,
                           PropertyDescriptor *desc)
 {
     return JS_DefinePropertyById(cx, wrappedObject(proxy), id, Jsvalify(desc->value),
                                  Jsvalify(desc->getter), Jsvalify(desc->setter),
                                  desc->attrs);
 }
 
 bool
-JSWrapper::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSWrapper::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     return GetPropertyNames(cx, wrappedObject(proxy), JSITER_OWNONLY | JSITER_HIDDEN, props);
 }
 
 bool
 JSWrapper::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
     AutoValueRooter tvr(cx);
     if (!JS_DeletePropertyById2(cx, wrappedObject(proxy), id, tvr.jsval_addr()))
         return false;
     *bp = js_ValueToBoolean(tvr.value());
     return true;
 }
 
 bool
-JSWrapper::enumerate(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSWrapper::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     return GetPropertyNames(cx, wrappedObject(proxy), 0, props);
 }
 
 bool
 JSWrapper::fix(JSContext *cx, JSObject *proxy, Value *vp)
 {
     vp->setUndefined();
@@ -159,17 +159,17 @@ JSWrapper::get(JSContext *cx, JSObject *
 
 bool
 JSWrapper::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
 {
     return JS_SetPropertyById(cx, wrappedObject(proxy), id, Jsvalify(vp));
 }
 
 bool
-JSWrapper::enumerateOwn(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSWrapper::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     return GetPropertyNames(cx, wrappedObject(proxy), JSITER_OWNONLY, props);
 }
 
 bool
 JSWrapper::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
 {
     return GetIterator(cx, wrappedObject(proxy), flags, vp);
@@ -184,17 +184,17 @@ JSWrapper::trace(JSTracer *trc, JSObject
 static int sTransparentWrapperKind;
 
 JSWrapper JSWrapper::singleton(&sTransparentWrapperKind);
 
 JSObject *
 JSWrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
                JSProxyHandler *handler)
 {
-    return NewProxyObject(cx, handler, OBJECT_TO_JSVAL(obj), proto, parent,
+    return NewProxyObject(cx, handler, ObjectTag(*obj), proto, parent,
                           obj->isCallable() ? obj : NULL, NULL);
 }
 
 /* Compartments. */
 
 namespace js {
 
 extern JSObject *
@@ -216,58 +216,56 @@ JSCompartment::~JSCompartment()
 
 bool
 JSCompartment::init()
 {
     return crossCompartmentWrappers.init();
 }
 
 bool
-JSCompartment::wrap(JSContext *cx, jsval *vp)
+JSCompartment::wrap(JSContext *cx, Value *vp)
 {
     JS_ASSERT(cx->compartment == this);
 
     JS_CHECK_RECURSION(cx, return false);
 
     /* Only GC things have to be wrapped or copied. */
-    if (JSVAL_IS_NULL(*vp) || !JSVAL_IS_GCTHING(*vp))
+    if (!vp->isMarkable())
         return true;
 
     /* Static strings do not have to be wrapped. */
-    if (JSVAL_IS_STRING(*vp) && JSString::isStatic(JSVAL_TO_STRING(*vp)))
+    if (vp->isString() && JSString::isStatic(vp->asString()))
         return true;
 
-    /* Identity is no issue for doubles, so simply always copy them. */
-    if (JSVAL_IS_DOUBLE(*vp))
-        return js_NewNumberInRootedValue(cx, *JSVAL_TO_DOUBLE(*vp), vp);
-
     /* Unwrap incoming objects. */
-    if (!JSVAL_IS_PRIMITIVE(*vp)) {
-        JSObject *obj =  JSVAL_TO_OBJECT(*vp)->unwrap();
-        *vp = OBJECT_TO_JSVAL(obj);
+    if (vp->isObject()) {
+        JSObject *obj =  vp->asObject().unwrap();
+        vp->setObject(*obj);
         /* If the wrapped object is already in this compartment, we are done. */
         if (obj->getCompartment(cx) == this)
             return true;
     }
 
     /* If we already have a wrapper for this value, use it. */
     if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) {
         *vp = p->value;
         return true;
     }
 
-    if (JSVAL_IS_STRING(*vp)) {
-        JSString *str = JSVAL_TO_STRING(*vp);
+    if (vp->isString()) {
+        Value orig = *vp;
+        JSString *str = vp->asString();
         JSString *wrapped = js_NewStringCopyN(cx, str->chars(), str->length());
         if (!wrapped)
             return false;
-        return crossCompartmentWrappers.put(*vp, *vp = STRING_TO_JSVAL(wrapped));
+        vp->setString(wrapped);
+        return crossCompartmentWrappers.put(orig, *vp);
     }
 
-    JSObject *obj = JSVAL_TO_OBJECT(*vp);
+    JSObject *obj = &vp->asObject();
 
     /*
      * Recurse to wrap the prototype. Long prototype chains will run out of
      * stack, causing an error in CHECK_RECURSE.
      *
      * Wrapping the proto before creating the new wrapper and adding it to the
      * cache helps avoid leaving a bad entry in the cache on OOM. But note that
      * if we wrapped both proto and parent, we would get infinite recursion
@@ -277,17 +275,17 @@ JSCompartment::wrap(JSContext *cx, jsval
     AutoObjectRooter proto(cx, obj->getProto());
     if (!wrap(cx, proto.addr()))
         return false;
 
     JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto.object());
     if (!wrapper)
         return false;
     wrapper->setProto(proto.object());
-    *vp = OBJECT_TO_JSVAL(wrapper);
+    vp->setObject(*wrapper);
     if (!crossCompartmentWrappers.put(wrapper->getProxyPrivate(), *vp))
         return false;
 
     /*
      * Wrappers should really be parented to the wrapped parent of the wrapped
      * object, but in that case a wrapped global object would have a NULL
      * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead,
      * we parent all wrappers to the global object in their home compartment.
@@ -296,116 +294,116 @@ JSCompartment::wrap(JSContext *cx, jsval
     JSObject *global = cx->fp ? cx->fp->scopeChain->getGlobal() : cx->globalObject;
     wrapper->setParent(global);
     return true;
 }
 
 bool
 JSCompartment::wrap(JSContext *cx, JSString **strp)
 {
-    AutoValueRooter tvr(cx, *strp);
+    AutoValueRooter tvr(cx, StringTag(*strp));
     if (!wrap(cx, tvr.addr()))
         return false;
-    *strp = JSVAL_TO_STRING(tvr.value());
+    *strp = tvr.value().asString();
     return true;
 }
 
 bool
 JSCompartment::wrap(JSContext *cx, JSObject **objp)
 {
     if (!*objp)
         return true;
-    AutoValueRooter tvr(cx, *objp);
+    AutoValueRooter tvr(cx, ObjectTag(**objp));
     if (!wrap(cx, tvr.addr()))
         return false;
-    *objp = JSVAL_TO_OBJECT(tvr.value());
+    *objp = &tvr.value().asObject();
     return true;
 }
 
 bool
 JSCompartment::wrapId(JSContext *cx, jsid *idp) {
     if (JSID_IS_INT(*idp))
         return true;
-    AutoValueRooter tvr(cx, ID_TO_VALUE(*idp));
+    AutoValueRooter tvr(cx, IdToValue(*idp));
     if (!wrap(cx, tvr.addr()))
         return false;
-    return JS_ValueToId(cx, tvr.value(), idp);
+    return ValueToId(cx, tvr.value(), idp);
 }
 
 bool
-JSCompartment::wrap(JSContext *cx, JSPropertyOp *propp)
+JSCompartment::wrap(JSContext *cx, PropertyOp *propp)
 {
     union {
-        JSPropertyOp op;
+        PropertyOp op;
         jsval v;
     } u;
     u.op = *propp;
-    if (!wrap(cx, &u.v))
+    if (!wrap(cx, &Valueify(u.v)))
         return false;
     *propp = u.op;
     return true;
 }
 
 bool
-JSCompartment::wrap(JSContext *cx, JSPropertyDescriptor *desc) {
+JSCompartment::wrap(JSContext *cx, PropertyDescriptor *desc) {
     return wrap(cx, &desc->obj) &&
            (!(desc->attrs & JSPROP_GETTER) || wrap(cx, &desc->getter)) &&
            (!(desc->attrs & JSPROP_SETTER) || wrap(cx, &desc->setter)) &&
            wrap(cx, &desc->value);
 }
 
 bool
-JSCompartment::wrap(JSContext *cx, AutoValueVector &props) {
+JSCompartment::wrap(JSContext *cx, AutoIdVector &props) {
     jsid *vector = props.begin();
     jsint length = props.length();
     for (size_t n = 0; n < size_t(length); ++n) {
-        if (!wrap(cx, &vector[n]))
+        if (!wrapId(cx, &vector[n]))
             return false;
     }
     return true;
 }
 
 bool
 JSCompartment::wrapException(JSContext *cx) {
     JS_ASSERT(cx->compartment == this);
 
     if (cx->throwing) {
         AutoValueRooter tvr(cx, cx->exception);
         cx->throwing = false;
-        cx->exception = JSVAL_NULL;
+        cx->exception = NullTag();
         if (wrap(cx, tvr.addr())) {
             cx->throwing = true;
             cx->exception = tvr.value();
         }
         return false;
     }
     return true;
 }
 
 void
 JSCompartment::sweep(JSContext *cx)
 {
     /* Remove dead wrappers from the table. */
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
-        if (js_IsAboutToBeFinalized(JSVAL_TO_GCTHING(e.front().value)))
+        if (js_IsAboutToBeFinalized(e.front().value.asGCThing()))
             e.removeFront();
     }
 }
 
 static bool
 SetupFakeFrame(JSContext *cx, ExecuteFrameGuard &frame, JSFrameRegs &regs, JSObject *obj)
 {
     const uintN vplen = 2;
     const uintN nfixed = 0;
     if (!cx->stack().getExecuteFrame(cx, js_GetTopStackFrame(cx), vplen, nfixed, frame))
         return false;
 
-    jsval *vp = frame.getvp();
-    vp[0] = JSVAL_VOID;
-    vp[1] = JSVAL_VOID;
+    Value *vp = frame.getvp();
+    vp[0] = UndefinedTag();
+    vp[1] = UndefinedTag();
 
     JSStackFrame *fp = frame.getFrame();
     PodZero(fp);  // fp->fun and fp->script are both NULL
     fp->argv = vp + 2;
     fp->scopeChain = obj->getGlobal();
 
     regs.pc = NULL;
     regs.sp = fp->slots();
@@ -483,64 +481,66 @@ JSCrossCompartmentWrapper::~JSCrossCompa
         leave(cx, proxy);                                                    \
         call.leave();                                                        \
         return (post);                                                       \
     JS_END_MACRO
 
 #define NOTHING (true)
 
 bool
-JSCrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
+JSCrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
 {
     PIERCE(cx, proxy, GET,
            call.destination->wrapId(cx, &id),
            JSWrapper::getPropertyDescriptor(cx, proxy, id, desc),
            call.origin->wrap(cx, desc));
 }
 
 bool
-JSCrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
+JSCrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
 {
     PIERCE(cx, proxy, GET,
            call.destination->wrapId(cx, &id),
            JSWrapper::getOwnPropertyDescriptor(cx, proxy, id, desc),
            call.origin->wrap(cx, desc));
 }
 
 bool
-JSCrossCompartmentWrapper::defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
+JSCrossCompartmentWrapper::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
 {
-    AutoDescriptor desc2(cx, desc);
+    AutoPropertyDescriptorRooter desc2(cx, desc);
     PIERCE(cx, proxy, SET,
            call.destination->wrapId(cx, &id) && call.destination->wrap(cx, &desc2),
            JSWrapper::getOwnPropertyDescriptor(cx, proxy, id, &desc2),
            NOTHING);
 }
 
 bool
-JSCrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSCrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
+    jsid id = JSID_VOID;
     PIERCE(cx, proxy, GET,
            NOTHING,
            JSWrapper::getOwnPropertyNames(cx, proxy, props),
            call.origin->wrap(cx, props));
 }
 
 bool
 JSCrossCompartmentWrapper::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
     PIERCE(cx, proxy, SET,
            call.destination->wrapId(cx, &id),
            JSWrapper::delete_(cx, proxy, id, bp),
            NOTHING);
 }
 
 bool
-JSCrossCompartmentWrapper::enumerate(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSCrossCompartmentWrapper::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
+    jsid id = JSID_VOID;
     PIERCE(cx, proxy, GET,
            NOTHING,
            JSWrapper::enumerate(cx, proxy, props),
            call.origin->wrap(cx, props));
 }
 
 bool
 JSCrossCompartmentWrapper::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
@@ -556,126 +556,157 @@ JSCrossCompartmentWrapper::hasOwn(JSCont
 {
     PIERCE(cx, proxy, GET,
            call.destination->wrapId(cx, &id),
            JSWrapper::hasOwn(cx, proxy, id, bp),
            NOTHING);
 }
 
 bool
-JSCrossCompartmentWrapper::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp)
+JSCrossCompartmentWrapper::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
 {
     PIERCE(cx, proxy, GET,
            call.destination->wrap(cx, &receiver) && call.destination->wrapId(cx, &id),
            JSWrapper::get(cx, proxy, receiver, id, vp),
            call.origin->wrap(cx, vp));
 }
 
 bool
-JSCrossCompartmentWrapper::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp)
+JSCrossCompartmentWrapper::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
 {
     AutoValueRooter tvr(cx, *vp);
     PIERCE(cx, proxy, SET,
            call.destination->wrap(cx, &receiver) && call.destination->wrapId(cx, &id) && call.destination->wrap(cx, tvr.addr()),
            JSWrapper::set(cx, proxy, receiver, id, tvr.addr()),
            NOTHING);
 }
 
 bool
-JSCrossCompartmentWrapper::enumerateOwn(JSContext *cx, JSObject *proxy, AutoValueVector &props)
+JSCrossCompartmentWrapper::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
+    jsid id = JSID_VOID;
     PIERCE(cx, proxy, GET,
            NOTHING,
            JSWrapper::enumerateOwn(cx, proxy, props),
            call.origin->wrap(cx, props));
 }
 
 /*
  * We can reify non-escaping iterator objects instead of having to wrap them. This
  * allows fast iteration over objects across a compartment boundary.
  */
 static bool
-CanReify(jsval *vp)
+CanReify(Value *vp)
 {
-    return !JSVAL_IS_PRIMITIVE(*vp) &&
-           JSVAL_TO_OBJECT(*vp)->getClass() == &js_IteratorClass.base &&
-           !!(JSVAL_TO_OBJECT(*vp)->getNativeIterator()->flags & JSITER_ENUMERATE);
+    JSObject *obj;
+    return vp->isObject() &&
+           (obj = &vp->asObject())->getClass() == &js_IteratorClass.base &&
+           (obj->getNativeIterator()->flags & JSITER_ENUMERATE);
 }
 
 static bool
-Reify(JSContext *cx, JSCompartment *origin, jsval *vp)
+Reify(JSContext *cx, JSCompartment *origin, Value