New Tag scheme, no SetObject silliness
authorLuke Wagner <lw@mozilla.com>
Wed, 12 May 2010 16:36:10 -0700
changeset 52526 ec553cbfb696d4e59f214920a4f38b1eb8f9ef21
parent 52525 d3fce875d722dbaec4f60ad7350a26b4d4be0dd7
child 52527 649a438d9ed70ab65be61868bfb6a061ad5b8b34
push idunknown
push userunknown
push dateunknown
milestone1.9.3a5pre
New Tag scheme, no SetObject silliness
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsatom.cpp
js/src/jsatominlines.h
js/src/jsbool.cpp
js/src/jscntxt.h
js/src/jsdbgapi.cpp
js/src/jsdtoa.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsops.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1100,35 +1100,35 @@ js_InitFunctionAndObjectClasses(JSContex
         JSObject *ctor;
 
         ctor = JS_GetConstructor(cx, fun_proto);
         if (!ctor) {
             fun_proto = NULL;
             goto out;
         }
         obj->defineProperty(cx, ATOM_TO_JSID(CLASS_ATOM(cx, Function)),
-                            FunObjValue(*ctor), 0, 0, 0);
+                            FunObjTag(*ctor), 0, 0, 0);
     }
 
     /* Initialize the object class next so Object.prototype works. */
     if (!js_GetClassPrototype(cx, obj, JSProto_Object, &obj_proto)) {
         fun_proto = NULL;
         goto out;
     }
     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(NonFunObjPtr(*obj_proto));
+    fun_proto->setProto(NonFunObjTag(*obj_proto));
     if (!obj->getProto())
-        obj->setProto(NonFunObjPtr(*obj_proto));
+        obj->setProto(NonFunObjTag(*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]));
@@ -2889,17 +2889,17 @@ JS_DefineObject(JSContext *cx, JSObject 
 {
     CHECK_REQUEST(cx);
     Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
     JSObject *nobj = NewObject(cx, clasp, proto, obj);
     if (!nobj)
         return NULL;
-    if (!DefineProperty(cx, obj, name, ToValue(nobj), NULL, NULL, attrs,
+    if (!DefineProperty(cx, obj, name, ObjectTag(*nobj), NULL, NULL, attrs,
                         0, 0)) {
         return NULL;
     }
     return nobj;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds)
@@ -3059,17 +3059,17 @@ LookupResult(JSContext *cx, JSObject *ob
 
     JSBool ok = JS_TRUE;
     if (obj2->isNative()) {
         JSScopeProperty *sprop = (JSScopeProperty *) prop;
 
         if (sprop->isMethod()) {
             AutoScopePropertyRooter root(cx, sprop);
             JS_UNLOCK_OBJ(cx, obj2);
-            vp->copy(sprop->methodValue());
+            vp->setFunObj(sprop->methodFunObj());
             return obj2->scope()->methodReadBarrier(cx, sprop, vp);
         }
 
         /* Peek at the native property's slot value, without doing a Get. */
         if (SPROP_HAS_VALID_SLOT(sprop, obj2->scope()))
             vp->copy(obj->lockedGetSlot(sprop->slot));
         else
             vp->setBoolean(true);
@@ -3961,17 +3961,17 @@ JS_NextProperty(JSContext *cx, JSObject 
     } else {
         /* Non-native case: use the ida enumerated when iterobj was created. */
         ida = (JSIdArray *) iterobj->getPrivate();
         JS_ASSERT(i <= ida->length);
         if (i == 0) {
             *idp = JSID_VOID;
         } else {
             *idp = ida->vector[--i];
-            iterobj->setSlot(JSSLOT_ITER_INDEX, Value(i));
+            iterobj->setSlot(JSSLOT_ITER_INDEX, Int32Tag(i));
         }
     }
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
                jsval *vp, uintN *attrsp)
@@ -4076,17 +4076,17 @@ JS_CloneFunctionObject(JSContext *cx, JS
     }
 
     if (funobj->getClass() != &js_FunctionClass) {
         /*
          * We cannot clone this object, so fail (we used to return funobj, bad
          * idea, but we changed incompatibly to teach any abusers a lesson!).
          */
         Value v;
-        SetObject(&v, funobj);
+        v.setObject(*funobj);
         js_ReportIsNotFunction(cx, &v, 0);
         return NULL;
     }
 
     JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
     JSObject *clone = CloneFunctionObject(cx, fun, parent);
     if (!clone)
         return NULL;
@@ -4679,18 +4679,17 @@ JS_CompileUCFunctionForPrincipals(JSCont
 
         if (!Compiler::compileFunctionBody(cx, fun, principals,
                                            chars, length, filename, lineno)) {
             fun = NULL;
             goto out;
         }
 
         if (obj && funAtom &&
-            !obj->defineProperty(cx, ATOM_TO_JSID(funAtom),
-                                 FunObjValue(*FUN_OBJECT(fun)),
+            !obj->defineProperty(cx, ATOM_TO_JSID(funAtom), fun->funObjVal(),
                                  NULL, NULL, JSPROP_ENUMERATE)) {
             fun = NULL;
         }
 
 #ifdef JS_SCOPE_DEPTH_METER
         if (fun && obj) {
             JSObject *pobj = obj;
             uintN depth = 1;
@@ -4844,18 +4843,18 @@ JS_EvaluateUCScriptForPrincipals(JSConte
 
 JS_PUBLIC_API(JSBool)
 JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc,
                 jsval *argv, jsval *rval)
 {
     JSBool ok;
 
     CHECK_REQUEST(cx);
-    ok = InternalCall(cx, obj, FunObjValue(*FUN_OBJECT(fun)), argc,
-                      Valueify(argv), Valueify(rval));
+    ok = InternalCall(cx, obj, fun->funObjVal(), 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)
 {
@@ -4893,17 +4892,17 @@ JS_New(JSContext *cx, JSObject *ctor, ui
     // 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;
 
     Value *vp = args.getvp();
-    SetObject(&vp[0], ctor);
+    vp[0].setObject(*ctor);
     vp[1].setNull();
     memcpy(vp + 2, argv, argc * sizeof(jsval));
 
     JSBool ok = InvokeConstructor(cx, args, JS_TRUE);
     JSObject *obj = ok ? vp[0].asObjectOrNull() : NULL;
 
     LAST_FRAME_CHECKS(cx, ok);
     return obj;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2945,16 +2945,69 @@ JS_END_EXTERN_C
 #ifdef __cplusplus
 
 /*
  * Spanky new C++ API
  */
 
 namespace js {
 
+struct NullTag {
+    explicit NullTag() {}
+};
+
+struct UndefinedTag {
+    explicit UndefinedTag() {}
+};
+
+struct Int32Tag {
+    explicit Int32Tag(int32 i32) : i32(i32) {}
+    int32 i32;
+};
+
+struct FunObjTag {
+    explicit FunObjTag(JSObject &obj) : obj(obj) {}
+    JSObject &obj;
+};
+
+struct FunObjOrNull {
+    explicit FunObjOrNull(JSObject *obj) : obj(obj) {}
+    JSObject *obj;
+};
+
+struct FunObjOrUndefinedTag {
+    explicit FunObjOrUndefinedTag(JSObject *obj) : obj(obj) {}
+    JSObject *obj;
+};
+
+struct NonFunObjTag {
+    explicit NonFunObjTag(JSObject &o) : obj(obj) {}
+    JSObject &obj;
+};
+
+struct NonFunObjOrNull {
+    explicit NonFunObjOrNull(JSObject *obj) : obj(obj) {}
+    JSObject *obj;
+};
+
+struct ObjectTag {
+    explicit ObjectTag(JSObject &obj) : obj(obj) {}
+    JSObject &obj;
+};
+
+struct ObjectOrNullTag {
+    explicit ObjectOrNullTag(JSObject *obj) : obj(obj) {}
+    JSObject *obj;
+};
+
+struct BooleanTag {
+    explicit BooleanTag(bool boo) : boo(boo) {}
+    bool boo;
+};
+
 /*
  * An ObjPtr is a restriction of a Value (below) to the three types: null,
  * non-function object and function object. ObjPtr is useful when code
  * conceptually is working with a JSObject*, but wants to avoid unnecessarily
  * querying obj->isFunction() by passing around the type tag as well. Also,
  * ObjPtr provides syntactic sugar so that it behaves more like a JSObject*.
  *
  * N.B. ObjPtr is not layout compatible with Value: on 32-bit systems, it does
@@ -2975,25 +3028,52 @@ class ObjPtr
     JSObject *obj;
 
   private:
     friend class Value;
     explicit ObjPtr(MaskType m, JSObject *o) : mask(m), obj(o) {}
 
   public:
     /* Constructors */
-
-    /* N.B. defualt construction yields a ObjPtr in an undefined state. */
-    ObjPtr() {}
-
-    /*
-     * To construct a null pointer, use NullObjPtr. To construct pointers to
-     * objects statically known to be, or not to be, function objects, use
-     * FunObjPtr, NonFunObjPtr.
-     */
+    
+    ObjPtr() {
+        /* N.B. mask and obj are undefined */
+    }
+
+    ObjPtr(NullTag) {
+        mask = NullMask;
+        obj = NULL;
+    }
+
+    ObjPtr(FunObjTag arg) {
+        JS_ASSERT(JS_ObjectIsFunction(NULL, &arg.obj));
+        mask = FunObjMask;
+        obj = &arg.obj;
+    }
+
+    ObjPtr(FunObjOrNull arg) {
+        JS_ASSERT_IF(arg.obj, JS_ObjectIsFunction(NULL, arg.obj));
+        mask = arg.obj ? FunObjMask : NullMask;
+        obj = arg.obj;
+    }
+
+    ObjPtr(NonFunObjTag arg) {
+        JS_ASSERT(!JS_ObjectIsFunction(NULL, &arg.obj));
+        mask = NonFunObjMask;
+        obj = &arg.obj;
+    }
+
+    ObjPtr(NonFunObjOrNull arg) {
+        JS_ASSERT_IF(arg.obj, !JS_ObjectIsFunction(NULL, arg.obj));
+        mask = arg.obj ? NonFunObjMask : NullMask;
+        obj = arg.obj;
+    }
+
+    inline ObjPtr(ObjectTag arg);
+    inline ObjPtr(ObjectOrNullTag arg);
 
     /* Accessors */
 
     bool isNull() const {
         return mask == NullMask;
     }
 
     bool isFunObj() const {
@@ -3035,97 +3115,52 @@ class ObjPtr
 
     /* Assignment */
 
     void setNull() {
         mask = NullMask;
         obj = NULL;
     }
 
-    void setFunObj(JSObject &o) {
-        JS_ASSERT(JS_ObjectIsFunction(NULL, &o));
+    void setFunObj(JSObject &arg) {
+        JS_ASSERT(JS_ObjectIsFunction(NULL, &arg));
         mask = FunObjMask;
-        obj = &o;
+        obj = &arg;
     }
 
-    void setNonFunObj(JSObject &o) {
-        JS_ASSERT(!JS_ObjectIsFunction(NULL, &o));
-        mask = NonFunObjMask;
-        obj = &o;
+    void setFunObjOrNull(JSObject *arg) {
+        JS_ASSERT_IF(arg, JS_ObjectIsFunction(NULL, arg));
+        mask = arg ? FunObjMask : NullMask;
+        obj = arg;
     }
 
-    void setNonFunObjOrNull(JSObject *o) {
-        if (o) {
-            JS_ASSERT(!JS_ObjectIsFunction(NULL, o));
-            mask = NonFunObjMask;
-            obj = o;
-        } else {
-            mask = NullMask;
-            obj = o;
-        }
+    void setNonFunObj(JSObject &arg) {
+        JS_ASSERT(!JS_ObjectIsFunction(NULL, &arg));
+        mask = NonFunObjMask;
+        obj = &arg;
     }
+
+    void setNonFunObjOrNull(JSObject *arg) {
+        JS_ASSERT_IF(arg, !JS_ObjectIsFunction(NULL, arg));
+        mask = arg ? NonFunObjMask : NullMask;
+        obj = arg;
+    }
+
+    inline void setObject(JSObject &arg);
+    inline void setObjectOrNull(JSObject *arg);
 };
 
 inline bool operator==(ObjPtr lhs, ObjPtr rhs) { return lhs == rhs; }
 inline bool operator==(ObjPtr lhs, JSObject *rhs) { return lhs == rhs; } 
 inline bool operator==(JSObject *lhs, ObjPtr rhs) { return lhs == rhs; }
 inline bool operator!=(ObjPtr lhs, ObjPtr rhs) { return !(lhs == rhs); }
 inline bool operator!=(ObjPtr lhs, JSObject *rhs) { return !(lhs == rhs); }
 inline bool operator!=(JSObject *lhs, ObjPtr rhs) { return !(lhs == rhs); }
 
-/*
- * These types are intended to be used like:
- *
- *   ObjPtr p = NullObjPtr();
- *   void f1(ObjPtr);
- *   f1(FunObjPtr(funobj));
- */
-
-struct NullObjPtr : ObjPtr
-{
-    explicit NullObjPtr() {
-        mask = NullMask;
-        obj = NULL;
-    }
-};
-
-struct FunObjPtr : ObjPtr
-{
-    explicit FunObjPtr(JSObject &o) {
-        JS_ASSERT(JS_ObjectIsFunction(NULL, &o));
-        mask = FunObjMask;
-        obj = &o;
-    }
-};
-
-struct NonFunObjPtr : ObjPtr
-{
-    explicit NonFunObjPtr(JSObject &o) {
-        JS_ASSERT(!JS_ObjectIsFunction(NULL, &o));
-        mask = NonFunObjMask;
-        obj = &o;
-    }
-};
-
-struct NonFunObjOrNullPtr : ObjPtr
-{
-    explicit NonFunObjOrNullPtr(JSObject *o) {
-        if (o) {
-            JS_ASSERT(!JS_ObjectIsFunction(NULL, o));
-            mask = NonFunObjMask;
-            obj = o;
-        } else {
-            mask = NullMask;
-            obj = NULL;
-        }
-    }
-};
-
-/* TODO: this can be removed when copying is implicit/public. */
-class ExplicitlyConstructedValue;
+class CopyableValue;
 
 /*
  * While there is a single representation for values, there are two declared
  * types for dealing with these values: jsval and js::Value. jsval allows a
  * high-degree of source compatibility with the old word-sized boxed value
  * representation. js::Value is a new C++-only type and more accurately
  * reflects the current, unboxed value representation. As these two types are
  * layout-compatible, pointers to jsval and js::Value are interchangeable and
@@ -3190,75 +3225,100 @@ class Value
      * To copy, use explicit copy().
      *
      * XXX This may be made public later, right now its just being used to
      * identify unnecessary copying.
      */
     Value &operator=(const Value &);
 
   public:
-    /* XXX: this can be removed when copying is public/implicit */
-    inline Value(const ExplicitlyConstructedValue &v);
-
-    /*
-     * Constructors
-     *
-     * To construct a null value, use NullValue. To construct pointers to
-     * objects statically known to be, or not to be, function objects, use
-     * FunObjValue, NonFunObjValue.
-     */
-
-    /* N.B. default construction yields a Value in an undefined state. */
-    Value() {}
+    /* Constructors */
+
+    Value() {
+        /* N.B. mask and data are undefined. */
+    }
+
+    Value(NullTag) {
+        mask = NullMask;
+        data.obj = NULL;
+    }
+
+    Value(UndefinedTag) {
+        mask = UndefinedMask;
+        /* N.B. data is undefined */
+    }
+
+    Value(Int32Tag arg) {
+        mask = Int32Mask;
+        data.i32 = arg.i32;
+    }
+
+    Value(double arg) {
+        mask = DoubleMask;
+        data.dbl = arg;
+    }
+
+    Value(JSString *arg) {
+        mask = StringMask;
+        data.str = arg;
+    }
+
+    Value(FunObjTag arg) {
+        JS_ASSERT(JS_ObjectIsFunction(NULL, &arg.obj));
+        mask = FunObjMask;
+        data.obj = &arg.obj;
+    }
+
+    Value(FunObjOrNull arg) {
+        JS_ASSERT_IF(arg.obj, JS_ObjectIsFunction(NULL, arg.obj));
+        mask = arg.obj ? FunObjMask : NullMask;
+        data.obj = arg.obj;
+    }
+
+    Value(FunObjOrUndefinedTag arg) {
+        JS_ASSERT_IF(arg.obj, JS_ObjectIsFunction(NULL, arg.obj));
+        mask = arg.obj ? FunObjMask : UndefinedMask;
+        data.obj = arg.obj;
+    }
+
+    Value(NonFunObjTag arg) {
+        JS_ASSERT(JS_ObjectIsFunction(NULL, &arg.obj));
+        mask = NonFunObjMask;
+        data.obj = &arg.obj;
+    }
+
+    Value(NonFunObjOrNull arg) {
+        JS_ASSERT_IF(arg.obj, !JS_ObjectIsFunction(NULL, arg.obj));
+        mask = arg.obj ? NonFunObjMask : NullMask;
+        data.obj = arg.obj;
+    }
+
+    inline Value(ObjectTag arg);
+    inline Value(ObjectOrNullTag arg);
+
+    Value(BooleanTag arg) {
+        mask = BooleanMask;
+        data.boo = arg.boo;
+    }
+
+    Value(JSWhyMagic arg) {
+        mask = MagicMask;
+#ifdef DEBUG
+        data.why = arg;
+#endif
+    }
 
     /* XXX: 'explicit' can be removed when copying is public/implicit */
+
     explicit Value(const Value &v)
      : mask(v.mask), data(v.data)
     {}
 
-    explicit Value(int32 i32) {
-        mask = Int32Mask;
-        data.i32 = i32;
-    }
-
-    explicit Value(double dbl) {
-        mask = DoubleMask;
-        data.dbl = dbl;
-    }
-
-    Value(JSString *str) {
-        mask = StringMask;
-        data.str = str;
-    }
-
-  private:
-    /*
-     * This overload catches values constructed with unsupported T* types
-     * before they are implicitly converted to bool.
-     */
-    template <class T> explicit Value(T *);  /* undefined */
-
-  public:
-
-    explicit Value(bool b) {
-        mask = BooleanMask;
-        data.boo = b;
-    }
-
-    explicit Value(JSWhyMagic why) {
-        mask = MagicMask;
-#ifdef DEBUG
-        data.why = why;
-#endif
-    }
-
-    Value(ObjPtr ptr) {
-        mask = ptr.mask;
-        data.obj = ptr.obj;
-    }
+    /* XXX: can be removed when copy is implicit */
+    Value(const CopyableValue &);
 
     /* Mutators */
 
     void setNull() {
         mask = NullMask;
         data.obj = NULL;
     }
 
@@ -3289,50 +3349,61 @@ class Value
         return data.dbl;
     }
 
     void setString(JSString *str) {
         mask = StringMask;
         data.str = str;
     }
 
+    void setFunObj(JSObject &arg) {
+        JS_ASSERT(JS_ObjectIsFunction(NULL, &arg));
+        mask = FunObjMask;
+        data.obj = &arg;
+    }
+
+    void setFunObjOrNull(JSObject *arg) {
+        JS_ASSERT_IF(arg, JS_ObjectIsFunction(NULL, arg));
+        mask = arg ? FunObjMask : NullMask;
+        data.obj = arg;
+    }
+
+    void setFunObjOrUndefined(JSObject *arg) {
+        JS_ASSERT_IF(arg, JS_ObjectIsFunction(NULL, arg));
+        mask = arg ? FunObjMask : UndefinedMask;
+        data.obj = arg;
+    }
+
+    void setNonFunObj(JSObject &arg) {
+        JS_ASSERT(!JS_ObjectIsFunction(NULL, &arg));
+        mask = NonFunObjMask;
+        data.obj = &arg;
+    }
+
+    void setNonFunObjOrNull(JSObject *arg) {
+        JS_ASSERT_IF(arg, !JS_ObjectIsFunction(NULL, arg));
+        mask = arg ? NonFunObjMask : NullMask;
+        data.obj = arg;
+    }
+
+    inline void setObject(JSObject &arg);
+    inline void setObjectOrNull(JSObject *arg);
+
     void setBoolean(bool b) {
         mask = BooleanMask;
         data.boo = b;
     }
 
     void setMagic(JSWhyMagic why) {
         mask = MagicMask;
 #ifdef DEBUG
         data.why = why;
 #endif
     }
 
-    void setFunObj(JSObject &o) {
-        JS_ASSERT(JS_ObjectIsFunction(NULL, &o));
-        mask = FunObjMask;
-        data.obj = &o;
-    }
-
-    void setNonFunObj(JSObject &o) {
-        JS_ASSERT(!JS_ObjectIsFunction(NULL, &o));
-        mask = NonFunObjMask;
-        data.obj = &o;
-    }
-
-    void setNonFunObjOrNull(JSObject *o) {
-        if (o) {
-            mask = NonFunObjMask;
-            data.obj = o;
-        } else {
-            mask = NullMask;
-            data.obj = NULL;
-        }
-    }
-
     /* Mutators */
 
     /* XXX: to be removed when copying is implicit/public */
     void copy(ObjPtr ptr) {
         data.obj = ptr.obj;
         mask = ptr.mask;
     }
 
@@ -3491,17 +3562,17 @@ class Value
     bool isSame(const Value &v) const;
 
     /*
      * Private API
      *
      * Private setters/getters allow the caller to read/write arbitrary types
      * that fit in the 64-bit payload. It is the caller's responsibility, after
      * storing to a value with setPrivateX to only read with getPrivateX.
-     * Privates values are given a valid type of Int32 and are thus GC-safe.
+     * Privates values are given a valid type of Int32Tag and are thus GC-safe.
      */
 
     void setPrivateVoidPtr(void *p) {
         mask = Int32Mask;
         data.ptr = p;
     }
 
     void *asPrivateVoidPtr() const {
@@ -3520,98 +3591,54 @@ class Value
     }
 
     uint32 &asPrivateUint32Ref() {
         JS_ASSERT(mask == Int32Mask);
         return data.u32;
     }
 };
 
-/*
- * These types are intended to be used like:
- *
- *   Value v = NullValue();
- *   void f1(Value);
- *   f1(FunObjValue(funobj));
- */
-
-/* TODO: this hackiness will be removed once copying is public/implicit */
-struct ExplicitlyConstructedValue : Value {};
-
-JS_ALWAYS_INLINE
-Value::Value(const ExplicitlyConstructedValue &v)
-  : mask(v.mask), data(v.data)
-{}
-
-struct NullValue : ExplicitlyConstructedValue
-{
-    explicit NullValue() {
-        mask = NullMask;
-        data.obj = NULL;
-    }
-};
-
-struct UndefinedValue : ExplicitlyConstructedValue
-{
-    explicit UndefinedValue() {
-        mask = UndefinedMask;
-    }
-};
-
-struct FunObjValue : ExplicitlyConstructedValue
-{
-    explicit FunObjValue(JSObject &o) {
-        JS_ASSERT(JS_ObjectIsFunction(NULL, &o));
-        mask = FunObjMask;
-        data.obj = &o;
-    }
-};
-
-struct NonFunObjValue : ExplicitlyConstructedValue
-{
-    explicit NonFunObjValue(JSObject &o) {
-        JS_ASSERT(JS_ObjectIsFunction(NULL, &o));
-        mask = NonFunObjMask;
-        data.obj = &o;
-    }
-};
-
-struct NonFunObjOrNullValue : ExplicitlyConstructedValue
-{
-    explicit NonFunObjOrNullValue(JSObject *o) {
-        if (o) {
-            JS_ASSERT(JS_ObjectIsFunction(NULL, o));
-            mask = NonFunObjMask;
-            data.obj = o;
-        } else {
-            mask = NullMask;
-            data.obj = NULL;
-        }
-    }
-};
-
 struct AssertLayoutCompatible 
 {
     JS_STATIC_ASSERT(sizeof(jsval) == 16);
     JS_STATIC_ASSERT(sizeof(Value::MaskType) == 4);
     JS_STATIC_ASSERT(sizeof(Value::Data) == 8);
     JS_STATIC_ASSERT(offsetof(jsval, mask) == 0);
     JS_STATIC_ASSERT(offsetof(jsval, mask) == offsetof(Value, mask));
     JS_STATIC_ASSERT(sizeof(Value) == sizeof(jsval));
     JS_STATIC_ASSERT(sizeof(Value::Data) == sizeof(((jsval *)0)->data));
     JS_STATIC_ASSERT(offsetof(Value, data) == offsetof(jsval, data));
     JS_STATIC_ASSERT(sizeof(Value::MaskType) == sizeof(JSValueMaskType));
     JS_STATIC_ASSERT(sizeof(Value::MaskType) == sizeof(((jsval *)0)->mask));
     JS_STATIC_ASSERT(offsetof(Value, mask) == offsetof(jsval, mask));
-    JS_STATIC_ASSERT(sizeof(NullValue) == sizeof(Value));
-    JS_STATIC_ASSERT(sizeof(UndefinedValue) == sizeof(Value));
-    JS_STATIC_ASSERT(sizeof(FunObjValue) == sizeof(Value));
-    JS_STATIC_ASSERT(sizeof(NonFunObjValue) == sizeof(Value));
 };
 
+/* XXX: this is a temporary hack until copying is implicit. */
+struct CopyableValue : Value
+{
+    CopyableValue(NullTag arg) : Value(arg) {}
+    CopyableValue(UndefinedTag arg) : Value(arg) {}
+    CopyableValue(Int32Tag arg) : Value(arg) {}
+    CopyableValue(double arg) : Value(arg) {}
+    CopyableValue(JSString *arg) : Value(arg) {}
+    CopyableValue(FunObjTag arg) : Value(arg) {}
+    CopyableValue(NonFunObjTag arg) : Value(arg) {}
+    CopyableValue(ObjectTag arg) : Value(arg) {}
+    CopyableValue(BooleanTag arg) : Value(arg) {}
+    CopyableValue(JSWhyMagic arg) : Value(arg) {}
+};
+
+JS_ALWAYS_INLINE
+Value::Value(const CopyableValue &v) {
+    mask = v.mask;
+    data = v.data;
+}
+
+JS_STATIC_ASSERT(sizeof(CopyableValue) == sizeof(Value));
+
 /*
  * As asserted above, js::Value and jsval are layout equivalent. To provide
  * widespread casting, the following safe casts are provided.
  */
 static JS_ALWAYS_INLINE jsval *       Jsvalify(Value *v)       { return (jsval *)v; }
 static JS_ALWAYS_INLINE const jsval * Jsvalify(const Value *v) { return (const jsval *)v; }
 static JS_ALWAYS_INLINE jsval &       Jsvalify(Value &v)       { return (jsval &)v; }
 static JS_ALWAYS_INLINE const jsval & Jsvalify(const Value &v) { return (const jsval &)v; }
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -236,17 +236,17 @@ js_GetLengthProperty(JSContext *cx, JSOb
         return true;
     }
 
     if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
         *lengthp = obj->getArgsLength();
         return true;
     }
 
-    AutoValueRooter tvr(cx, NullValue());
+    AutoValueRooter tvr(cx);
     if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), tvr.addr()))
         return false;
 
     if (tvr.value().isInt32()) {
         *lengthp = jsuint(jsint(tvr.value().asInt32())); /* jsuint cast does ToUint32 */
         return true;
     }
 
@@ -271,17 +271,17 @@ js_IndexToId(JSContext *cx, jsuint index
 
     if (index <= JSVAL_INT_MAX) {
         *idp = INT_TO_JSID(index);
         return JS_TRUE;
     }
     str = js_NumberToString(cx, index);
     if (!str)
         return JS_FALSE;
-    return js_ValueToStringId(cx, Value(str), idp);
+    return js_ValueToStringId(cx, str, idp);
 }
 
 static JSBool
 BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
              jsid *idp)
 {
     jschar buf[10], *start;
     Class *clasp;
@@ -345,17 +345,17 @@ JSObject::resizeDenseArrayElements(JSCon
     if (!newslots)
         return false;
 
     dslots = newslots + 1;
     setDenseArrayCapacity(newcap);
 
     if (initializeAllSlots) {
         for (uint32 i = oldcap; i < newcap; i++)
-            setDenseArrayElement(i, Value(JS_ARRAY_HOLE));
+            setDenseArrayElement(i, JS_ARRAY_HOLE);
     }
 
     return true;
 }
 
 bool
 JSObject::ensureDenseArrayElements(JSContext *cx, uint32 newcap, bool initializeAllSlots)
 {
@@ -400,27 +400,27 @@ JSObject::ensureDenseArrayElements(JSCon
         if (!resizeDenseArrayElements(cx, oldcap, actualCapacity, initializeAllSlots))
             return false;
 
         if (!initializeAllSlots) {
             /*
              * Initialize the slots caller didn't actually ask for.
              */
             for (uint32 i = newcap; i < actualCapacity; i++) {
-                setDenseArrayElement(i, Value(JS_ARRAY_HOLE));
+                setDenseArrayElement(i, JS_ARRAY_HOLE);
             }
         }
     }
     return true;
 }
 
 static bool
 ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp)
 {
-    return js_ValueToStringId(cx, Value(index), idp);
+    return js_ValueToStringId(cx, index, idp);
 }
 
 static bool
 IndexToId(JSContext* cx, JSObject* obj, jsdouble index, JSBool* hole, jsid* idp,
           JSBool createAtom = JS_FALSE)
 {
     if (index <= JSVAL_INT_MAX) {
         *idp = INT_TO_JSID(int(index));
@@ -529,17 +529,17 @@ DeleteArrayElement(JSContext *cx, JSObje
 {
     JS_ASSERT(index >= 0);
     if (obj->isDenseArray()) {
         if (index <= jsuint(-1)) {
             jsuint idx = jsuint(index);
             if (!INDEX_TOO_SPARSE(obj, idx) && idx < obj->getDenseArrayCapacity()) {
                 if (!obj->getDenseArrayElement(idx).isMagic(JS_ARRAY_HOLE))
                     obj->decDenseArrayCountBy(1);
-                obj->setDenseArrayElement(idx, Value(JS_ARRAY_HOLE));
+                obj->setDenseArrayElement(idx, JS_ARRAY_HOLE);
                 return JS_TRUE;
             }
         }
         return JS_TRUE;
     }
 
     AutoIdRooter idr(cx);
 
@@ -577,17 +577,17 @@ js_SetLengthProperty(JSContext *cx, JSOb
     id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
     return obj->setProperty(cx, id, &v);
 }
 
 JSBool
 js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
 {
     JSErrorReporter older = JS_SetErrorReporter(cx, NULL);
-    AutoValueRooter tvr(cx, NullValue());
+    AutoValueRooter tvr(cx);
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
     JSBool ok = obj->getProperty(cx, id, tvr.addr());
     JS_SetErrorReporter(cx, older);
     if (!ok)
         return false;
 
     *lengthp = ValueIsLength(cx, tvr.addr());
     return !tvr.value().isNull();
@@ -1045,17 +1045,17 @@ array_deleteProperty(JSContext *cx, JSOb
     if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
         rval->setBoolean(false);
         return JS_TRUE;
     }
 
     if (js_IdIsIndex(id, &i) && i < obj->getDenseArrayCapacity() &&
         !obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
         obj->decDenseArrayCountBy(1);
-        obj->setDenseArrayElement(i, Value(JS_ARRAY_HOLE));
+        obj->setDenseArrayElement(i, JS_ARRAY_HOLE);
     }
 
     rval->setBoolean(true);
     return JS_TRUE;
 }
 
 static void
 array_finalize(JSContext *cx, JSObject *obj)
@@ -1158,17 +1158,17 @@ js_MakeArraySlow(JSContext *cx, JSObject
     for (uint32 i = 0; i < capacity; i++) {
         jsid id;
         JSScopeProperty *sprop;
 
         if (!JS_ValueToId(cx, INT_TO_JSVAL(i), &id))
             goto out_bad;
 
         if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
-            obj->setDenseArrayElement(i, UndefinedValue());
+            obj->setDenseArrayElement(i, UndefinedTag());
             continue;
         }
 
         sprop = scope->addDataProperty(cx, id, JS_INITIAL_NSLOTS + i,
                                        JSPROP_ENUMERATE);
         if (!sprop)
             goto out_bad;
     }
@@ -1523,17 +1523,17 @@ InitArrayElements(JSContext *cx, JSObjec
 
     /* Finish out any remaining elements past the max array index. */
     if (obj->isDenseArray() && !ENSURE_SLOW_ARRAY(cx, obj))
         return JS_FALSE;
 
     JS_ASSERT(start == MAXINDEX);
     AutoValueRooter tvr(cx);
     AutoIdRooter idr(cx);
-    Value idval(double(MAXINDEX));
+    Value idval(MAXINDEX);
     do {
         tvr.addr()->copy(*vector++);
         if (!js_ValueToStringId(cx, idval, idr.addr()) ||
             !obj->setProperty(cx, idr.id(), tvr.addr())) {
             return JS_FALSE;
         }
         idval.asDoubleRef() += 1;
     } while (vector != end);
@@ -1596,17 +1596,17 @@ array_join(JSContext *cx, uintN argc, Va
 
 static JSBool
 array_reverse(JSContext *cx, uintN argc, Value *vp)
 {
     jsuint len;
     JSObject *obj = ComputeThisObjectFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &len))
         return JS_FALSE;
-    SetObject(vp, *obj);
+    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;
 
         /*
          * It's actually surprisingly complicated to reverse an array due to the
@@ -1894,17 +1894,17 @@ array_sort(JSContext *cx, uintN argc, Va
     } else {
         fval.setNull();
     }
 
     JSObject *obj = ComputeThisObjectFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &len))
         return false;
     if (len == 0) {
-        SetObject(vp, *obj);
+        vp->setObject(*obj);
         return true;
     }
 
     /*
      * We need a temporary array of 2 * len Value to hold the array elements
      * and the scratch space for merge sort. Check that its size does not
      * overflow size_t, which would allow for indexing beyond the end of the
      * malloc'd vector.
@@ -2105,17 +2105,17 @@ array_sort(JSContext *cx, uintN argc, Va
         }
     }
 
     /* Re-create any holes that sorted to the end of the array. */
     while (len > newlen) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) || !DeleteArrayElement(cx, obj, --len))
             return JS_FALSE;
     }
-    SetObject(vp, *obj);
+    vp->setObject(*obj);
     return true;
 }
 
 /*
  * Perl-inspired push, pop, shift, unshift, and splice methods.
  */
 static JSBool
 array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
@@ -2268,17 +2268,17 @@ array_shift(JSContext *cx, uintN argc, V
             length < obj->getDenseArrayCapacity()) {
             vp->copy(obj->getDenseArrayElement(0));
             if (vp->isMagic(JS_ARRAY_HOLE))
                 vp->setUndefined();
             else
                 obj->decDenseArrayCountBy(1);
             Value *elems = obj->getDenseArrayElements();
             memmove(elems, elems + 1, length * sizeof(jsval));
-            obj->setDenseArrayElement(length, Value(JS_ARRAY_HOLE));
+            obj->setDenseArrayElement(length, JS_ARRAY_HOLE);
             obj->setDenseArrayLength(length);
             return JS_TRUE;
         }
 
         /* Get the to-be-deleted property's value into vp ASAP. */
         if (!GetArrayElement(cx, obj, 0, &hole, vp))
             return JS_FALSE;
 
@@ -2318,17 +2318,17 @@ array_unshift(JSContext *cx, uintN argc,
             if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
                 !INDEX_TOO_SPARSE(obj, unsigned(newlen + argc))) {
                 JS_ASSERT(newlen + argc == length + argc);
                 if (!obj->ensureDenseArrayElements(cx, length + argc))
                     return JS_FALSE;
                 Value *elems = obj->getDenseArrayElements();
                 memmove(elems + argc, elems, length * sizeof(jsval));
                 for (uint32 i = 0; i < argc; i++)
-                    obj->setDenseArrayElement(i, Value(JS_ARRAY_HOLE));
+                    obj->setDenseArrayElement(i, JS_ARRAY_HOLE);
             } else {
                 last = length;
                 jsdouble upperIndex = last + argc;
                 AutoValueRooter tvr(cx);
                 do {
                     --last, --upperIndex;
                     if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                         !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
@@ -2408,17 +2408,17 @@ array_splice(JSContext *cx, uintN argc, 
         else if (d > delta)
             d = delta;
         count = (jsuint)d;
         end = begin + count;
         argc--;
         argv++;
     }
 
-    AutoValueRooter tvr(cx, NullValue());
+    AutoValueRooter tvr(cx);
 
     /* If there are elements to remove, put them into the return value. */
     if (count > 0) {
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
             !js_PrototypeHasIndexedProperties(cx, obj2) &&
             end <= obj->getDenseArrayCapacity()) {
             if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin,
                                  obj->getDenseArrayCount() != obj->getArrayLength())) {
@@ -2540,17 +2540,17 @@ array_concat(JSContext *cx, uintN argc, 
     } else {
         nobj = js_NewArrayObject(cx, 0, NULL);
         if (!nobj)
             return JS_FALSE;
         vp->setNonFunObj(*nobj);
         length = 0;
     }
 
-    AutoValueRooter tvr(cx, NullValue());
+    AutoValueRooter tvr(cx);
 
     /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
     for (uintN i = 0; i <= argc; i++) {
         if (!JS_CHECK_OPERATION_LIMIT(cx))
             return false;
         const Value &v = p[i];
         if (v.isObject()) {
             JSObject *wobj;
@@ -2869,19 +2869,19 @@ array_extra(JSContext *cx, ArrayExtraMod
         return JS_FALSE;
 
     MUST_FLOW_THROUGH("out");
     JSBool ok = JS_TRUE;
     JSBool cond;
     Value *invokevp = args.getvp();
 
     Value calleev, thisv, objv;
-    SetObject(&calleev, callable);
-    SetObject(&thisv, thisp);
-    SetObject(&objv, obj);
+    calleev.setObject(*callable);
+    thisv.setObjectOrNull(thisp);
+    objv.setObject(*obj);
     AutoValueRooter tvr(cx);
     for (jsint i = start; i != end; i += step) {
         JSBool hole;
         ok = JS_CHECK_OPERATION_LIMIT(cx) &&
              GetArrayElement(cx, obj, i, &hole, tvr.addr());
         if (!ok)
             goto out;
         if (hole)
@@ -3093,17 +3093,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, NonFunObjPtr(*proto), proto->getParent(), NullValue());
+    obj->init(&js_ArrayClass, NonFunObjTag(*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/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -90,17 +90,17 @@ JS_STATIC_ASSERT(ATOM_OFFSET_LIMIT % siz
 JS_STATIC_ASSERT(1 * sizeof(JSAtom *) ==
                  offsetof(JSAtomState, booleanAtoms) - ATOM_OFFSET_START);
 JS_STATIC_ASSERT((1 + 2) * sizeof(JSAtom *) ==
                  offsetof(JSAtomState, typeAtoms) - ATOM_OFFSET_START);
 
 const char *
 js_AtomToPrintableString(JSContext *cx, JSAtom *atom)
 {
-    return js_ValueToPrintableString(cx, Value(ATOM_TO_STRING(atom)));
+    return js_ValueToPrintableString(cx, ATOM_TO_STRING(atom));
 }
 
 #define JS_PROTO(name,code,init) const char js_##name##_str[] = #name;
 #include "jsproto.tbl"
 #undef JS_PROTO
 
 /*
  * String constants for common atoms defined in JSAtomState starting from
@@ -1294,17 +1294,17 @@ js_InternNonIntElementIdSlow(JSContext *
 bool
 js_InternNonIntElementIdSlow(JSContext *cx, JSObject *obj, const Value &idval,
                              jsid *idp, Value *vp)
 {
     JS_ASSERT(idval.isObject());
     if (obj->isXML()) {
         JSObject &idobj = idval.asObject();
         *idp = OBJECT_TO_JSID(&idobj);
-        SetObject(vp, idobj);
+        vp->setObject(idobj);
         return true;
     }
 
     if (!js_IsFunctionQName(cx, &idval.asObject(), idp))
         return JS_FALSE;
     if (*idp != 0) {
         vp->copy(IdToValue(*idp));
         return true;
@@ -1346,21 +1346,18 @@ ValueToId(JSContext *cx, const Value *vp
 
 /*
  * Normally, js::Value should not be passed by value, but this function should
  * only be used on cold paths, so ease of use wins out.
  */
 Value
 IdToValue(jsid id)
 {
-    ExplicitlyConstructedValue v;
     if (JSID_IS_INT(id))
-        v.setInt32(JSID_TO_INT(id));
+        return CopyableValue(Int32Tag(JSID_TO_INT(id)));
     else if (JSID_IS_ATOM(id))
-        v.setString(ATOM_TO_STRING(JSID_TO_ATOM(id)));
+        return CopyableValue(ATOM_TO_STRING(JSID_TO_ATOM(id)));
     else if (JSID_IS_NULL(id))
-        v.setNull();
-    else
-        SetObject(&v, *JSID_TO_OBJECT(id));
-    return v;
+        return CopyableValue(NullTag());
+    return CopyableValue(ObjectTag(*JSID_TO_OBJECT(id)));
 }
 
 } /* namespace js */
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -93,17 +93,17 @@ js_Int32ToId(JSContext* cx, int32 index,
 {
     if (INT32_FITS_IN_JSID(index)) {
         *id = INT_TO_JSID(index);
         return true;
     }
     JSString* str = js_NumberToString(cx, index);
     if (!str)
         return false;
-    return js_ValueToStringId(cx, js::Value(str), id);
+    return js_ValueToStringId(cx, str, id);
 }
 
 inline bool
 js_InternNonIntElementId(JSContext *cx, JSObject *obj, const js::Value &idval,
                          jsid *idp)
 {
     JS_ASSERT_IF(idval.isInt32(), !INT32_FITS_IN_JSID(idval.asInt32()));
 
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -147,17 +147,17 @@ JSObject *
 js_InitBooleanClass(JSContext *cx, JSObject *obj)
 {
     JSObject *proto;
 
     proto = js_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1,
                          NULL, boolean_methods, NULL, NULL);
     if (!proto)
         return NULL;
-    proto->setPrimitiveThis(Value(false));
+    proto->setPrimitiveThis(BooleanTag(false));
     return proto;
 }
 
 JSString *
 js_BooleanToString(JSContext *cx, JSBool b)
 {
     return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]);
 }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2237,17 +2237,17 @@ class AutoPreserveWeakRoots : private Au
 };
 
 /* FIXME(bug 332648): Move this into a public header. */
 class AutoValueRooter : private AutoGCRooter
 {
   public:
     explicit AutoValueRooter(JSContext *cx
                              JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, JSVAL), val(js::NullValue())
+      : AutoGCRooter(cx, JSVAL), val(js::NullTag())
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     AutoValueRooter(JSContext *cx, const Value &v
                              JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, JSVAL)
     {
@@ -2985,17 +2985,17 @@ class AutoValueVector : private AutoGCRo
         : AutoGCRooter(cx, VECTOR), vector(cx)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     size_t length() const { return vector.length(); }
 
     bool append(const Value &v) { return vector.append(v); }
-    bool append(JSString *str) { return append(Value(str)); }
+    bool append(JSString *str) { return append(str); }
 
     void popBack() { vector.popBack(); }
 
     bool resize(size_t newLength) {
         return vector.resize(newLength);
     }
 
     bool reserve(size_t newLength) {
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -674,17 +674,17 @@ js_watch_set(JSContext *cx, JSObject *ob
                     DBG_LOCK(rt);
                     DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
                     return JS_FALSE;
                 }
 
                 /* Initialize slots/frame. */
                 Value *vp = frame.getvp();
                 PodZero(vp, vplen);
-                SetObject(&vp[0], closure);
+                vp[0].setObject(*closure);
                 JSStackFrame *fp = frame.getFrame();
                 PodZero(fp->slots(), nfixed);
                 PodZero(fp);
                 fp->script = script;
                 fp->fun = fun;
                 fp->argv = vp + 2;
                 fp->scopeChain = closure->getParent();
 
@@ -702,17 +702,17 @@ js_watch_set(JSContext *cx, JSObject *ob
                     DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
                     return JS_FALSE;
                 }
             }
 
             JSBool ok = !wp->setter ||
                         (sprop->hasSetterValue()
                          ? InternalCall(cx, obj,
-                                        ToValue(CastAsObject(wp->setter)),
+                                        ObjectTag(*CastAsObject(wp->setter)),
                                         1, vp, vp)
                          : wp->setter(cx, obj, userid, vp));
 
             /* Evil code can cause us to have an arguments object. */
             if (frame.getFrame())
                 frame.getFrame()->putActivationObjects(cx);
 
             DBG_LOCK(rt);
--- a/js/src/jsdtoa.cpp
+++ b/js/src/jsdtoa.cpp
@@ -48,16 +48,18 @@
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsprvtd.h"
 #include "jsnum.h"
 #include "jsbit.h"
 #include "jslibmath.h"
 #include "jscntxt.h"
 
+#include "jsobjinlines.h"
+
 #ifdef IS_LITTLE_ENDIAN
 #define IEEE_8087
 #else
 #define IEEE_MC68k
 #endif
 
 #ifndef Long
 #define Long int32
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -280,17 +280,18 @@ InitExnPrivate(JSContext *cx, JSObject *
     older = JS_SetErrorReporter(cx, NULL);
     state = JS_SaveExceptionState(cx);
 
     callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom);
     stackDepth = 0;
     valueCount = 0;
     for (fp = js_GetTopStackFrame(cx); fp; fp = fp->down) {
         if (fp->fun && fp->argv) {
-            NullValue v;
+            Value v;
+            v.setNull();
             if (checkAccess &&
                 !checkAccess(cx, fp->callee(), callerid, JSACC_READ, &v)) {
                 break;
             }
             valueCount += fp->argc;
         }
         ++stackDepth;
     }
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -171,18 +171,18 @@ 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, NonFunObjPtr(*proto), parent, NullValue());
-    argsobj->setArgsCallee(ToValue(callee));
+    argsobj->init(&js_ArgumentsClass, NonFunObjTag(*proto), parent, NullTag());
+    argsobj->setArgsCallee(ObjectOrNullTag(callee));
     argsobj->setArgsLength(argc);
 
     argsobj->map = cx->runtime->emptyArgumentsScope;
     cx->runtime->emptyArgumentsScope->hold();
 
     /* This must come after argsobj->map has been set. */
     if (!js_EnsureReservedSlots(cx, argsobj, argc))
         return NULL;
@@ -836,17 +836,17 @@ js_GetCallObject(JSContext *cx, JSStackF
 
 JSObject * JS_FASTCALL
 js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain)
 {
     JS_ASSERT(!js_IsNamedLambda(fun));
     JSObject *callobj = NewCallObject(cx, fun, scopeChain);
     if (!callobj)
         return NULL;
-    callobj->setSlot(JSSLOT_CALLEE, NonFunObjValue(*callee));
+    callobj->setSlot(JSSLOT_CALLEE, NonFunObjTag(*callee));
     return callobj;
 }
 
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT,
                      0, nanojit::ACC_STORE_ANY)
 
 JSFunction *
 js_GetCallObjectFunction(JSObject *obj)
@@ -872,17 +872,17 @@ 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->flags & JSFRAME_OVERRIDE_ARGS))
-            callobj->setSlot(JSSLOT_CALL_ARGUMENTS, NonFunObjValue(*fp->argsobj));
+            callobj->setSlot(JSSLOT_CALL_ARGUMENTS, NonFunObjTag(*fp->argsobj));
         js_PutArgsObject(cx, fp);
     }
 
     JSFunction *fun = fp->fun;
     JS_ASSERT(fun == js_GetCallObjectFunction(callobj));
     uintN n = fun->countArgsAndVars();
 
     /*
@@ -1718,17 +1718,17 @@ fun_hasInstance(JSContext *cx, JSObject 
     if (!obj->getProperty(cx, id, &pval))
         return JS_FALSE;
 
     if (pval.isPrimitive()) {
         /*
          * Throw a runtime error if instanceof is called on a function that
          * has a non-object as its .prototype value.
          */
-        js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, ToValue(obj), NULL);
+        js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, ObjectTag(*obj), NULL);
         return JS_FALSE;
     }
 
     *bp = js_IsDelegate(cx, &pval.asObject(), pval);
     return JS_TRUE;
 }
 
 static void
@@ -2522,17 +2522,17 @@ js_DefineFunction(JSContext *cx, JSObjec
         attrs &= ~JSFUN_STUB_GSOPS;
         gsop = PropertyStub;
     } else {
         gsop = NULL;
     }
     fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
     if (!fun)
         return NULL;
-    if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), FunObjValue(*FUN_OBJECT(fun)),
+    if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), fun->funObjVal(),
                              gsop, gsop, attrs & ~JSFUN_FLAGS_MASK)) {
         return NULL;
     }
     return fun;
 }
 
 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -194,16 +194,20 @@ struct JSFunction : public JSObject
      */
     JSAtom *findDuplicateFormal() const;
 
     uint32 countInterpretedReservedSlots() const;
 
     bool mightEscape() const {
         return FUN_INTERPRETED(this) && (FUN_FLAT_CLOSURE(this) || u.i.nupvars == 0);
     }
+
+    js::FunObjTag funObjVal() {
+        return js::FunObjTag(*this);
+    }
 };
 
 /*
  * Trace-annotated native. This expands to a JSFunctionSpec initializer (like
  * JS_FN in jsapi.h). fastcall is a JSFastNative; trcinfo is a
  * JSNativeTraceInfo*.
  */
 #ifdef JS_TRACER
@@ -304,28 +308,27 @@ js_NewFunction(JSContext *cx, JSObject *
 
 extern void
 js_TraceFunction(JSTracer *trc, JSFunction *fun);
 
 extern void
 js_FinalizeFunction(JSContext *cx, JSFunction *fun);
 
 extern JSObject *
-js_CloneFunctionObject(JSContext *cx, JSFunction *fun, const js::Value &parent,
-                       const js::Value &proto);
+js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
+                       JSObject *proto);
 
 inline JSObject *
 CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
 {
     JS_ASSERT(parent);
     JSObject *proto;
     if (!js_GetClassPrototype(cx, parent, JSProto_Function, &proto))
         return NULL;
-    return js_CloneFunctionObject(cx, fun, js::NonFunObjValue(*parent),
-                                  js::FunObjValue(*proto));
+    return js_CloneFunctionObject(cx, fun, parent, proto);
 }
 
 extern JS_REQUIRES_STACK JSObject *
 js_NewFlatClosure(JSContext *cx, JSFunction *fun);
 
 extern JS_REQUIRES_STACK JSObject *
 js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2338,17 +2338,17 @@ ProcessSetSlotRequest(JSContext *cx, JSS
             ssr->cycle = true;
             return;
         }
         pobj = pobj->getSlot(slot).asObjectOrNull();
     }
 
     pobj = ssr->pobj;
     if (slot == JSSLOT_PROTO) {
-        obj->setProto(ToObjPtr(pobj));
+        obj->setProto(ObjectOrNullTag(pobj));
     } else {
         JS_ASSERT(slot == JSSLOT_PARENT);
         obj->setParent(pobj);
     }
 }
 
 void
 js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data)
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -268,17 +268,17 @@ CallThisObjectHook(JSContext *cx, ObjPtr
 JS_STATIC_INTERPRET bool
 ComputeGlobalThis(JSContext *cx, Value *argv)
 {
     JSObject *thisp;
     if (argv[-2].isPrimitive() || !argv[-2].asObject().getParent())
         thisp = cx->globalObject;
     else
         thisp = argv[-2].asObject().getGlobal();
-    return CallThisObjectHook(cx, NonFunObjPtr(*thisp), argv);
+    return CallThisObjectHook(cx, NonFunObjTag(*thisp), argv);
 }
 
 static bool
 ComputeThis(JSContext *cx, Value *argv)
 {
     JS_ASSERT(!argv[-1].isNull());
     if (!argv[-1].isObject())
         return js_PrimitiveToObject(cx, &argv[-1]);
@@ -477,17 +477,17 @@ Invoke(JSContext *cx, const InvokeArgsGu
             }
         } else {
             native = fun->u.n.native;
             script = NULL;
         }
 
         if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
             /* Handle bound method special case. */
-            SetObject(&vp[1], parent);
+            vp[1].setObjectOrNull(parent);
         } else if (vp[1].isPrimitive()) {
             JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
             if (PrimitiveThisTest(fun, vp[1]))
                 goto start_call;
         }
     }
 
     if (flags & JSINVOKE_CONSTRUCT) {
@@ -662,17 +662,17 @@ InternalInvoke(JSContext *cx, JSObject *
 {
     LeaveTrace(cx);
 
     InvokeArgsGuard args;
     if (!cx->stack().pushInvokeArgs(cx, argc, args))
         return JS_FALSE;
 
     args.getvp()[0].copy(fval);
-    SetObject(&args.getvp()[1], obj);
+    args.getvp()[1].setObjectOrNull(obj);
     memcpy(args.getvp() + 2, argv, argc * sizeof(*argv));
 
     if (!Invoke(cx, args, flags))
         return JS_FALSE;
 
     /*
      * Store *rval in the a scoped local root if a scope is open, else in
      * the lastInternalResult pigeon-hole GC root, solely so users of
@@ -787,17 +787,17 @@ Execute(JSContext *cx, JSObject *chain, 
         fp->argsobj = NULL;
         JSObject *obj = chain;
         if (cx->options & JSOPTION_VAROBJFIX)
             obj = obj->getGlobal();
         fp->fun = NULL;
         JSObject *thisp = chain->thisObject(cx);
         if (!thisp)
             return false;
-        SetObject(&fp->thisv, *thisp);
+        fp->thisv.setObject(*thisp);
         fp->flags = flags | JSFRAME_COMPUTED_THIS;
         fp->argc = 0;
         fp->argv = NULL;
         fp->annotation = NULL;
         Innerize(cx, &chain);
         if (!chain)
             return false;
         fp->scopeChain = chain;
@@ -1100,30 +1100,30 @@ InvokeConstructor(JSContext *cx, const I
                 clasp = f->u.n.clasp;
         }
     }
     JSObject *obj = NewObject(cx, clasp, proto, parent);
     if (!obj)
         return JS_FALSE;
 
     /* Now we have an object with a constructor method; call it. */
-    SetObject(&vp[1], *obj);
+    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. */
     if (clampReturn && vp->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;
         }
-        SetObject(vp, *obj);
+        vp->setObject(*obj);
     }
 
     JS_RUNTIME_METER(cx->runtime, constructs);
     return JS_TRUE;
 }
 
 } /* namespace js */
 
@@ -1671,28 +1671,28 @@ namespace reprmeter {
                 fprintf(f, ",%s", reprName[o.uses[i]]);
             fprintf(f, ",%llu\n", c);
         }
         fclose(f);
     }
 }
 #endif /* JS_REPRMETER */
 
-#define PUSH_COPY(v)            regs.sp++->copy(v)
-#define PUSH_NULL()             regs.sp++->setNull()
-#define PUSH_UNDEFINED()        regs.sp++->setUndefined()
-#define PUSH_BOOLEAN(b)         regs.sp++->setBoolean(b)
-#define PUSH_INT32(i)           regs.sp++->setInt32(i)
-#define PUSH_STRING(s)          regs.sp++->setString(s)
-#define PUSH_NONFUNOBJ(o)       regs.sp++->setNonFunObj(o)
-#define PUSH_FUNOBJ(o)          regs.sp++->setFunObj(o)
-#define PUSH_OBJECT(o)          SetObject(regs.sp++, o)
-#define PUSH_OBJECT_OR_NULL(o)  SetObject(regs.sp++, o)
-#define PUSH_HOLE()             regs.sp++->setMagic(JS_ARRAY_HOLE)
-#define POP_COPY_TO(v)          v.copy(*--regs.sp)
+#define PUSH_COPY(v)             regs.sp++->copy(v)
+#define PUSH_NULL()              regs.sp++->setNull()
+#define PUSH_UNDEFINED()         regs.sp++->setUndefined()
+#define PUSH_BOOLEAN(b)          regs.sp++->setBoolean(b)
+#define PUSH_INT32(i)            regs.sp++->setInt32(i)
+#define PUSH_STRING(s)           regs.sp++->setString(s)
+#define PUSH_NONFUNOBJ(obj)      regs.sp++->setNonFunObj(obj)
+#define PUSH_FUNOBJ(obj)         regs.sp++->setFunObj(obj)
+#define PUSH_OBJECT(obj)         regs.sp++->setObject(obj)
+#define PUSH_OBJECT_OR_NULL(obj) regs.sp++->setObject(obj)
+#define PUSH_HOLE()              regs.sp++->setMagic(JS_ARRAY_HOLE)
+#define POP_COPY_TO(v)           v.copy(*--regs.sp)
 
 /*
  * Push the jsdouble d using sp from the lexical environment. Try to convert d
  * to a jsint that fits in a jsval, otherwise GC-alloc space for it and push a
  * reference.
  */
 #define STORE_NUMBER(cx, n, d)                                                \
     JS_BEGIN_MACRO                                                            \
@@ -1854,30 +1854,30 @@ AssertValidPropertyCacheHit(JSContext *c
 
     JSScopeProperty *sprop = (JSScopeProperty *) prop;
     if (entry->vword.isSlot()) {
         JS_ASSERT(entry->vword.toSlot() == sprop->slot);
         JS_ASSERT(!sprop->isMethod());
     } else if (entry->vword.isSprop()) {
         JS_ASSERT(entry->vword.toSprop() == sprop);
         JS_ASSERT_IF(sprop->isMethod(),
-                     sprop->methodValue().isSame(pobj->lockedGetSlot(sprop->slot)));
+                     &sprop->methodFunObj() == &pobj->lockedGetSlot(sprop->slot).asFunObj());
     } else {
         Value v;
         JS_ASSERT(entry->vword.isFunObj());
         JS_ASSERT(!entry->vword.isNull());
         JS_ASSERT(pobj->scope()->brandedOrHasMethodBarrier());
         JS_ASSERT(sprop->hasDefaultGetterOrIsMethod());
         JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, pobj->scope()));
         v.copy(pobj->lockedGetSlot(sprop->slot));
         JS_ASSERT(&entry->vword.toFunObj() == &v.asFunObj());
 
         if (sprop->isMethod()) {
             JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_CALLOP);
-            JS_ASSERT(sprop->methodValue().isSame(v));
+            JS_ASSERT(&sprop->methodFunObj() == &v.asFunObj());
         }
     }
 
     pobj->dropProperty(cx, prop);
     return true;
 }
 
 #else
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -471,15 +471,15 @@ js_MeterSlotOpcode(JSOp op, uint32 slot)
 #endif /* JS_LONE_INTERPRET */
 
 inline js::ObjPtr
 JSStackFrame::getThisObject(JSContext *cx)
 {
     if (flags & JSFRAME_COMPUTED_THIS)
         return thisv.asObjPtr();  /* JSVAL_COMPUTED_THIS invariant */
     if (!js::ComputeThisFromArgv(cx, argv))
-        return js::NullObjPtr();
+        return js::NullTag();
     thisv.copy(argv[-1]);
     flags |= JSFRAME_COMPUTED_THIS;
     return thisv.asObjPtr();
 }
 
 #endif /* jsinterp_h___ */
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -98,19 +98,19 @@ ExtendedClass js_IteratorClass = {
     NULL,             NULL,            NULL,             iterator_iterator,
     NULL,
     JSCLASS_NO_RESERVED_MEMBERS
 };
 
 void
 NativeIterator::mark(JSTracer *trc)
 {
-    for (jsval *vp = props_array; vp < props_end; ++vp) {
+    for (Value *vp = props_array; vp < props_end; ++vp) {
         JS_SET_TRACING_INDEX(trc, "props", (vp - props_array));
-        js_CallValueTracerIfGCThing(trc, *vp);
+        CallGCMarkerIfGCThing(trc, *vp);
     }
 }
 
 /*
  * 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
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -159,17 +159,17 @@ obj_getProto(JSContext *cx, JSObject *ob
              * DeclEnv only exists as a parent for a Call object which we
              * censor. So it cannot escape to scripts.
              */
             JS_ASSERT(clasp != &js_DeclEnvClass);
             if (JSObjectOp thisObject = pobj->map->ops->thisObject) {
                 pobj = thisObject(cx, pobj);
                 if (!pobj)
                     return JS_FALSE;
-                SetObject(vp, *pobj);
+                vp->setObject(*pobj);
             }
         }
     }
     return JS_TRUE;
 }
 
 static JSBool
 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
@@ -234,17 +234,17 @@ js_SetProtoOrParent(JSContext *cx, JSObj
             JSObject *tmp = oldproto->getProto();
             JS_UNLOCK_OBJ(cx, oldproto);
             oldproto = tmp;
         }
     }
 
     if (!pobj || !checkForCycles) {
         if (slot == JSSLOT_PROTO)
-            obj->setProto(ToObjPtr(pobj));
+            obj->setProto(ObjectOrNullTag(pobj));
         else
             obj->setParent(pobj);
     } else {
         /*
          * Use the GC machinery to serialize access to all objects on the
          * prototype or parent chain.
          */
         JSSetSlotRequest ssr;
@@ -327,24 +327,24 @@ MarkSharpObjects(JSContext *cx, JSObject
                 continue;
             ok = obj2->getAttributes(cx, id, prop, &attrs);
             if (ok) {
                 if (obj2->isNative() &&
                     (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
                     JSScopeProperty *sprop = (JSScopeProperty *) prop;
                     val.setUndefined();
                     if (attrs & JSPROP_GETTER)
-                        val.copy(sprop->getterValue());
+                        val.setFunObjOrUndefined(sprop->getterObject());
                     if (attrs & JSPROP_SETTER) {
                         if (!val.isUndefined()) {
                             /* Mark the getter, then set val to setter. */
                             ok = (MarkSharpObjects(cx, &val.asFunObj(), NULL)
                                   != NULL);
                         }
-                        val.copy(sprop->setterValue());
+                        val.setFunObjOrUndefined(sprop->setterObject());
                     }
                 } else {
                     ok = obj->getProperty(cx, id, &val);
                 }
             }
             obj2->dropProperty(cx, prop);
             if (!ok)
                 break;
@@ -570,17 +570,17 @@ obj_toSource(JSContext *cx, uintN argc, 
     JSProperty *prop;
     uintN attrs;
     Value *val;
     JSString *gsop[2];
     JSString *idstr, *valstr, *str;
 
     JS_CHECK_RECURSION(cx, return JS_FALSE);
 
-    Value localroot[4] = {NullValue(), NullValue(), NullValue(), NullValue()};
+    CopyableValue localroot[4] = { NullTag(), NullTag(), NullTag(), NullTag() };
     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);
     if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
         ok = JS_FALSE;
         goto out;
@@ -669,22 +669,22 @@ obj_toSource(JSContext *cx, uintN argc, 
             ok = obj2->getAttributes(cx, id, prop, &attrs);
             if (!ok) {
                 obj2->dropProperty(cx, prop);
                 goto error;
             }
             if (obj2->isNative() && (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
                 JSScopeProperty *sprop = (JSScopeProperty *) prop;
                 if (attrs & JSPROP_GETTER) {
-                    val[valcnt].copy(sprop->getterValue());
+                    val[valcnt].setFunObjOrUndefined(sprop->getterObject());
                     gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.getAtom);
                     valcnt++;
                 }
                 if (attrs & JSPROP_SETTER) {
-                    val[valcnt].copy(sprop->setterValue());
+                    val[valcnt].setFunObjOrUndefined(sprop->setterObject());
                     gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.setAtom);
                     valcnt++;
                 }
             } else {
                 valcnt = 1;
                 gsop[0] = NULL;
                 ok = obj->getProperty(cx, id, &val[0]);
             }
@@ -1243,17 +1243,17 @@ obj_eval(JSContext *cx, uintN argc, Valu
         if (scopeobj->getParent()) {
             JSObject *global = scopeobj->getGlobal();
             withGuard.obj = js_NewWithObject(cx, scopeobj, global, 0);
             if (!withGuard.obj)
                 return JS_FALSE;
 
             scopeobj = withGuard.obj;
             JS_ASSERT(argc >= 2);
-            SetObject(&argv[1], *withGuard.obj);
+            argv[1].setObject(*withGuard.obj);
         }
 
         /* We're pretending that we're in global code. */
         staticLevel = 0;
     }
 
     /* Ensure we compile this eval with the right object in the scope chain. */
     JSObject *result = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
@@ -1429,17 +1429,17 @@ obj_watch_handler(JSContext *cx, JSObjec
         return JS_FALSE;
     if (!entry)
         return JS_TRUE;
     generation = cx->resolvingTable->generation;
 
     argv[0].copy(IdToValue(id));
     argv[1].copy(*old);
     argv[2].copy(*nvp);
-    ok = InternalCall(cx, obj, ToValue(callable), 3, argv, nvp);
+    ok = InternalCall(cx, obj, ObjectOrNullTag(callable), 3, argv, nvp);
     js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
     return ok;
 }
 
 static JSBool
 obj_watch(JSContext *cx, uintN argc, Value *vp)
 {
     if (argc <= 1) {
@@ -1724,17 +1724,17 @@ obj_lookupGetter(JSContext *cx, uintN ar
     JSProperty *prop;
     if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
         return JS_FALSE;
     vp->setUndefined();
     if (prop) {
         if (pobj->isNative()) {
             JSScopeProperty *sprop = (JSScopeProperty *) prop;
             if (sprop->hasGetterValue())
-                vp->copy(sprop->getterValue());
+                vp->setFunObjOrUndefined(sprop->getterObject());
         }
         pobj->dropProperty(cx, prop);
     }
     return JS_TRUE;
 }
 
 static JSBool
 obj_lookupSetter(JSContext *cx, uintN argc, Value *vp)
@@ -1747,17 +1747,17 @@ obj_lookupSetter(JSContext *cx, uintN ar
     JSProperty *prop;
     if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
         return JS_FALSE;
     vp->setUndefined();
     if (prop) {
         if (pobj->isNative()) {
             JSScopeProperty *sprop = (JSScopeProperty *) prop;
             if (sprop->hasSetterValue())
-                vp->copy(sprop->setterValue());
+                vp->setFunObjOrUndefined(sprop->setterObject());
         }
         pobj->dropProperty(cx, prop);
     }
     return JS_TRUE;
 }
 #endif /* OLD_GETTER_SETTER_METHODS */
 
 JSBool
@@ -1797,41 +1797,41 @@ js_GetOwnPropertyDescriptor(JSContext *c
     }
 
     uintN attrs;
     if (!pobj->getAttributes(cx, id, prop, &attrs)) {
         pobj->dropProperty(cx, prop);
         return false;
     }
 
-    Value roots[] = { UndefinedValue(), UndefinedValue() };
+    CopyableValue roots[2] = { UndefinedTag(), UndefinedTag() };
     AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
         if (obj->isNative()) {
             JSScopeProperty *sprop = reinterpret_cast<JSScopeProperty *>(prop);
             if (attrs & JSPROP_GETTER)
-                roots[0].copy(sprop->getterValue());
+                roots[0].setFunObjOrUndefined(sprop->getterObject());
             if (attrs & JSPROP_SETTER)
-                roots[1].copy(sprop->setterValue());
+                roots[1].setFunObjOrUndefined(sprop->setterObject());
         }
 
         pobj->dropProperty(cx, prop);
     } else {
         pobj->dropProperty(cx, prop);
 
         if (!obj->getProperty(cx, id, &roots[0]))
             return false;
     }
 
 
     /* We have our own property, so start creating the descriptor. */
     JSObject *desc = NewObject(cx, &js_ObjectClass, NULL, NULL);
     if (!desc)
         return false;
-    SetObject(vp, *desc);    /* Root and return. */
+    vp->setObject(*desc);    /* Root and return. */
 
     const JSAtomState &atomState = cx->runtime->atomState;
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
         if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.getAtom), roots[0],
                                   PropertyStub, PropertyStub, JSPROP_ENUMERATE) ||
             !desc->defineProperty(cx, ATOM_TO_JSID(atomState.setAtom), roots[1],
                                   PropertyStub, PropertyStub, JSPROP_ENUMERATE)) {
             return false;
@@ -1929,19 +1929,19 @@ HasProperty(JSContext* cx, JSObject* obj
         vp->setUndefined();
         return JS_TRUE;
     }
     return JS_GetPropertyById(cx, obj, id, Jsvalify(vp));
 }
 
 PropertyDescriptor::PropertyDescriptor()
   : id(INT_TO_JSID(0)),
-    value(UndefinedValue()),
-    get(UndefinedValue()),
-    set(UndefinedValue()),
+    value(UndefinedTag()),
+    get(UndefinedTag()),
+    set(UndefinedTag()),
     attrs(0),
     hasGet(false),
     hasSet(false),
     hasValue(false),
     hasWritable(false),
     hasEnumerable(false),
     hasConfigurable(false)
 {
@@ -2137,24 +2137,22 @@ DefinePropertyOnObject(JSContext *cx, JS
 
     JSScopeProperty *sprop = reinterpret_cast<JSScopeProperty *>(current);
     do {
         if (desc.isAccessorDescriptor()) {
             if (!sprop->isAccessorDescriptor())
                 break;
 
             if (desc.hasGet &&
-                !SameValue(cx, desc.getterValue(),
-                           sprop->hasGetterValue() ? sprop->getterValue() : UndefinedValue())) {
+                !SameValue(cx, desc.getterValue(), sprop->getterOrUndefined())) {
                 break;
             }
 
             if (desc.hasSet &&
-                !SameValue(cx, desc.setterValue(),
-                           sprop->hasSetterValue() ? sprop->setterValue() : UndefinedValue())) {
+                !SameValue(cx, desc.setterValue(), sprop->setterOrUndefined())) {
                 break;
             }
         } else {
             /*
              * Determine the current value of the property once, if the current
              * value might actually need to be used or preserved later.  NB: we
              * guard on whether the current property is a data descriptor to
              * avoid calling a getter; we won't need the value if it's not a
@@ -2256,21 +2254,19 @@ DefinePropertyOnObject(JSContext *cx, JS
                               throwError, desc.id, rval);
             }
         }
     } else {
         /* 8.12.9 step 11. */
         JS_ASSERT(desc.isAccessorDescriptor() && sprop->isAccessorDescriptor());
         if (!sprop->configurable()) {
             if ((desc.hasSet &&
-                 !SameValue(cx, desc.setterValue(),
-                            sprop->hasSetterValue() ? sprop->setterValue() : UndefinedValue())) ||
+                 !SameValue(cx, desc.setterValue(), sprop->setterOrUndefined())) ||
                 (desc.hasGet &&
-                 !SameValue(cx, desc.getterValue(),
-                            sprop->hasGetterValue() ? sprop->getterValue() : UndefinedValue()))) {
+                 !SameValue(cx, desc.getterValue(), sprop->getterOrUndefined()))) {
                 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
                               throwError, desc.id, rval);
             }
         }
     }
 
     /* 8.12.9 step 12. */
     uintN attrs;
@@ -3023,17 +3019,17 @@ js_CloneBlockObject(JSContext *cx, JSObj
 
     JSObject *clone = js_NewGCObject(cx);
     if (!clone)
         return NULL;
 
     /* The caller sets parent on its own. */
     Value priv;
     priv.setPrivateVoidPtr(js_FloatingFrameIfGenerator(cx, fp));
-    clone->init(&js_BlockClass, NonFunObjPtr(*proto), NULL, priv);
+    clone->init(&js_BlockClass, NonFunObjTag(*proto), NULL, priv);
     clone->fslots[JSSLOT_BLOCK_DEPTH].copy(proto->fslots[JSSLOT_BLOCK_DEPTH]);
 
     JS_ASSERT(cx->runtime->emptyBlockScope->freeslot == JSSLOT_BLOCK_DEPTH + 1);
     clone->map = cx->runtime->emptyBlockScope;
     cx->runtime->emptyBlockScope->hold();
     JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone));
     return clone;
 }
@@ -3371,17 +3367,17 @@ js_InitClass(JSContext *cx, JSObject *ob
          * 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) {
             named = JS_FALSE;
         } else {
-            named = obj->defineProperty(cx, ATOM_TO_JSID(atom), ToValue(proto),
+            named = obj->defineProperty(cx, ATOM_TO_JSID(atom), ObjectOrNullTag(proto),
                                         PropertyStub, PropertyStub,
                                         (clasp->flags & JSCLASS_IS_ANONYMOUS)
                                         ? JSPROP_READONLY | JSPROP_PERMANENT
                                         : 0);
             if (!named)
                 goto bad;
         }
 
@@ -3405,31 +3401,31 @@ js_InitClass(JSContext *cx, JSObject *ob
          * Optionally construct the prototype object, before the class has
          * been fully initialized.  Allow the ctor to replace proto with a
          * different object, as is done for operator new -- and as at least
          * XML support requires.
          */
         ctor = FUN_OBJECT(fun);
         if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) {
             Value rval;
-            if (!InternalConstruct(cx, proto, ToValue(ctor), 0, NULL, &rval))
+            if (!InternalConstruct(cx, proto, ObjectOrNullTag(ctor), 0, NULL, &rval))
                 goto bad;
             if (rval.isObject() && &rval.asObject() != proto)
                 proto = &rval.asObject();
         }
 
         /* 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(ToObjPtr(proto));
+            ctor->setProto(ObjectOrNullTag(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;
@@ -3658,17 +3654,17 @@ js_GetClassObject(JSContext *cx, JSObjec
 
 JSBool
 js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj)
 {
     JS_ASSERT(!obj->getParent());
     if (!(obj->getClass()->flags & JSCLASS_IS_GLOBAL))
         return JS_TRUE;
 
-    return js_SetReservedSlot(cx, obj, key, ToValue(cobj));
+    return js_SetReservedSlot(cx, obj, key, ObjectOrNullTag(cobj));
 }
 
 JSBool
 js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
                    Value *vp, Class *clasp)
 {
     JSStackFrame *fp;
     JSObject *obj, *cobj, *pobj;
@@ -3704,17 +3700,17 @@ js_FindClassObject(JSContext *cx, JSObje
         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;
         if (cobj) {
-            SetObject(vp, *cobj);
+            vp->setObject(*cobj);
             return JS_TRUE;
         }
         id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[protoKey]);
     } else {
         JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
         if (!atom)
             return false;
         id = ATOM_TO_JSID(atom);
@@ -4583,17 +4579,17 @@ js_NativeGet(JSContext *cx, JSObject *ob
     if (slot != SPROP_INVALID_SLOT)
         vp->copy(pobj->lockedGetSlot(slot));
     else
         vp->setUndefined();
     if (sprop->hasDefaultGetter())
         return true;
 
     if (JS_UNLIKELY(sprop->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
-        JS_ASSERT(sprop->methodValue().isSame(*vp));
+        JS_ASSERT(&sprop->methodFunObj() == &vp->asFunObj());
         return true;
     }
 
     sample = cx->runtime->propertyRemovals;
     JS_UNLOCK_SCOPE(cx, scope);
     {
         AutoScopePropertyRooter tvr(cx, sprop);
         AutoObjectRooter tvr2(cx, pobj);
@@ -5199,17 +5195,17 @@ js_DeleteProperty(JSContext *cx, JSObjec
     obj->dropProperty(cx, prop);
     return ok;
 }
 
 JSBool
 js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
 {
     Value v;
-    SetObject(&v, obj);
+    v.setObject(*obj);
     if (hint == JSTYPE_STRING) {
     JS_ASSERT(hint != JSTYPE_OBJECT && hint != JSTYPE_FUNCTION);
 
         /*
          * Optimize for String objects with standard toString methods. Support
          * new String(...) instances whether mutated to have their own scope or
          * not, as well as direct String.prototype references.
          */
@@ -5276,17 +5272,17 @@ js_DefaultValue(JSContext *cx, JSObject 
         JSString *str;
         if (hint == JSTYPE_STRING) {
             str = JS_InternString(cx, obj->getClass()->name);
             if (!str)
                 return JS_FALSE;
         } else {
             str = NULL;
         }
-        SetObject(vp, *obj);
+        vp->setObject(*obj);
         js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO,
                              JSDVG_SEARCH_STACK, *vp, str,
                              (hint == JSTYPE_VOID)
                              ? "primitive type"
                              : JS_TYPE_STR(hint));
         return JS_FALSE;
     }
     vp->copy(v);
@@ -5551,17 +5547,17 @@ js_HasInstance(JSContext *cx, JSObject *
             if (!js_InternalCall(cx, obj, fval, 1, &v, &rval))
                 return JS_FALSE;
             *bp = js_ValueToBoolean(rval);
             return JS_TRUE;
         }
     }
 #endif
     js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
-                        JSDVG_SEARCH_STACK, ToValue(*obj), NULL);
+                        JSDVG_SEARCH_STACK, ObjectTag(*obj), NULL);
     return JS_FALSE;
 }
 
 bool
 js_IsDelegate(JSContext *cx, JSObject *obj, const Value &v)
 {
     if (v.isPrimitive())
         return false;
@@ -5677,26 +5673,26 @@ js_SetClassPrototype(JSContext *cx, JSOb
 {
     /*
      * Use the given attributes for the prototype property of the constructor,
      * as user-defined constructors have a DontDelete prototype (which may be
      * reset), while native or "system" constructors have DontEnum | ReadOnly |
      * DontDelete.
      */
     if (!ctor->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
-                              ToValue(proto), PropertyStub, PropertyStub, attrs)) {
+                              ObjectOrNullTag(proto), PropertyStub, PropertyStub, attrs)) {
         return JS_FALSE;
     }
 
     /*
      * ECMA says that Object.prototype.constructor, or f.prototype.constructor
      * for a user-defined function f, is DontEnum.
      */
     return proto->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
-                                 ToValue(ctor), CheckCtorGetAccess, CheckCtorSetAccess, 0);
+                                 ObjectOrNullTag(ctor), CheckCtorGetAccess, CheckCtorSetAccess, 0);
 }
 
 JSBool
 js_PrimitiveToObject(JSContext *cx, Value *vp)
 {
     Value v;
     v.copy(*vp);
     JS_ASSERT(v.isPrimitive());
@@ -6370,21 +6366,21 @@ DumpObject(JSObject *obj)
             dumpScopeProp(sprop);
         }
     } else {
         if (!obj->isNative())
             fprintf(stderr, "not native\n");
     }
 
     fprintf(stderr, "proto ");
-    dumpValue(ToValue(obj->getProto()));
+    dumpValue(obj->getProtoValue());
     fputc('\n', stderr);
 
     fprintf(stderr, "parent ");
-    dumpValue(ToValue(obj->getParent()));
+    dumpValue(obj->getParentValue());
     fputc('\n', stderr);
 
     i = JSSLOT_PRIVATE;
     if (clasp->flags & JSCLASS_HAS_PRIVATE) {
         i = JSSLOT_PRIVATE + 1;
         fprintf(stderr, "private %p\n", obj->getPrivate());
     }
 
@@ -6404,17 +6400,17 @@ DumpObject(JSObject *obj)
     fputc('\n', stderr);
 }
 
 static void
 MaybeDumpObject(const char *name, JSObject *obj)
 {
     if (obj) {
         fprintf(stderr, "  %s: ", name);
-        dumpValue(ToValue(*obj));
+        dumpValue(ObjectTag(*obj));
         fputc('\n', stderr);
     }
 }
 
 static void
 MaybeDumpValue(const char *name, const Value &v)
 {
     if (!v.isNull()) {
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1287,26 +1287,9 @@ JS_FRIEND_API(void) DumpStackFrameChain(
 
 extern uintN
 js_InferFlags(JSContext *cx, uintN defaultFlags);
 
 /* Object constructor native. Exposed only so the JIT can know its address. */
 JSBool
 js_Object(JSContext *cx, JSObject *obj, uintN argc, js::Value *argv, js::Value *rval);
 
-namespace js {
-
-/*
- * Convenience functions for manipulating objects and values. To be inlined,
- * these functions need to be in jsobj.h, so they cannot be member functions.
- */
-
-JS_ALWAYS_INLINE ObjPtr ToObjPtr(JSObject *pobj);
-JS_ALWAYS_INLINE ObjPtr ToObjPtr(JSObject &obj);
-JS_ALWAYS_INLINE Value ToValue(JSObject *pobj);
-JS_ALWAYS_INLINE Value ToValue(JSObject &obj);
-JS_ALWAYS_INLINE void SetObject(ObjPtr *vp, JSObject *pobj);
-JS_ALWAYS_INLINE void SetObject(Value *vp, JSObject *pobj);
-JS_ALWAYS_INLINE void SetObject(ObjPtr *vp, JSObject &obj);
-JS_ALWAYS_INLINE void SetObject(Value *vp, JSObject &obj);
-
-} /* namespace js */
 #endif /* jsobj_h___ */
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -465,20 +465,20 @@ JSObject::thisObject(JSContext *cx)
         return thisOp(cx, this);
     return this;
 }
 
 inline js::ObjPtr
 JSObject::thisObject(JSContext *cx, js::ObjPtr obj)
 {
     if (JSObjectOp thisOp = obj->map->ops->thisObject) {
-        JSObject *o = thisOp(cx, obj);
-        if (!o)
-            return js::NullObjPtr();
-        SetObject(&obj, *o);
+        JSObject *pobj = thisOp(cx, obj);
+        if (!pobj)
+            return js::NullTag();
+        obj.setObject(*pobj);
     }
     return obj;
 }
 
 namespace js {
 
 typedef Vector<PropertyDescriptor, 1> PropertyDescriptorArray;
 
@@ -585,17 +585,17 @@ 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,
-              ToObjPtr(proto),
+              js::ObjectOrNullTag(proto),
               (!parent && proto) ? proto->getParent() : parent,
               JSObject::defaultPrivate(clasp));
 
     if (ops->isNative()) {
         if (!InitScopeForObject(cx, obj, clasp, proto, ops)) {
             obj = NULL;
             goto out;
         }
@@ -645,87 +645,67 @@ NewObject(JSContext *cx, js::Class *clas
             !js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) {
             return NULL;
         }
     }
 
     return NewObjectWithGivenProto(cx, clasp, proto, parent, objectSize);
 }
 
-JS_ALWAYS_INLINE ObjPtr
-ToObjPtr(JSObject *pobj)
+JS_ALWAYS_INLINE
+ObjPtr::ObjPtr(ObjectTag arg)
 {
-    if (pobj)
-        return NullObjPtr();
-    if (pobj->isFunction())
-        return FunObjPtr(*pobj);
-    return NonFunObjPtr(*pobj);
+    mask = arg.obj.isFunction() ? FunObjMask : NonFunObjMask;
+    obj = &arg.obj;
 }
 
-JS_ALWAYS_INLINE ObjPtr
-ToObjPtr(JSObject &obj)
+JS_ALWAYS_INLINE
+ObjPtr::ObjPtr(ObjectOrNullTag arg)
 {
-    if (obj.isFunction())
-        return FunObjPtr(obj);
-    return NonFunObjPtr(obj);
+    mask = arg.obj ? arg.obj->isFunction() ? FunObjMask : NonFunObjMask : NullMask;
+    obj = arg.obj;
 }
 
-JS_ALWAYS_INLINE Value
-ToValue(JSObject *pobj)
+JS_ALWAYS_INLINE void
+ObjPtr::setObject(JSObject &arg)
 {
-    if (pobj)
-        return NullValue();
-    if (pobj->isFunction())
-        return FunObjValue(*pobj);
-    return NonFunObjValue(*pobj);
-}
-
-JS_ALWAYS_INLINE Value
-ToValue(JSObject &obj)
-{
-    if (obj.isFunction())
-        return FunObjValue(obj);
-    return NonFunObjValue(obj);
+    mask = arg.isFunction() ? FunObjMask : NonFunObjMask;
+    obj = &arg;
 }
 
 JS_ALWAYS_INLINE void
-SetObject(ObjPtr *vp, JSObject *pobj)
+ObjPtr::setObjectOrNull(JSObject *arg)
+{
+    mask = arg ? arg->isFunction() ? FunObjMask : NonFunObjMask : NullMask;
+    obj = arg;
+}
+
+JS_ALWAYS_INLINE
+Value::Value(ObjectTag arg)
 {
-    if (!pobj)
-        vp->setNull();
-    else if (pobj->isFunction())
-        vp->setFunObj(*pobj);
-    else
-        vp->setNonFunObj(*pobj);
+    mask = arg.obj.isFunction() ? FunObjMask : NonFunObjMask;
+    data.obj = &arg.obj;
+}
+
+JS_ALWAYS_INLINE
+Value::Value(ObjectOrNullTag arg)
+{
+    mask = arg.obj ? arg.obj->isFunction() ? FunObjMask : NonFunObjMask : NullMask;
+    data.obj = arg.obj;
 }
 
 JS_ALWAYS_INLINE void
-SetObject(Value *vp, JSObject *pobj)
+Value::setObject(JSObject &arg)
 {
-    if (!pobj)
-        vp->setNull();
-    if (pobj->isFunction())
-        vp->setFunObj(*pobj);
-    else
-        vp->setNonFunObj(*pobj);
+    mask = arg.isFunction() ? FunObjMask : NonFunObjMask;
+    data.obj = &arg;
 }
 
 JS_ALWAYS_INLINE void
-SetObject(ObjPtr *vp, JSObject &obj)
+Value::setObjectOrNull(JSObject *arg)
 {
-    if (obj.isFunction())
-        vp->setFunObj(obj);
-    else
-        vp->setNonFunObj(obj);
-}
-
-JS_ALWAYS_INLINE void
-SetObject(Value *vp, JSObject &obj)
-{
-    if (obj.isFunction())
-        vp->setFunObj(obj);
-    else
-        vp->setNonFunObj(obj);
+    mask = arg ? arg->isFunction() ? FunObjMask : NonFunObjMask : NullMask;
+    data.obj = arg;
 }
 
 } /* namespace js */
 
 #endif /* jsobjinlines_h___ */
--- a/js/src/jsops.cpp
+++ b/js/src/jsops.cpp
@@ -3146,17 +3146,18 @@ 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->scopeChain = obj;
 
-    FunObjValue rval(*obj);
+    Value rval;
+    rval.setFunObj(*obj);
 
     /*
      * ECMA requires functions defined when entering Eval code to be
      * impermanent.
      */
     uintN attrs = (fp->flags & JSFRAME_EVAL)
                   ? JSPROP_ENUMERATE
                   : JSPROP_ENUMERATE | JSPROP_PERMANENT;
@@ -3245,17 +3246,19 @@ BEGIN_CASE(JSOP_DEFFUN_DBGFC)
     JSFunction *fun;
     LOAD_FUNCTION(0);
 
     JSObject *obj = (op == JSOP_DEFFUN_FC)
                     ? js_NewFlatClosure(cx, fun)
                     : js_NewDebuggableFlatClosure(cx, fun);
     if (!obj)
         goto error;
-    FunObjValue rval(*obj);
+
+    Value rval;
+    rval.setFunObj(*obj);
 
     uintN attrs = (fp->flags & JSFRAME_EVAL)
                   ? JSPROP_ENUMERATE
                   : JSPROP_ENUMERATE | JSPROP_PERMANENT;
 
     uintN flags = JSFUN_GSFLAG2ATTR(fun->flags);
     if (flags) {
         attrs |= flags | JSPROP_SHARED;
@@ -4039,30 +4042,30 @@ BEGIN_CASE(JSOP_QNAMECONST)
     JSAtom *atom;
     LOAD_ATOM(0, atom);
     Value rval(ATOM_TO_STRING(ATOM_KEY(atom)));
     Value lval;
     lval.copy(regs.sp[-1]);
     JSObject *obj = js_ConstructXMLQNameObject(cx, lval, rval);
     if (!obj)
         goto error;
-    SetObject(&regs.sp[-1], *obj);
+    regs.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_QNAMECONST)
 
 BEGIN_CASE(JSOP_QNAME)
 {
     Value rval, lval;
     rval.copy(regs.sp[-1]);
     lval.copy(regs.sp[-2]);
     JSObject *obj = js_ConstructXMLQNameObject(cx, lval, rval);
     if (!obj)
         goto error;
     regs.sp--;
-    SetObject(&regs.sp[-1], *obj);
+    regs.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_QNAME)
 
 BEGIN_CASE(JSOP_TOATTRNAME)
 {
     Value rval;
     rval.copy(regs.sp[-1]);
     if (!js_ToAttributeName(cx, &rval))
@@ -4102,17 +4105,17 @@ END_CASE(JSOP_ADDATTRNAME)
 BEGIN_CASE(JSOP_BINDXMLNAME)
 {
     Value lval;
     lval.copy(regs.sp[-1]);
     JSObject *obj;
     jsid id;
     if (!js_FindXMLProperty(cx, lval, &obj, &id))
         goto error;
-    SetObject(&regs.sp[-1], obj);
+    regs.sp[-1].setObjectOrNull(obj);
     PUSH_COPY(IdToValue(id));
 }
 END_CASE(JSOP_BINDXMLNAME)
 
 BEGIN_CASE(JSOP_SETXMLNAME)
 {
     JSObject *obj = &regs.sp[-3].asObject();
     Value rval;
@@ -4210,28 +4213,28 @@ END_CASE(JSOP_ENDFILTER);
 
 BEGIN_CASE(JSOP_TOXML)
 {
     Value rval;
     rval.copy(regs.sp[-1]);
     JSObject *obj = js_ValueToXMLObject(cx, rval);
     if (!obj)
         goto error;
-    SetObject(&regs.sp[-1], *obj);
+    regs.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_TOXML)
 
 BEGIN_CASE(JSOP_TOXMLLIST)
 {
     Value rval;
     rval.copy(regs.sp[-1]);
     JSObject *obj = js_ValueToXMLListObject(cx, rval);
     if (!obj)
         goto error;
-    SetObject(&regs.sp[-1], *obj);
+    regs.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_TOXMLLIST)
 
 BEGIN_CASE(JSOP_XMLTAGEXPR)
 {
     Value rval;
     rval.copy(regs.sp[-1]);
     JSString *str = js_ValueToString(cx, rval);
@@ -4300,17 +4303,17 @@ BEGIN_CASE(JSOP_XMLPI)
     LOAD_ATOM(0, atom);
     JSString *str = ATOM_TO_STRING(atom);
     Value rval;
     rval.copy(regs.sp[-1]);
     JSString *str2 = rval.asString();
     JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_PROCESSING_INSTRUCTION, str, str2);
     if (!obj)
         goto error;
-    SetObject(&regs.sp[-1], *obj);
+    regs.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_XMLPI)
 
 BEGIN_CASE(JSOP_GETFUNNS)
 {
     Value rval;
     if (!js_GetFunctionNamespace(cx, &rval))
         goto error;
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -684,43 +684,46 @@ struct JSScopeProperty {
         PUBLIC_FLAGS    = ALIAS | HAS_SHORTID | METHOD
     };
 
     uintN getFlags() const  { return flags & PUBLIC_FLAGS; }
     bool isAlias() const    { return (flags & ALIAS) != 0; }
     bool hasShortID() const { return (flags & HAS_SHORTID) != 0; }
     bool isMethod() const   { return (flags & METHOD) != 0; }
 
-    JSObject *methodObject() const { JS_ASSERT(isMethod()); return getterObj; }
-    js::Value methodValue() const      { return js::FunObjValue(*methodObject()); }
+    JSObject &methodFunObj() const { JS_ASSERT(isMethod()); return *getterObj; }
 
     js::PropertyOp getter() const { return rawGetter; }
     bool hasDefaultGetter() const  { return !rawGetter; }
     js::PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return rawGetter; }
     JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return getterObj; }
 
     // Per ES5, decode null getterObj as the undefined value, which encodes as null.
-    js::Value getterValue() const {
+    js::FunObjOrUndefinedTag getterValue() const {
         JS_ASSERT(hasGetterValue());
-        if (getterObj)
-            return js::FunObjValue(*getterObj);
-        return js::UndefinedValue();
+        return js::FunObjOrUndefinedTag(getterObj);
+    }
+
+    js::FunObjOrUndefinedTag getterOrUndefined() const {
+        return js::FunObjOrUndefinedTag(hasGetterValue() ? getterObj : NULL);
     }
 
     js::PropertyOp setter() const { return rawSetter; }
     bool hasDefaultSetter() const  { return !rawSetter; }
     js::PropertyOp setterOp() const { JS_ASSERT(!hasSetterValue()); return rawSetter; }
     JSObject *setterObject() const { JS_ASSERT(hasSetterValue()); return setterObj; }
 
     // Per ES5, decode null setterObj as the undefined value, which encodes as null.
-    js::Value setterValue() const {
+    js::FunObjOrUndefinedTag setterValue() const {
         JS_ASSERT(hasSetterValue());
-        if (setterObj)
-            return js::FunObjValue(*setterObj);
-        return js::UndefinedValue();
+        return js::FunObjOrUndefinedTag(setterObj);
+    }
+
+    js::FunObjOrUndefinedTag setterOrUndefined() const {
+        return js::FunObjOrUndefinedTag(hasSetterValue() ? setterObj : NULL);
     }
 
     inline JSDHashNumber hash() const;
     inline bool matches(const JSScopeProperty *p) const;
     inline bool matchesParamsAfterId(js::PropertyOp agetter, js::PropertyOp asetter,
                                      uint32 aslot, uintN aattrs, uintN aflags,
                                      intN ashortid) const;
 
@@ -959,17 +962,17 @@ JSScopeProperty::get(JSContext* cx, JSOb
     JS_ASSERT(!hasDefaultGetter());
 
     if (hasGetterValue()) {
         JS_ASSERT(!isMethod());
         return js::InternalGetOrSet(cx, obj, id, getterValue(), JSACC_READ, 0, 0, vp);
     }
 
     if (isMethod()) {
-        vp->copy(methodValue());
+        vp->setFunObj(methodFunObj());
 
         JSScope *scope = pobj->scope();
         JS_ASSERT(scope->object == pobj);
         return scope->methodReadBarrier(cx, this, vp);
     }
 
     /*
      * JSObjectOps is private, so we know there are only two implementations
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -114,17 +114,17 @@ JSScope::extend(JSContext *cx, JSScopePr
  * objects optimized as typically non-escaping, ad-hoc methods in obj.
  */
 inline bool
 JSScope::methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, js::Value *vp)
 {
     JS_ASSERT(hasMethodBarrier());
     JS_ASSERT(hasProperty(sprop));
     JS_ASSERT(sprop->isMethod());
-    JS_ASSERT(sprop->methodValue().isSame(*vp));
+    JS_ASSERT(&vp->asFunObj() == &sprop->methodFunObj());
     JS_ASSERT(object->getClass() == &js_ObjectClass);
 
     JSObject *funobj = &vp->asFunObj();
     JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
     JS_ASSERT(FUN_OBJECT(fun) == funobj && FUN_NULL_CLOSURE(fun));
 
     funobj = CloneFunctionObject(cx, fun, funobj->getParent());
     if (!funobj)