(unrebased) fat value patch
authorLuke Wagner <lw@mozilla.com>
Mon, 10 May 2010 22:01:31 -0700
changeset 52522 a88154256ee0b5182ba4435c1b9b0511fcc7bbe0
parent 52521 d917ddae373cc03cd7c3503300c7dd6b866430b8
child 52523 57e7fc4ae81243865fee66ca364f3244686bbb04
push idunknown
push userunknown
push dateunknown
milestone1.9.3a4pre
(unrebased) fat value patch
js/jsd/jsd_scpt.c
js/jsd/jsd_val.c
js/jsd/jsd_xpc.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsatom.cpp
js/src/jsatom.h
js/src/jsatominlines.h
js/src/jsbool.cpp
js/src/jsbool.h
js/src/jsbuiltins.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jsdate.cpp
js/src/jsdate.h
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/jsdtoa.cpp
js/src/jsdtracef.h
js/src/jsemit.cpp
js/src/jsemit.h
js/src/jsexn.cpp
js/src/jsexn.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jshashtable.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jsiter.h
js/src/jsmath.h
js/src/jsnum.cpp
js/src/jsnum.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.h
js/src/jsopcode.h
js/src/jsops.cpp
js/src/jsparse.cpp
js/src/jsparse.h
js/src/jspropertycache.h
js/src/jsprvtd.h
js/src/jspubtd.h
js/src/jsregexp.h
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jsstr.h
js/src/jstl.h
js/src/jstracer.cpp
js/src/jstypedarray.h
js/src/jstypes.h
js/src/jsxdrapi.h
js/src/jsxml.h
--- a/js/jsd/jsd_scpt.c
+++ b/js/jsd/jsd_scpt.c
@@ -712,17 +712,17 @@ static JSBool
     return JS_FALSE;
 }
 
 
 JSTrapStatus
 jsd_TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
                 void *closure)
 {
-    JSDExecHook* jsdhook = (JSDExecHook*) JSVAL_TO_PRIVATE(((jsval)closure));
+    JSDExecHook* jsdhook = (JSDExecHook*)closure;
     JSD_ExecutionHookProc hook;
     void* hookData;
     JSDContext*  jsdc;
     JSDScript* jsdscript;
 
     JSD_LOCK();
 
     if( NULL == (jsdc = jsd_JSDContextForJSContext(cx)) ||
@@ -795,17 +795,17 @@ jsd_SetExecutionHook(JSDContext*        
     }
     jsdhook->jsdscript  = jsdscript;
     jsdhook->pc         = pc;
     jsdhook->hook       = hook;
     jsdhook->callerdata = callerdata;
 
     if( ! JS_SetTrap(jsdc->dumbContext, jsdscript->script, 
                      (jsbytecode*)pc, jsd_TrapHandler, 
-                     (void*) PRIVATE_TO_JSVAL(jsdhook)) )
+                     PRIVATE_TO_JSVAL(jsdhook)) )
     {
         free(jsdhook);
         JSD_UNLOCK();
         return JS_FALSE;
     }
 
     JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks);
     JSD_UNLOCK();
--- a/js/jsd/jsd_val.c
+++ b/js/jsd/jsd_val.c
@@ -201,20 +201,19 @@ jsd_GetValueInt(JSDContext* jsdc, JSDVal
     if(!JSVAL_IS_INT(val))
         return 0;
     return JSVAL_TO_INT(val);
 }
 
 jsdouble*
 jsd_GetValueDouble(JSDContext* jsdc, JSDValue* jsdval)
 {
-    jsval val = jsdval->val;
-    if(!JSVAL_IS_DOUBLE(val))
+    if(!JSVAL_IS_DOUBLE(jsdval->val))
         return 0;
-    return JSVAL_TO_DOUBLE(val);
+    return JSVAL_PTR_TO_DOUBLE_PTR(&jsdval->val);
 }
 
 JSString*
 jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSContext* cx = jsdc->dumbContext;
     JSExceptionState* exceptionState;
 
@@ -337,24 +336,24 @@ static JSDProperty* _newProperty(JSDCont
     if(!(jsdprop = (JSDProperty*) calloc(1, sizeof(JSDProperty))))
         return NULL;
 
     JS_INIT_CLIST(&jsdprop->links);
     jsdprop->nref = 1;
     jsdprop->flags = pd->flags | additionalFlags;
     jsdprop->slot = pd->slot;
 
-    if(!(jsdprop->name = jsd_NewValue(jsdc, pd->id)))
+    if(!(jsdprop->name = jsd_NewValue(jsdc, JSID_TO_JSVAL(pd->id))))
         goto new_prop_fail;
 
     if(!(jsdprop->val = jsd_NewValue(jsdc, pd->value)))
         goto new_prop_fail;
 
     if((jsdprop->flags & JSDPD_ALIAS) &&
-       !(jsdprop->alias = jsd_NewValue(jsdc, pd->alias)))
+       !(jsdprop->alias = jsd_NewValue(jsdc, JSID_TO_JSVAL(pd->alias))))
         goto new_prop_fail;
 
     return jsdprop;
 new_prop_fail:
     jsd_DropProperty(jsdc, jsdprop);
     return NULL;
 }
 
@@ -487,17 +486,17 @@ jsd_GetValueProperty(JSDContext* jsdc, J
     JSDProperty* jsdprop;
     JSDProperty* iter = NULL;
     JSObject* obj;
     uintN  attrs = 0;
     JSBool found;
     JSPropertyDesc pd;
     const jschar * nameChars;
     size_t nameLen;
-    jsval val;
+    jsval val, nameval;
 
     if(!jsd_IsValueObject(jsdc, jsdval))
         return NULL;
 
     /* If we already have the prop, then return it */
     while(NULL != (jsdprop = jsd_IterateProperties(jsdc, jsdval, &iter)))
     {
         JSString* propName = jsd_GetValueString(jsdc, jsdprop->name);
@@ -543,18 +542,21 @@ jsd_GetValueProperty(JSDContext* jsdc, J
     }
     else
     {
         pd.value = val;
     }
 
     JS_EndRequest(cx);
 
-    pd.id = STRING_TO_JSVAL(name);
-    pd.alias = pd.slot = pd.spare = 0;
+    nameval = STRING_TO_JSVAL(name);
+    if (!JS_ValueToId(cx, &nameval, &pd.id))
+        return NULL;
+    pd.slot = pd.spare = 0;
+    pd.alias = JSID_NULL;
     pd.flags |= (attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0
         | (attrs & JSPROP_READONLY)  ? JSPD_READONLY  : 0
         | (attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0;
 
     return _newProperty(jsdc, &pd, JSDPD_HINTED);
 }
 
 
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -33,18 +33,16 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "jsdbgapi.h"
-#include "jscntxt.h"
-#include "jsfun.h"
 #include "jsd_xpc.h"
 
 #include "nsIXPConnect.h"
 #include "nsIGenericFactory.h"
 #include "nsIServiceManager.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
@@ -1009,26 +1007,27 @@ jsdScript::CreatePPLineMap()
     JSAutoRequest ar(cx);
     JSObject   *obj = JS_NewObject(cx, NULL, NULL, NULL);
     JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
     JSScript   *script;
     PRUint32    baseLine;
     PRBool      scriptOwner = PR_FALSE;
     
     if (fun) {
-        if (fun->nargs > 12)
+        uintN nargs = JS_GetFunctionArgumentCount(cx, fun);
+        if (nargs > 12)
             return nsnull;
         JSString *jsstr = JS_DecompileFunctionBody (cx, fun, 4);
         if (!jsstr)
             return nsnull;
     
         const char *argnames[] = {"arg1", "arg2", "arg3", "arg4", 
                                   "arg5", "arg6", "arg7", "arg8",
                                   "arg9", "arg10", "arg11", "arg12" };
-        fun = JS_CompileUCFunction (cx, obj, "ppfun", fun->nargs, argnames,
+        fun = JS_CompileUCFunction (cx, obj, "ppfun", nargs, argnames,
                                     JS_GetStringChars(jsstr),
                                     JS_GetStringLength(jsstr),
                                     "x-jsd:ppbuffer?type=function", 3);
         if (!fun || !(script = JS_GetFunctionScript(cx, fun)))
             return nsnull;
         baseLine = 3;
     } else {
         JSString *jsstr = JS_DecompileScript (cx, JSD_GetJSScript(mCx, mScript),
@@ -1227,60 +1226,55 @@ jsdScript::GetParameterNames(PRUint32* c
     if (!cx) {
         NS_WARNING("No default context !?");
         return NS_ERROR_FAILURE;
     }
     JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
 
     JSAutoRequest ar(cx);
 
-    if (!fun || !fun->hasLocalNames() || fun->nargs == 0) {
+    uintN nargs = JS_GetFunctionArgumentCount(cx, fun);
+    if (!fun || !JS_FunctionHasLocalNames(cx, fun) || nargs == 0) {
         *count = 0;
         *paramNames = nsnull;
         return NS_OK;
     }
 
     PRUnichar **ret =
-        static_cast<PRUnichar**>(NS_Alloc(fun->nargs * sizeof(PRUnichar*)));
+        static_cast<PRUnichar**>(NS_Alloc(nargs * sizeof(PRUnichar*)));
     if (!ret)
         return NS_ERROR_OUT_OF_MEMORY;
 
-    void *mark = JS_ARENA_MARK(&cx->tempPool);
-    jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
+    void *mark;
+    jsuword *names = JS_GetFunctionLocalNameArray(cx, fun, &mark);
     if (!names) {
         NS_Free(ret);
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     nsresult rv = NS_OK;
-    for (uintN i = 0; i < fun->nargs; ++i) {
-        JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(names[i]);
+    for (uintN i = 0; i < nargs; ++i) {
+        JSAtom *atom = JS_LocalNameToAtom(names[i]);
         if (!atom) {
             ret[i] = 0;
         } else {
-            jsval atomVal = ATOM_KEY(atom);
-            if (!JSVAL_IS_STRING(atomVal)) {
-                NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret);
-                rv = NS_ERROR_UNEXPECTED;
-                break;
-            }
-            JSString *str = JSVAL_TO_STRING(atomVal);
+            JSString *str = JS_AtomKey(atom);
             ret[i] = NS_strndup(reinterpret_cast<PRUnichar*>(JS_GetStringChars(str)),
                                 JS_GetStringLength(str));
             if (!ret[i]) {
                 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret);
                 rv = NS_ERROR_OUT_OF_MEMORY;
                 break;
             }
         }
     }
-    JS_ARENA_RELEASE(&cx->tempPool, mark);
+    JS_ReleaseFunctionLocalNameArray(cx, mark);
     if (NS_FAILED(rv))
         return rv;
-    *count = fun->nargs;
+    *count = nargs;
     *paramNames = ret;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdScript::GetFunctionObject(jsdIValue **_rval)
 {
     JSFunction *fun = JSD_GetJSFunction(mCx, mScript);
@@ -1479,18 +1473,17 @@ jsdScript::IsLineExecutable(PRUint32 aLi
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdScript::SetBreakpoint(PRUint32 aPC)
 {
     ASSERT_VALID_EPHEMERAL;
     jsuword pc = mFirstPC + aPC;
-    JSD_SetExecutionHook (mCx, mScript, pc, jsds_ExecutionHookProc,
-                          reinterpret_cast<void *>(PRIVATE_TO_JSVAL(NULL)));
+    JSD_SetExecutionHook (mCx, mScript, pc, jsds_ExecutionHookProc, NULL);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdScript::ClearBreakpoint(PRUint32 aPC)
 {
     ASSERT_VALID_EPHEMERAL;    
     jsuword pc = mFirstPC + aPC;
@@ -1984,17 +1977,17 @@ jsdStackFrame::Eval (const nsAString &by
                                               mStackFrameInfo,
                                               char_bytes, bytes.Length(),
                                               PromiseFlatCString(fileName).get(),
                                               line, &jv);
     if (!*_rval) {
         if (JS_IsExceptionPending(cx))
             JS_GetPendingException (cx, &jv);
         else
-            jv = 0;
+            jv = JSVAL_NULL;
     }
 
     JS_RestoreExceptionState (cx, estate);
 
 #ifdef DEBUG
     JSContext* poppedCX;
     rv = stack->Pop(&poppedCX);
     NS_ASSERTION(NS_SUCCEEDED(rv) && poppedCX == cx, "bad pop");
@@ -2431,18 +2424,18 @@ jsdService::SetInitAtStartup (PRBool sta
 
     if (mInitAtStartup == triUnknown) {
         /* side effect sets mInitAtStartup */
         rv = GetInitAtStartup(nsnull);
         if (NS_FAILED(rv))
             return rv;
     }
 
-    if (state && mInitAtStartup == triYes ||
-        !state && mInitAtStartup == triNo) {
+    if ((state && mInitAtStartup == triYes) ||
+        (!state && mInitAtStartup == triNo)) {
         /* already in the requested state */
         return NS_OK;
     }
     
     nsCOMPtr<nsICategoryManager>
         categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
     if (NS_FAILED(rv))
         return rv;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -33,16 +33,18 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#define __STDC_LIMIT_MACROS
+
 /*
  * JavaScript API.
  */
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
@@ -92,59 +94,54 @@
 #include "jscntxtinlines.h"
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 using namespace js;
 
+/* Assumed by IS_SAME_JSVAL */
+JS_STATIC_ASSERT(sizeof(JSBool) == sizeof(int32));
+
 #ifdef HAVE_VA_LIST_AS_ARRAY
 #define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap))
 #else
 #define JS_ADDRESSOF_VA_LIST(ap) (&(ap))
 #endif
 
 #if defined(JS_THREADSAFE)
 #define CHECK_REQUEST(cx)                                                   \
     JS_ASSERT((cx)->requestDepth || (cx)->thread == (cx)->runtime->gcThread)
 #else
 #define CHECK_REQUEST(cx)       ((void)0)
 #endif
 
-/* Check that we can cast JSObject* as jsval without tag bit manipulations. */
-JS_STATIC_ASSERT(JSVAL_OBJECT == 0);
-
-/* Check that JSVAL_TRACE_KIND works. */
-JS_STATIC_ASSERT(JSVAL_TRACE_KIND(JSVAL_OBJECT) == JSTRACE_OBJECT);
-JS_STATIC_ASSERT(JSVAL_TRACE_KIND(JSVAL_DOUBLE) == JSTRACE_DOUBLE);
-JS_STATIC_ASSERT(JSVAL_TRACE_KIND(JSVAL_STRING) == JSTRACE_STRING);
-
 JS_PUBLIC_API(int64)
 JS_Now()
 {
     return PRMJ_Now();
 }
 
 JS_PUBLIC_API(jsval)
 JS_GetNaNValue(JSContext *cx)
 {
-    return cx->runtime->NaNValue;
+    return Jsvalify(cx->runtime->NaNValue);
 }
 
 JS_PUBLIC_API(jsval)
 JS_GetNegativeInfinityValue(JSContext *cx)
 {
-    return cx->runtime->negativeInfinityValue;
+    return Jsvalify(cx->runtime->negativeInfinityValue);
 }
 
 JS_PUBLIC_API(jsval)
 JS_GetPositiveInfinityValue(JSContext *cx)
 {
-    return cx->runtime->positiveInfinityValue;
+    return Jsvalify(cx->runtime->positiveInfinityValue);
 }
 
 JS_PUBLIC_API(jsval)
 JS_GetEmptyStringValue(JSContext *cx)
 {
     return STRING_TO_JSVAL(cx->runtime->emptyString);
 }
 
@@ -198,32 +195,32 @@ JS_ConvertArgumentsVA(JSContext *cx, uin
         if (isspace(c))
             continue;
         if (c == '/') {
             required = JS_FALSE;
             continue;
         }
         if (sp == argv + argc) {
             if (required) {
-                fun = js_ValueToFunction(cx, &argv[-2], 0);
+                fun = js_ValueToFunction(cx, Valueify(&argv[-2]), 0);
                 if (fun) {
                     char numBuf[12];
                     JS_snprintf(numBuf, sizeof numBuf, "%u", argc);
                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                          JSMSG_MORE_ARGS_NEEDED,
                                          JS_GetFunctionName(fun), numBuf,
                                          (argc == 1) ? "" : "s");
                 }
                 return JS_FALSE;
             }
             break;
         }
         switch (c) {
           case 'b':
-            *va_arg(ap, JSBool *) = js_ValueToBoolean(*sp);
+            *va_arg(ap, JSBool *) = js_ValueToBoolean(Valueify(*sp));
             break;
           case 'c':
             if (!JS_ValueToUint16(cx, *sp, va_arg(ap, uint16 *)))
                 return JS_FALSE;
             break;
           case 'i':
             if (!JS_ValueToECMAInt32(cx, *sp, va_arg(ap, int32 *)))
                 return JS_FALSE;
@@ -243,17 +240,17 @@ JS_ConvertArgumentsVA(JSContext *cx, uin
           case 'I':
             if (!JS_ValueToNumber(cx, *sp, &d))
                 return JS_FALSE;
             *va_arg(ap, jsdouble *) = js_DoubleToInteger(d);
             break;
           case 's':
           case 'S':
           case 'W':
-            str = js_ValueToString(cx, *sp);
+            str = js_ValueToString(cx, Valueify(*sp));
             if (!str)
                 return JS_FALSE;
             *sp = STRING_TO_JSVAL(str);
             if (c == 's') {
                 const char *bytes = js_GetStringBytes(cx, str);
                 if (!bytes)
                     return JS_FALSE;
                 *va_arg(ap, const char **) = bytes;
@@ -262,23 +259,25 @@ 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':
-            if (!js_ValueToObject(cx, *sp, &obj))
+          {
+            Value objv;
+            if (!js_ValueToObjectOrNull(cx, Valueify(*sp), Valueify(sp)))
                 return JS_FALSE;
-            *sp = OBJECT_TO_JSVAL(obj);
-            *va_arg(ap, JSObject **) = obj;
+            *va_arg(ap, JSObject **) = JSVAL_TO_OBJECT(*sp);
             break;
+          }
           case 'f':
-            obj = js_ValueToFunctionObject(cx, sp, 0);
+            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':
             *va_arg(ap, jsval *) = *sp;
             break;
@@ -346,51 +345,45 @@ JS_RemoveArgumentFormatter(JSContext *cx
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp)
 {
     JSBool ok;
     JSObject *obj;
     JSString *str;
-    jsdouble d, *dp;
+    jsdouble d;
 
     CHECK_REQUEST(cx);
     switch (type) {
       case JSTYPE_VOID:
         *vp = JSVAL_VOID;
         ok = JS_TRUE;
         break;
       case JSTYPE_OBJECT:
-        ok = js_ValueToObject(cx, v, &obj);
-        if (ok)
-            *vp = OBJECT_TO_JSVAL(obj);
+        ok = js_ValueToObjectOrNull(cx, Valueify(v), Valueify(vp));
         break;
       case JSTYPE_FUNCTION:
         *vp = v;
-        obj = js_ValueToFunctionObject(cx, vp, JSV2F_SEARCH_STACK);
+        obj = js_ValueToFunctionObject(cx, Valueify(vp), JSV2F_SEARCH_STACK);
         ok = (obj != NULL);
         break;
       case JSTYPE_STRING:
-        str = js_ValueToString(cx, v);
+        str = js_ValueToString(cx, Valueify(v));
         ok = (str != NULL);
         if (ok)
             *vp = STRING_TO_JSVAL(str);
         break;
       case JSTYPE_NUMBER:
         ok = JS_ValueToNumber(cx, v, &d);
-        if (ok) {
-            dp = js_NewWeaklyRootedDouble(cx, d);
-            ok = (dp != NULL);
-            if (ok)
-                *vp = DOUBLE_TO_JSVAL(dp);
-        }
+        if (ok)
+            *vp = DOUBLE_TO_JSVAL(d);
         break;
       case JSTYPE_BOOLEAN:
-        *vp = BOOLEAN_TO_JSVAL(js_ValueToBoolean(v));
+        *vp = BOOLEAN_TO_JSVAL(js_ValueToBoolean(Valueify(v)));
         return JS_TRUE;
       default: {
         char numBuf[12];
         JS_snprintf(numBuf, sizeof numBuf, "%d", (int)type);
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_TYPE,
                              numBuf);
         ok = JS_FALSE;
         break;
@@ -398,148 +391,133 @@ JS_ConvertValue(JSContext *cx, jsval v, 
     }
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
 {
     CHECK_REQUEST(cx);
-    return js_ValueToObject(cx, v, objp);
+    Value objv;
+    return js_ValueToObjectOrNull(cx, Valueify(v), &objv);
+    *objp = objv.asObjectOrNull();
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS_ValueToFunction(JSContext *cx, jsval v)
 {
     CHECK_REQUEST(cx);
-    return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK);
+    return js_ValueToFunction(cx, Valueify(&v), JSV2F_SEARCH_STACK);
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS_ValueToConstructor(JSContext *cx, jsval v)
 {
     CHECK_REQUEST(cx);
-    return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK);
+    return js_ValueToFunction(cx, Valueify(&v), JSV2F_SEARCH_STACK);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_ValueToString(JSContext *cx, jsval v)
 {
     CHECK_REQUEST(cx);
-    return js_ValueToString(cx, v);
+    return js_ValueToString(cx, Valueify(v));
 }
 
 JS_PUBLIC_API(JSString *)
 JS_ValueToSource(JSContext *cx, jsval v)
 {
     CHECK_REQUEST(cx);
-    return js_ValueToSource(cx, v);
+    return js_ValueToSource(cx, Valueify(v));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
 {
     CHECK_REQUEST(cx);
 
-    AutoValueRooter tvr(cx, v);
-    return ValueToNumber(cx, v, dp);
+    AutoValueRooter tvr(cx, Valueify(v));
+    return ValueToNumber(cx, tvr.addr(), dp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DoubleIsInt32(jsdouble d, jsint *ip)
 {
-    return JSDOUBLE_IS_INT(d, *ip);
+    return JSDOUBLE_IS_INT32(d, *ip);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip)
 {
     CHECK_REQUEST(cx);
 
-    AutoValueRooter tvr(cx, v);
-    return ValueToECMAInt32(cx, v, (int32_t *)ip);
+    AutoValueRooter tvr(cx, Valueify(v));
+    return ValueToECMAInt32(cx, tvr.addr(), (int32_t *)ip);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip)
 {
     CHECK_REQUEST(cx);
 
-    AutoValueRooter tvr(cx, v);
-    return ValueToECMAUint32(cx, v, (uint32_t *)ip);
+    AutoValueRooter tvr(cx, Valueify(v));
+    return ValueToECMAUint32(cx, tvr.addr(), (uint32_t *)ip);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip)
 {
     CHECK_REQUEST(cx);
 
-    AutoValueRooter tvr(cx, v);
-    return ValueToInt32(cx, v, (int32_t *)ip);
+    AutoValueRooter tvr(cx, Valueify(v));
+    return ValueToInt32(cx, tvr.addr(), (int32_t *)ip);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip)
 {
     CHECK_REQUEST(cx);
 
-    AutoValueRooter tvr(cx, v);
-    return ValueToUint16(cx, v, (uint16_t *)ip);
+    AutoValueRooter tvr(cx, Valueify(v));
+    return ValueToUint16(cx, tvr.addr(), (uint16_t *)ip);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp)
 {
     CHECK_REQUEST(cx);
-    *bp = js_ValueToBoolean(v);
+    *bp = js_ValueToBoolean(Valueify(v));
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSType)
 JS_TypeOfValue(JSContext *cx, jsval v)
 {
-    JSType type;
-    JSObject *obj;
-
     CHECK_REQUEST(cx);
-    if (JSVAL_IS_OBJECT(v)) {
-        obj = JSVAL_TO_OBJECT(v);
-        if (obj)
-            return obj->map->ops->typeOf(cx, obj);
-        type = JSTYPE_OBJECT;
-    } else if (JSVAL_IS_NUMBER(v)) {
-        type = JSTYPE_NUMBER;
-    } else if (JSVAL_IS_STRING(v)) {
-        type = JSTYPE_STRING;
-    } else if (JSVAL_IS_BOOLEAN(v)) {
-        type = JSTYPE_BOOLEAN;
-    } else {
-        type = JSTYPE_VOID;
-    }
-    return type;
+    return TypeOfValue(cx, Valueify(v));
 }
 
 JS_PUBLIC_API(const char *)
 JS_GetTypeName(JSContext *cx, JSType type)
 {
     if ((uintN)type >= (uintN)JSTYPE_LIMIT)
         return NULL;
     return JS_TYPE_STR(type);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2)
 {
-    return js_StrictlyEqual(cx, v1, v2);
+    return StrictlyEqual(cx, Valueify(v1), Valueify(v2));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SameValue(JSContext *cx, jsval v1, jsval v2)
 {
-    return js_SameValue(v1, v2, cx);
+    return SameValue(cx, Valueify(v1), Valueify(v2));
 }
 
 /************************************************************************/
 
 /*
  * Has a new runtime ever been created?  This flag is used to detect unsafe
  * changes to js_CStringsAreUTF8 after a runtime has been created, and to
  * ensure that "first checks" on runtime creation are run only once.
@@ -677,31 +655,16 @@ JS_NewRuntime(uint32 maxbytes)
             if (*fmt == '{' && isdigit(fmt[1]))                               \
                 ++numfmtspecs;                                                \
         }                                                                     \
         JS_ASSERT(count == numfmtspecs);                                      \
     JS_END_MACRO;
 #include "js.msg"
 #undef MSG_DEF
 
-        /*
-         * If it were possible for pure inline function calls with constant
-         * arguments to be computed at compile time, these would be static
-         * assertions, but since it isn't, this is the best we can do.
-         */
-        JS_ASSERT(JSVAL_NULL == OBJECT_TO_JSVAL(NULL));
-        JS_ASSERT(JSVAL_ZERO == INT_TO_JSVAL(0));
-        JS_ASSERT(JSVAL_ONE == INT_TO_JSVAL(1));
-        JS_ASSERT(JSVAL_FALSE == BOOLEAN_TO_JSVAL(JS_FALSE));
-        JS_ASSERT(JSVAL_TRUE == BOOLEAN_TO_JSVAL(JS_TRUE));
-
-        JS_ASSERT(JSVAL_TO_SPECIAL(JSVAL_VOID) == 2);
-        JS_ASSERT(JSVAL_TO_SPECIAL(JSVAL_HOLE) == (2 | (JSVAL_HOLE_FLAG >> JSVAL_TAGBITS)));
-        JS_ASSERT(JSVAL_TO_SPECIAL(JSVAL_ARETURN) == 8);
-
         js_NewRuntimeWasCalled = JS_TRUE;
     }
 #endif /* DEBUG */
 
     void *mem = js_calloc(sizeof(JSRuntime));
     if (!mem)
         return NULL;
 
@@ -1143,35 +1106,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)),
-                            OBJECT_TO_JSVAL(ctor), 0, 0, 0);
+                            FunObjValue(*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(obj_proto);
+    fun_proto->setProto(NonFunObjPtr(*obj_proto));
     if (!obj->getProto())
-        obj->setProto(obj_proto);
+        obj->setProto(NonFunObjPtr(*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]));
@@ -1187,18 +1150,18 @@ JS_PUBLIC_API(JSBool)
 JS_InitStandardClasses(JSContext *cx, JSObject *obj)
 {
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
 
     /* Define a top-level property 'undefined' with the undefined value. */
     atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
-    if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), JSVAL_VOID,
-                             JS_PropertyStub, JS_PropertyStub,
+    if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), sUndefinedValue,
+                             PropertyStub, PropertyStub,
                              JSPROP_PERMANENT | JSPROP_READONLY)) {
         return JS_FALSE;
     }
 
     /* Function and Object require cooperative bootstrapping magic. */
     if (!js_InitFunctionAndObjectClasses(cx, obj))
         return JS_FALSE;
 
@@ -1229,17 +1192,17 @@ JS_InitStandardClasses(JSContext *cx, JS
 #define EAGER_ATOM_AND_CLASP(name)  EAGER_CLASS_ATOM(name), CLASP(name)
 #define EAGER_ATOM_AND_XCLASP(name) EAGER_CLASS_ATOM(name), XCLASP(name)
 #define LAZY_ATOM(name)             ATOM_OFFSET(lazy.name), js_##name##_str
 
 typedef struct JSStdName {
     JSObjectOp  init;
     size_t      atomOffset;     /* offset of atom pointer in JSAtomState */
     const char  *name;          /* null if atom is pre-pinned, else name */
-    JSClass     *clasp;
+    Class       *clasp;
 } JSStdName;
 
 static JSAtom *
 StdNameToAtom(JSContext *cx, JSStdName *stdn)
 {
     size_t offset;
     JSAtom *atom;
     const char *name;
@@ -1395,18 +1358,18 @@ JS_ResolveStandardClass(JSContext *cx, J
         return JS_TRUE;
 
     idstr = JSVAL_TO_STRING(id);
 
     /* Check whether we're resolving 'undefined', and define it if so. */
     atom = rt->atomState.typeAtoms[JSTYPE_VOID];
     if (idstr == ATOM_TO_STRING(atom)) {
         *resolved = JS_TRUE;
-        return obj->defineProperty(cx, ATOM_TO_JSID(atom), JSVAL_VOID,
-                                   JS_PropertyStub, JS_PropertyStub,
+        return obj->defineProperty(cx, ATOM_TO_JSID(atom), sUndefinedValue,
+                                   PropertyStub, PropertyStub,
                                    JSPROP_PERMANENT | JSPROP_READONLY);
     }
 
     /* Try for class constructors/prototypes named by well-known atoms. */
     stdnm = NULL;
     for (i = 0; standard_class_atoms[i].init; i++) {
         atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset);
         if (idstr == ATOM_TO_STRING(atom)) {
@@ -1486,18 +1449,18 @@ JS_EnumerateStandardClasses(JSContext *c
     uintN i;
 
     CHECK_REQUEST(cx);
     rt = cx->runtime;
 
     /* Check whether we need to bind 'undefined' and define it if so. */
     atom = rt->atomState.typeAtoms[JSTYPE_VOID];
     if (!AlreadyHasOwnProperty(cx, obj, atom) &&
-        !obj->defineProperty(cx, ATOM_TO_JSID(atom), JSVAL_VOID,
-                             JS_PropertyStub, JS_PropertyStub,
+        !obj->defineProperty(cx, ATOM_TO_JSID(atom), sUndefinedValue,
+                             PropertyStub, PropertyStub,
                              JSPROP_PERMANENT | JSPROP_READONLY)) {
         return JS_FALSE;
     }
 
     /* Initialize any classes that have not been resolved yet. */
     for (i = 0; standard_class_atoms[i].init; i++) {
         atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset);
         if (!AlreadyHasOwnProperty(cx, obj, atom) &&
@@ -1661,34 +1624,35 @@ 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;
         }
 
-        OBJ_TO_INNER_OBJECT(cx, obj);
+        Innerize(cx, &obj);
         return obj;
     }
     return js_GetScopeChain(cx, fp);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetGlobalForObject(JSContext *cx, JSObject *obj)
 {
     return obj->getGlobal();
 }
 
 JS_PUBLIC_API(jsval)
 JS_ComputeThis(JSContext *cx, jsval *vp)
 {
-    if (!js_ComputeThis(cx, vp + 2))
+    Value *thisvp;
+    if (!ComputeThisValueFromVp(cx, Valueify(vp), &thisvp))
         return JSVAL_NULL;
-    return vp[1];
+    return Jsvalify(*thisvp);
 }
 
 JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes)
 {
     return cx->malloc(nbytes);
 }
 
@@ -1718,75 +1682,115 @@ JS_strdup(JSContext *cx, const char *s)
 
     n = strlen(s) + 1;
     p = cx->malloc(n);
     if (!p)
         return NULL;
     return (char *)memcpy(p, s, n);
 }
 
-JS_PUBLIC_API(jsdouble *)
-JS_NewDouble(JSContext *cx, jsdouble d)
+#undef JS_AddRoot
+
+JS_PUBLIC_API(JSBool)
+JS_AddValueRoot(JSContext *cx, js::Value *vp)
 {
     CHECK_REQUEST(cx);
-    return js_NewWeaklyRootedDouble(cx, d);
+    return js_AddRoot(cx, vp, NULL);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_AddStringRoot(JSContext *cx, JSString **rp)
+{
+    CHECK_REQUEST(cx);
+    return js_AddGCThingRoot(cx, (void **)rp, NULL);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval)
-{
-    jsdouble *dp;
-
+JS_AddObjectRoot(JSContext *cx, JSObject **rp)
+{
+    CHECK_REQUEST(cx);
+    return js_AddGCThingRoot(cx, (void **)rp, NULL);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_AddNamedValueRoot(JSContext *cx, js::Value *vp, const char *name)
+{
     CHECK_REQUEST(cx);
-    dp = js_NewWeaklyRootedDouble(cx, d);
-    if (!dp)
-        return JS_FALSE;
-    *rval = DOUBLE_TO_JSVAL(dp);
-    return JS_TRUE;
+    return js_AddRoot(cx, vp, name);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_AddNamedStringRoot(JSContext *cx, JSString **rp, const char *name)
+{
+    CHECK_REQUEST(cx);
+    return js_AddGCThingRoot(cx, (void **)rp, name);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_AddNamedObjectRoot(JSContext *cx, JSObject **rp, const char *name)
+{
+    CHECK_REQUEST(cx);
+    return js_AddGCThingRoot(cx, (void **)rp, name);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
+JS_AddNamedValueRootRT(JSRuntime *rt, js::Value *vp, const char *name)
+{
+    return js_AddRootRT(rt, vp, name);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_AddNamedStringRootRT(JSRuntime *rt, JSString **rp, const char *name)
+{
+    return js_AddGCThingRootRT(rt, (void **)rp, name);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_AddNamedObjectRootRT(JSRuntime *rt, JSObject **rp, const char *name)
+{
+    return js_AddGCThingRootRT(rt, (void **)rp, name);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_RemoveValueRoot(JSContext *cx, js::Value *vp)
 {
     CHECK_REQUEST(cx);
-    return js_NewWeaklyRootedNumber(cx, d, rval);
-}
-
-#undef JS_AddRoot
-JS_PUBLIC_API(JSBool)
-JS_AddRoot(JSContext *cx, void *rp)
-{
-    CHECK_REQUEST(cx);
-    return js_AddRoot(cx, rp, NULL);
+    return js_RemoveRoot(cx->runtime, (void *)vp);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name)
-{
-    return js_AddRootRT(rt, rp, name);
+JS_RemoveStringRoot(JSContext *cx, JSString **rp)
+{
+    CHECK_REQUEST(cx);
+    return js_RemoveRoot(cx->runtime, (void *)rp);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_RemoveObjectRoot(JSContext *cx, JSObject **rp)
+{
+    CHECK_REQUEST(cx);
+    return js_RemoveRoot(cx->runtime, (void *)rp);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_RemoveRoot(JSContext *cx, void *rp)
-{
-    CHECK_REQUEST(cx);
-    return js_RemoveRoot(cx->runtime, rp);
+JS_RemoveValueRootRT(JSRuntime *rt, js::Value *vp)
+{
+    return js_RemoveRoot(rt, (void *)vp);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_RemoveRootRT(JSRuntime *rt, void *rp)
-{
-    return js_RemoveRoot(rt, rp);
+JS_RemoveStringRootRT(JSRuntime *rt, JSString **rp)
+{
+    return js_RemoveRoot(rt, (void *)rp);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_AddNamedRoot(JSContext *cx, void *rp, const char *name)
-{
-    CHECK_REQUEST(cx);
-    return js_AddRoot(cx, rp, name);
+JS_RemoveObjectRootRT(JSRuntime *rt, JSObject **rp)
+{
+    return js_RemoveRoot(rt, (void *)rp);
 }
 
 JS_PUBLIC_API(void)
 JS_ClearNewbornRoots(JSContext *cx)
 {
     JS_CLEAR_WEAK_ROOTS(&cx->weakRoots);
 }
 
@@ -1803,31 +1807,31 @@ JS_LeaveLocalRootScope(JSContext *cx)
     CHECK_REQUEST(cx);
     js_LeaveLocalRootScope(cx);
 }
 
 JS_PUBLIC_API(void)
 JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
 {
     CHECK_REQUEST(cx);
-    js_LeaveLocalRootScopeWithResult(cx, rval);
+    js_LeaveLocalRootScopeWithResult(cx, Valueify(rval));
 }
 
 JS_PUBLIC_API(void)
 JS_ForgetLocalRoot(JSContext *cx, void *thing)
 {
     CHECK_REQUEST(cx);
-    js_ForgetLocalRoot(cx, (jsval) thing);
+    js_ForgetLocalRoot(cx, thing);
 }
 
 #ifdef DEBUG
 
 JS_PUBLIC_API(void)
 JS_DumpNamedRoots(JSRuntime *rt,
-                  void (*dump)(const char *name, void *rp, void *data),
+                  void (*dump)(const char *name, void *rp, JSGCRootType type, void *data),
                   void *data)
 {
     js_DumpNamedRoots(rt, dump, data);
 }
 
 #endif /* DEBUG */
 
 JS_PUBLIC_API(uint32)
@@ -1883,17 +1887,17 @@ JS_TraceRuntime(JSTracer *trc)
 
     LeaveTrace(trc->context);
     js_TraceRuntime(trc, allAtoms);
 }
 
 JS_PUBLIC_API(void)
 JS_CallTracer(JSTracer *trc, void *thing, uint32 kind)
 {
-    js_CallGCMarker(trc, thing, kind);
+    CallGCMarker(trc, thing, kind);
 }
 
 #ifdef DEBUG
 
 #ifdef HAVE_XPCONNECT
 #include "dump_xpc.h"
 #endif
 
@@ -1906,17 +1910,17 @@ JS_PrintTraceThingInfo(char *buf, size_t
 
     if (bufsize == 0)
         return;
 
     switch (kind) {
       case JSTRACE_OBJECT:
       {
         JSObject *obj = (JSObject *)thing;
-        JSClass *clasp = obj->getClass();
+        Class *clasp = obj->getClass();
 
         name = clasp->name;
 #ifdef HAVE_XPCONNECT
         if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
             void *privateThing = obj->getPrivate();
             if (privateThing) {
                 const char *xpcClassName = GetXPCObjectClassName(privateThing);
                 if (xpcClassName)
@@ -1958,17 +1962,17 @@ JS_PrintTraceThingInfo(char *buf, size_t
     if (details && bufsize > 2) {
         *buf++ = ' ';
         bufsize--;
 
         switch (kind) {
           case JSTRACE_OBJECT:
           {
             JSObject  *obj = (JSObject *)thing;
-            JSClass *clasp = obj->getClass();
+            Class *clasp = obj->getClass();
             if (clasp == &js_FunctionClass) {
                 JSFunction *fun = GET_FUNCTION_PRIVATE(trc->context, obj);
                 if (!fun) {
                     JS_snprintf(buf, bufsize, "<newborn>");
                 } else if (FUN_OBJECT(fun) != obj) {
                     JS_snprintf(buf, bufsize, "%p", fun);
                 } else {
                     if (fun->atom && ATOM_IS_STRING(fun->atom))
@@ -2262,31 +2266,31 @@ JS_DumpHeap(JSContext *cx, FILE *fp, voi
     JS_ASSERT(depth == 1);
     JS_DHashTableFinish(&dtrc.visited);
     return dtrc.ok;
 }
 
 #endif /* DEBUG */
 
 JS_PUBLIC_API(void)
-JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg)
+JS_MarkGCThing(JSContext *cx, jsval v, const char *name, void *arg)
 {
     JSTracer *trc;
 
     trc = (JSTracer *)arg;
     if (!trc)
         trc = cx->runtime->gcMarkingTracer;
     else
         JS_ASSERT(trc == cx->runtime->gcMarkingTracer);
 
 #ifdef JS_THREADSAFE
     JS_ASSERT(cx->runtime->gcThread == trc->context->thread);
 #endif
     JS_SET_TRACING_NAME(trc, name ? name : "unknown");
-    js_CallValueTracerIfGCThing(trc, (jsval)thing);
+    CallGCMarkerIfGCThing(trc, Valueify(v));
 }
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsGCMarkingTracer(JSTracer *trc)
 {
     return IS_GC_MARKING_TRACER(trc);
 }
 
@@ -2524,128 +2528,113 @@ JS_SetScriptStackQuota(JSContext *cx, si
 /************************************************************************/
 
 JS_PUBLIC_API(void)
 JS_DestroyIdArray(JSContext *cx, JSIdArray *ida)
 {
     cx->free(ida);
 }
 
-JS_PUBLIC_API(JSBool)
-JS_ValueToId(JSContext *cx, jsval v, jsid *idp)
+JS_PUBLIC_API(jsval)
+JSID_TO_JSVAL(jsid id)
 {
     CHECK_REQUEST(cx);
-    if (JSVAL_IS_INT(v)) {
-        *idp = INT_JSVAL_TO_JSID(v);
-        return JS_TRUE;
-    }
-
-#if JS_HAS_XML_SUPPORT
-    if (!JSVAL_IS_PRIMITIVE(v)) {
-        JSClass *clasp = JSVAL_TO_OBJECT(v)->getClass();
-        if (JS_UNLIKELY(clasp == &js_QNameClass.base ||
-                        clasp == &js_AttributeNameClass ||
-                        clasp == &js_AnyNameClass)) {
-            *idp = OBJECT_JSVAL_TO_JSID(v);
-            return JS_TRUE;
-        }
-    }
-#endif
-
-    return js_ValueToStringId(cx, v, idp);
+    return Jsvalify(IdToValue(id));
+}
+
+JS_PUBLIC_API(JSBool)
+JS_ValueToId(JSContext *cx, const jsval *vp, jsid *idp)
+{
+    CHECK_REQUEST(cx);
+    return ValueToId(cx, Valueify(*vp), idp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IdToValue(JSContext *cx, jsid id, jsval *vp)
 {
     CHECK_REQUEST(cx);
-    *vp = ID_TO_VALUE(id);
+    *vp = Jsvalify(IdToValue(id));
     return JS_TRUE;
 }
 
-JS_PUBLIC_API(JSBool)
-JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+namespace js {
+
+JSBool
+PropertyStub(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     return JS_TRUE;
 }
 
-JS_PUBLIC_API(JSBool)
-JS_EnumerateStub(JSContext *cx, JSObject *obj)
+JSBool
+EnumerateStub(JSContext *cx, JSObject *obj)
 {
     return JS_TRUE;
 }
 
-JS_PUBLIC_API(JSBool)
-JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id)
+JSBool
+ResolveStub(JSContext *cx, JSObject *obj, jsid id)
 {
     return JS_TRUE;
 }
 
-JS_PUBLIC_API(JSBool)
-JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
+JSBool
+ConvertStub(JSContext *cx, JSObject *obj, JSType type, Value *vp)
 {
     return js_TryValueOf(cx, obj, type, vp);
 }
 
-JS_PUBLIC_API(void)
-JS_FinalizeStub(JSContext *cx, JSObject *obj)
-{
-}
+void
+FinalizeStub(JSContext *cx, JSObject *obj)
+{}
+
+} /* namespace js */
+
+JS_PUBLIC_DATA(JSPropertyOp)  JS_PropertyStub  = Jsvalify(PropertyStub);
+JS_PUBLIC_DATA(JSEnumerateOp) JS_EnumerateStub = EnumerateStub;
+JS_PUBLIC_DATA(JSResolveOp)   JS_ResolveStub   = ResolveStub;
+JS_PUBLIC_DATA(JSConvertOp)   JS_ConvertStub   = Jsvalify(ConvertStub);
+JS_PUBLIC_DATA(JSFinalizeOp)  JS_FinalizeStub  = FinalizeStub;
 
 JS_PUBLIC_API(JSObject *)
 JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
              JSClass *clasp, JSNative constructor, uintN nargs,
              JSPropertySpec *ps, JSFunctionSpec *fs,
              JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
 {
     CHECK_REQUEST(cx);
-    return js_InitClass(cx, obj, parent_proto, clasp, constructor, nargs,
+    return js_InitClass(cx, obj, parent_proto, Valueify(clasp),
+                        Valueify(constructor), nargs,
                         ps, fs, static_ps, static_fs);
 }
 
 #ifdef JS_THREADSAFE
 JS_PUBLIC_API(JSClass *)
 JS_GetClass(JSContext *cx, JSObject *obj)
 {
     return obj->getClass();
 }
 #else
 JS_PUBLIC_API(JSClass *)
 JS_GetClass(JSObject *obj)
 {
-    return obj->getClass();
+    return Jsvalify(obj->getClass());
 }
 #endif
 
 JS_PUBLIC_API(JSBool)
 JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv)
 {
-    JSFunction *fun;
-
     CHECK_REQUEST(cx);
-    if (obj && obj->getClass() == clasp)
-        return JS_TRUE;
-    if (argv) {
-        fun = js_ValueToFunction(cx, &argv[-2], 0);
-        if (fun) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                                 JSMSG_INCOMPATIBLE_PROTO,
-                                 clasp->name, JS_GetFunctionName(fun),
-                                 obj
-                                 ? obj->getClass()->name
-                                 : js_null_str);
-        }
-    }
-    return JS_FALSE;
+    return InstanceOf(cx, obj, Valueify(clasp), Valueify(argv));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
 {
-    return js_HasInstance(cx, obj, v, bp);
+    return js_HasInstance(cx, obj, Valueify(&v), bp);
 }
 
 JS_PUBLIC_API(void *)
 JS_GetPrivate(JSContext *cx, JSObject *obj)
 {
     return obj->getPrivate();
 }
 
@@ -2655,17 +2644,17 @@ JS_SetPrivate(JSContext *cx, JSObject *o
     obj->setPrivate(data);
     return true;
 }
 
 JS_PUBLIC_API(void *)
 JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp,
                       jsval *argv)
 {
-    if (!JS_InstanceOf(cx, obj, clasp, argv))
+    if (!InstanceOf(cx, obj, Valueify(clasp), Valueify(argv)))
         return NULL;
     return obj->getPrivate();
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetPrototype(JSContext *cx, JSObject *obj)
 {
     JSObject *proto;
@@ -2692,73 +2681,76 @@ JS_GetParent(JSContext *cx, JSObject *ob
     /* Beware ref to dead object (we may be called from obj's finalizer). */
     return parent && parent->map ? parent : NULL;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent)
 {
     CHECK_REQUEST(cx);
+    JS_ASSERT(!parent->isFunction() &&
+              "Functions may not be set as the parents of objects.");
     return js_SetProtoOrParent(cx, obj, JSSLOT_PARENT, parent, JS_FALSE);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetConstructor(JSContext *cx, JSObject *proto)
 {
-    jsval cval;
+    Value cval;
 
     CHECK_REQUEST(cx);
     {
         JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
 
         if (!proto->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), &cval))
             return NULL;
     }
-    if (!VALUE_IS_FUNCTION(cx, cval)) {
+    if (!cval.isFunObj()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR,
                              proto->getClass()->name);
         return NULL;
     }
-    return JSVAL_TO_OBJECT(cval);
+    return &cval.asFunObj();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp)
 {
     JS_ASSERT(JSID_IS_OBJECT(obj));
     *idp = OBJECT_TO_JSID(obj);
     return JS_TRUE;
 }
 
 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);
+    Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
     return js_NewObject(cx, clasp, proto, parent);
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto,
+JS_NewObjectWithGivenProto(JSContext *cx, JSClass *jsclasp, JSObject *proto,
                            JSObject *parent)
 {
     CHECK_REQUEST(cx);
+    Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
     return js_NewObjectWithGivenProto(cx, clasp, proto, parent);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep)
 {
     JSScope *scope;
     JSIdArray *ida;
     uint32 nslots, i;
-    jsval v;
 
     if (obj->isDenseArray() && !js_MakeArraySlow(cx, obj))
         return JS_FALSE;
 
     if (!obj->isNative()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_CANT_SEAL_OBJECT,
                              obj->getClass()->name);
@@ -2798,63 +2790,66 @@ JS_SealObject(JSContext *cx, JSObject *o
 
     /* If we are not sealing an entire object graph, we're done. */
     if (!deep)
         return JS_TRUE;
 
     /* Walk slots in obj and if any value is a non-null object, seal it. */
     nslots = scope->freeslot;
     for (i = 0; i != nslots; ++i) {
-        v = obj->getSlot(i);
+        const Value &v = obj->getSlot(i);
         if (i == JSSLOT_PRIVATE && (obj->getClass()->flags & JSCLASS_HAS_PRIVATE))
             continue;
-        if (JSVAL_IS_PRIMITIVE(v))
+        if (v.isPrimitive())
             continue;
-        if (!JS_SealObject(cx, JSVAL_TO_OBJECT(v), deep))
+        if (!JS_SealObject(cx, &v.asObject(), deep))
             return JS_FALSE;
     }
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
+JS_ConstructObject(JSContext *cx, JSClass *jsclasp, JSObject *proto,
                    JSObject *parent)
 {
     CHECK_REQUEST(cx);
+    Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
     return js_ConstructObject(cx, clasp, proto, parent, 0, NULL);
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto,
+JS_ConstructObjectWithArguments(JSContext *cx, JSClass *jsclasp, JSObject *proto,
                                 JSObject *parent, uintN argc, jsval *argv)
 {
     CHECK_REQUEST(cx);
+    Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
-    return js_ConstructObject(cx, clasp, proto, parent, argc, argv);
+    return js_ConstructObject(cx, clasp, proto, parent, argc, Valueify(argv));
 }
 
 static JSBool
-DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value,
-                   JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
+DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, const Value &value,
+                   PropertyOp getter, PropertyOp setter, uintN attrs,
                    uintN flags, intN tinyid)
 {
     if (flags != 0 && obj->isNative()) {
         JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING);
-        return !!js_DefineNativeProperty(cx, obj, id, value, getter, setter,
-                                         attrs, flags, tinyid, NULL);
+        return !!js_DefineNativeProperty(cx, obj, id, value,
+                                         getter, setter, attrs, flags,
+                                         tinyid, NULL);
     }
     return obj->defineProperty(cx, id, value, getter, setter, attrs);
 }
 
 static JSBool
-DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value,
-               JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
+DefineProperty(JSContext *cx, JSObject *obj, const char *name, const Value &value,
+               PropertyOp getter, PropertyOp setter, uintN attrs,
                uintN flags, intN tinyid)
 {
     jsid id;
     JSAtom *atom;
 
     if (attrs & JSPROP_INDEX) {
         id = INT_TO_JSID(intptr_t(name));
         atom = NULL;
@@ -2868,65 +2863,62 @@ DefineProperty(JSContext *cx, JSObject *
     return DefinePropertyById(cx, obj, id, value, getter, setter, attrs,
                               flags, tinyid);
 }
 
 #define AUTO_NAMELEN(s,n)   (((n) == (size_t)-1) ? js_strlen(s) : (n))
 
 static JSBool
 DefineUCProperty(JSContext *cx, JSObject *obj,
-                 const jschar *name, size_t namelen, jsval value,
-                 JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
+                 const jschar *name, size_t namelen, const Value &value,
+                 PropertyOp getter, PropertyOp setter, uintN attrs,
                  uintN flags, intN tinyid)
 {
     JSAtom *atom;
 
     atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0);
     if (!atom)
         return JS_FALSE;
     if (flags != 0 && obj->isNative()) {
         JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING);
-        return !!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value,
-                                         getter, setter, attrs, flags, tinyid,
-                                         NULL);
+        return !!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom),
+                                         value, getter, setter, attrs,
+                                         flags, tinyid, NULL);
     }
-    return obj->defineProperty(cx, ATOM_TO_JSID(atom), value, getter, setter, attrs);
+    return obj->defineProperty(cx, ATOM_TO_JSID(atom), value, getter,
+                               setter, attrs);
 }
 
 JS_PUBLIC_API(JSObject *)
-JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp,
+JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *jsclasp,
                 JSObject *proto, uintN attrs)
 {
-    JSObject *nobj;
-
     CHECK_REQUEST(cx);
+    Class *clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &js_ObjectClass;    /* default class is Object */
-    nobj = js_NewObject(cx, clasp, proto, obj);
+    JSObject *nobj = js_NewObject(cx, clasp, proto, obj);
     if (!nobj)
         return NULL;
-    if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs,
+    if (!DefineProperty(cx, obj, name, Value(nobj), NULL, NULL, attrs,
                         0, 0)) {
         return NULL;
     }
     return nobj;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds)
 {
     JSBool ok;
-    jsval value;
     uintN attrs;
 
     CHECK_REQUEST(cx);
     for (ok = JS_TRUE; cds->name; cds++) {
-        ok = js_NewNumberInRootedValue(cx, cds->dval, &value);
-        if (!ok)
-            break;
+        Value value(cds->dval);
         attrs = cds->flags;
         if (!attrs)
             attrs = JSPROP_READONLY | JSPROP_PERMANENT;
         ok = DefineProperty(cx, obj, cds->name, value, NULL, NULL, attrs, 0, 0);
         if (!ok)
             break;
     }
     return ok;
@@ -2934,57 +2926,62 @@ JS_DefineConstDoubles(JSContext *cx, JSO
 
 JS_PUBLIC_API(JSBool)
 JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps)
 {
     JSBool ok;
 
     CHECK_REQUEST(cx);
     for (ok = JS_TRUE; ps->name; ps++) {
-        ok = DefineProperty(cx, obj, ps->name, JSVAL_VOID,
-                            ps->getter, ps->setter, ps->flags,
+        ok = DefineProperty(cx, obj, ps->name, sUndefinedValue,
+                            Valueify(ps->getter), Valueify(ps->setter), ps->flags,
                             JSScopeProperty::HAS_SHORTID, ps->tinyid);
         if (!ok)
             break;
     }
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value,
                   JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
 {
     CHECK_REQUEST(cx);
-    return DefineProperty(cx, obj, name, value, getter, setter, attrs, 0, 0);
+    return DefineProperty(cx, obj, name, Valueify(value),
+                          Valueify(getter), Valueify(setter),
+                          attrs, 0, 0);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value,
                       JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
 {
     CHECK_REQUEST(cx);
-    return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, 0, 0);
+    return DefinePropertyById(cx, obj, id, Valueify(value),
+                              Valueify(getter), Valueify(setter),
+                              attrs, 0, 0);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name,
                             int8 tinyid, jsval value,
                             JSPropertyOp getter, JSPropertyOp setter,
                             uintN attrs)
 {
     CHECK_REQUEST(cx);
-    return DefineProperty(cx, obj, name, value, getter, setter, attrs,
-                          JSScopeProperty::HAS_SHORTID, tinyid);
+    return DefineProperty(cx, obj, name, Valueify(value),
+                          Valueify(getter), Valueify(setter),
+                          attrs, JSScopeProperty::HAS_SHORTID, tinyid);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, jsval descriptor, JSBool *bp)
 {
     CHECK_REQUEST(cx);
-    return js_DefineOwnProperty(cx, obj, id, descriptor, bp);
+    return js_DefineOwnProperty(cx, obj, id, Valueify(descriptor), bp);
 }
 
 static JSBool
 LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, uintN flags,
                    JSObject **objp, JSProperty **propp)
 {
     JSAutoResolveFlags rf(cx, flags);
     id = js_CheckForStringIndex(id);
@@ -3051,44 +3048,45 @@ JS_AliasProperty(JSContext *cx, JSObject
               != NULL);
     }
     obj->dropProperty(cx, prop);
     return ok;
 }
 
 static JSBool
 LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, JSProperty *prop,
-             jsval *vp)
+             Value *vp)
 {
     if (!prop) {
         /* XXX bad API: no way to tell "not defined" from "void value" */
-        *vp = JSVAL_VOID;
+        vp->setUndefined();
         return JS_TRUE;
     }
 
     JSBool ok = JS_TRUE;
     if (obj2->isNative()) {
         JSScopeProperty *sprop = (JSScopeProperty *) prop;
 
         if (sprop->isMethod()) {
             AutoScopePropertyRooter root(cx, sprop);
             JS_UNLOCK_OBJ(cx, obj2);
-            *vp = sprop->methodValue();
+            vp->copy(sprop->methodValue());
             return obj2->scope()->methodReadBarrier(cx, sprop, vp);
         }
 
         /* Peek at the native property's slot value, without doing a Get. */
-        *vp = SPROP_HAS_VALID_SLOT(sprop, obj2->scope())
-               ? obj2->lockedGetSlot(sprop->slot)
-               : JSVAL_TRUE;
+        if (SPROP_HAS_VALID_SLOT(sprop, obj2->scope()))
+            vp->copy(obj->lockedGetSlot(sprop->slot));
+        else
+            vp->setBoolean(true);
     } else if (obj2->isDenseArray()) {
         ok = js_GetDenseArrayElementValue(cx, obj2, prop, vp);
     } else {
         /* XXX bad API: no way to return "defined but value unknown" */
-        *vp = JSVAL_TRUE;
+        vp->setBoolean(true);
     }
     obj2->dropProperty(cx, prop);
     return ok;
 }
 
 static JSBool
 GetPropertyAttributesById(JSContext *cx, JSObject *obj, jsid id, uintN flags,
                           JSBool own, JSPropertyDescriptor *desc)
@@ -3113,21 +3111,22 @@ GetPropertyAttributesById(JSContext *cx,
 
     desc->obj = obj2;
 
     ok = obj2->getAttributes(cx, id, prop, &desc->attrs);
     if (ok) {
         if (obj2->isNative()) {
             JSScopeProperty *sprop = (JSScopeProperty *) prop;
 
-            desc->getter = sprop->getter();
-            desc->setter = sprop->setter();
-            desc->value = SPROP_HAS_VALID_SLOT(sprop, obj2->scope())
-                          ? obj2->lockedGetSlot(sprop->slot)
-                          : JSVAL_VOID;
+            desc->getter = Jsvalify(sprop->getter());
+            desc->setter = Jsvalify(sprop->setter());
+            if (SPROP_HAS_VALID_SLOT(sprop, obj2->scope()))
+                Valueify(desc->value).copy(obj2->lockedGetSlot(sprop->slot));
+            else
+                Valueify(desc->value).setUndefined();
         } else {
             desc->getter = NULL;
             desc->setter = NULL;
             desc->value = JSVAL_VOID;
         }
     }
     obj2->dropProperty(cx, prop);
     return ok;
@@ -3329,28 +3328,28 @@ JS_HasPropertyById(JSContext *cx, JSObje
 JS_PUBLIC_API(JSBool)
 JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp)
 {
     JSObject *obj2;
     JSProperty *prop;
 
     CHECK_REQUEST(cx);
     return LookupProperty(cx, obj, name, JSRESOLVE_QUALIFIED, &obj2, &prop) &&
-           LookupResult(cx, obj, obj2, prop, vp);
+           LookupResult(cx, obj, obj2, prop, Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     JSObject *obj2;
     JSProperty *prop;
 
     CHECK_REQUEST(cx);
     return LookupPropertyById(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop) &&
-           LookupResult(cx, obj, obj2, prop, vp);
+           LookupResult(cx, obj, obj2, prop, Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name,
                            uintN flags, jsval *vp)
 {
     JSAtom *atom;
     JSObject *obj2;
@@ -3368,64 +3367,64 @@ JS_LookupPropertyWithFlagsById(JSContext
     JSBool ok;
     JSProperty *prop;
 
     CHECK_REQUEST(cx);
     ok = obj->isNative()
          ? js_LookupPropertyWithFlags(cx, obj, id, flags, objp, &prop) >= 0
          : obj->lookupProperty(cx, id, objp, &prop);
     if (ok)
-        ok = LookupResult(cx, obj, *objp, prop, vp);
+        ok = LookupResult(cx, obj, *objp, prop, Valueify(vp));
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, uintN flags,
                              JSPropertyDescriptor *desc)
 {
     CHECK_REQUEST(cx);
 
     return GetPropertyAttributesById(cx, obj, id, flags, JS_FALSE, desc);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     CHECK_REQUEST(cx);
-    return js_GetOwnPropertyDescriptor(cx, obj, id, vp);
+    return js_GetOwnPropertyDescriptor(cx, obj, id, Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp)
 {
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     atom = js_Atomize(cx, name, strlen(name), 0);
     if (!atom)
         return JS_FALSE;
 
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
-    return obj->getProperty(cx, ATOM_TO_JSID(atom), vp);
+    return obj->getProperty(cx, ATOM_TO_JSID(atom), Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     CHECK_REQUEST(cx);
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
-    return obj->getProperty(cx, id, vp);
+    return obj->getProperty(cx, id, Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
                  jsval *vp)
 {
     CHECK_REQUEST(cx);
-    if (!js_GetMethod(cx, obj, id, JSGET_METHOD_BARRIER, vp))
+    if (!js_GetMethod(cx, obj, id, JSGET_METHOD_BARRIER, Valueify(vp)))
         return JS_FALSE;
     if (objp)
         *objp = obj;
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp,
@@ -3445,25 +3444,25 @@ JS_SetProperty(JSContext *cx, JSObject *
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     atom = js_Atomize(cx, name, strlen(name), 0);
     if (!atom)
         return JS_FALSE;
 
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
-    return obj->setProperty(cx, ATOM_TO_JSID(atom), vp);
+    return obj->setProperty(cx, ATOM_TO_JSID(atom), Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     CHECK_REQUEST(cx);
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
-    return obj->setProperty(cx, id, vp);
+    return obj->setProperty(cx, id, Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name)
 {
     jsval junk;
 
     return JS_DeleteProperty2(cx, obj, name, &junk);
@@ -3476,43 +3475,44 @@ JS_DeleteProperty2(JSContext *cx, JSObje
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     atom = js_Atomize(cx, name, strlen(name), 0);
     if (!atom)
         return JS_FALSE;
 
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
-    return obj->deleteProperty(cx, ATOM_TO_JSID(atom), rval);
+    return obj->deleteProperty(cx, ATOM_TO_JSID(atom), Valueify(rval));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeletePropertyById(JSContext *cx, JSObject *obj, jsid id)
 {
     jsval junk;
 
     return JS_DeletePropertyById2(cx, obj, id, &junk);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeletePropertyById2(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
 {
     CHECK_REQUEST(cx);
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
-    return obj->deleteProperty(cx, id, rval);
+    return obj->deleteProperty(cx, id, Valueify(rval));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefineUCProperty(JSContext *cx, JSObject *obj,
                     const jschar *name, size_t namelen, jsval value,
                     JSPropertyOp getter, JSPropertyOp setter,
                     uintN attrs)
 {
     CHECK_REQUEST(cx);
-    return DefineUCProperty(cx, obj, name, namelen, value, getter, setter,
+    return DefineUCProperty(cx, obj, name, namelen, Valueify(value),
+                            Valueify(getter), Valueify(setter),
                             attrs, 0, 0);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj,
                            const jschar *name, size_t namelen,
                            uintN *attrsp, JSBool *foundp)
 {
@@ -3549,17 +3549,18 @@ JS_SetUCPropertyAttributes(JSContext *cx
 JS_PUBLIC_API(JSBool)
 JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj,
                               const jschar *name, size_t namelen,
                               int8 tinyid, jsval value,
                               JSPropertyOp getter, JSPropertyOp setter,
                               uintN attrs)
 {
     CHECK_REQUEST(cx);
-    return DefineUCProperty(cx, obj, name, namelen, value, getter, setter,
+    return DefineUCProperty(cx, obj, name, namelen, Valueify(value),
+                            Valueify(getter), Valueify(setter),
                             attrs, JSScopeProperty::HAS_SHORTID, tinyid);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AlreadyHasOwnUCProperty(JSContext *cx, JSObject *obj,
                            const jschar *name, size_t namelen,
                            JSBool *foundp)
 {
@@ -3599,73 +3600,73 @@ JS_LookupUCProperty(JSContext *cx, JSObj
                     jsval *vp)
 {
     JSObject *obj2;
     JSProperty *prop;
 
     CHECK_REQUEST(cx);
     return LookupUCProperty(cx, obj, name, namelen, JSRESOLVE_QUALIFIED,
                             &obj2, &prop) &&
-           LookupResult(cx, obj, obj2, prop, vp);
+           LookupResult(cx, obj, obj2, prop, Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetUCProperty(JSContext *cx, JSObject *obj,
                  const jschar *name, size_t namelen,
                  jsval *vp)
 {
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0);
     if (!atom)
         return JS_FALSE;
 
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
-    return obj->getProperty(cx, ATOM_TO_JSID(atom), vp);
+    return obj->getProperty(cx, ATOM_TO_JSID(atom), Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetUCProperty(JSContext *cx, JSObject *obj,
                  const jschar *name, size_t namelen,
                  jsval *vp)
 {
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0);
     if (!atom)
         return JS_FALSE;
 
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
-    return obj->setProperty(cx, ATOM_TO_JSID(atom), vp);
+    return obj->setProperty(cx, ATOM_TO_JSID(atom), Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeleteUCProperty2(JSContext *cx, JSObject *obj,
                      const jschar *name, size_t namelen,
                      jsval *rval)
 {
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0);
     if (!atom)
         return JS_FALSE;
 
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
-    return obj->deleteProperty(cx, ATOM_TO_JSID(atom), rval);
+    return obj->deleteProperty(cx, ATOM_TO_JSID(atom), Valueify(rval));
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector)
 {
     CHECK_REQUEST(cx);
     /* NB: jsuint cast does ToUint32. */
-    return js_NewArrayObject(cx, (jsuint)length, vector);
+    return js_NewArrayObject(cx, (jsuint)length, Valueify(vector));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsArrayObject(JSContext *cx, JSObject *obj)
 {
     return js_GetWrappedObject(cx, obj)->isArray();
 }
 
@@ -3692,17 +3693,18 @@ JS_HasArrayLength(JSContext *cx, JSObjec
 
 JS_PUBLIC_API(JSBool)
 JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value,
                  JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
 {
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING);
 
     CHECK_REQUEST(cx);
-    return obj->defineProperty(cx, INT_TO_JSID(index), value, getter, setter, attrs);
+    return obj->defineProperty(cx, INT_TO_JSID(index), Valueify(value),
+                               Valueify(getter), Valueify(setter), attrs);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias)
 {
     JSObject *obj2;
     JSProperty *prop;
     JSScopeProperty *sprop;
@@ -3763,52 +3765,52 @@ JS_PUBLIC_API(JSBool)
 JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp)
 {
     JSObject *obj2;
     JSProperty *prop;
 
     CHECK_REQUEST(cx);
     return LookupPropertyById(cx, obj, INT_TO_JSID(index), JSRESOLVE_QUALIFIED,
                               &obj2, &prop) &&
-           LookupResult(cx, obj, obj2, prop, vp);
+           LookupResult(cx, obj, obj2, prop, Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp)
 {
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
 
     CHECK_REQUEST(cx);
-    return obj->getProperty(cx, INT_TO_JSID(index), vp);
+    return obj->getProperty(cx, INT_TO_JSID(index), Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp)
 {
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
 
     CHECK_REQUEST(cx);
-    return obj->setProperty(cx, INT_TO_JSID(index), vp);
+    return obj->setProperty(cx, INT_TO_JSID(index), Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index)
 {
     jsval junk;
 
     return JS_DeleteElement2(cx, obj, index, &junk);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval)
 {
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
 
     CHECK_REQUEST(cx);
-    return obj->deleteProperty(cx, INT_TO_JSID(index), rval);
+    return obj->deleteProperty(cx, INT_TO_JSID(index), Valueify(rval));
 }
 
 JS_PUBLIC_API(void)
 JS_ClearScope(JSContext *cx, JSObject *obj)
 {
     CHECK_REQUEST(cx);
 
     if (obj->map->ops->clear)
@@ -3824,50 +3826,50 @@ JS_ClearScope(JSContext *cx, JSObject *o
 }
 
 JS_PUBLIC_API(JSIdArray *)
 JS_Enumerate(JSContext *cx, JSObject *obj)
 {
     jsint i, n;
     jsid id;
     JSIdArray *ida;
-    jsval *vector;
+    jsid *vector;
 
     CHECK_REQUEST(cx);
 
     ida = NULL;
     AutoEnumStateRooter iterState(cx, obj);
 
     /* Get the number of properties to enumerate. */
-    jsval num_properties;
+    jsid num_properties;
     if (!obj->enumerate(cx, JSENUMERATE_INIT, iterState.addr(), &num_properties))
         goto error;
-    if (!JSVAL_IS_INT(num_properties)) {
+    if (!JSID_IS_INT(num_properties)) {
         JS_ASSERT(0);
         goto error;
     }
 
     /* Grow as needed if we don't know the exact amount ahead of time. */
-    n = JSVAL_TO_INT(num_properties);
+    n = JSID_TO_INT(num_properties);
     if (n <= 0)
         n = 8;
 
     /* Create an array of jsids large enough to hold all the properties */
     ida = NewIdArray(cx, n);
     if (!ida)
         goto error;
 
     i = 0;
     vector = &ida->vector[0];
     for (;;) {
         if (!obj->enumerate(cx, JSENUMERATE_NEXT, iterState.addr(), &id))
             goto error;
 
         /* No more jsid's to enumerate ? */
-        if (iterState.state() == JSVAL_NULL)
+        if (iterState.state().isNull())
             break;
 
         if (i == ida->length) {
             ida = SetIdArrayLength(cx, ida, ida->length * 2);
             if (!ida)
                 goto error;
             vector = &ida->vector[0];
         }
@@ -3893,47 +3895,47 @@ JS_STATIC_ASSERT(JSSLOT_ITER_INDEX < JS_
 
 static void
 prop_iter_finalize(JSContext *cx, JSObject *obj)
 {
     void *pdata = obj->getPrivate();
     if (!pdata)
         return;
 
-    if (JSVAL_TO_INT(obj->fslots[JSSLOT_ITER_INDEX]) >= 0) {
+    if (obj->fslots[JSSLOT_ITER_INDEX].asInt32() >= 0) {
         /* Non-native case: destroy the ida enumerated when obj was created. */
         JSIdArray *ida = (JSIdArray *) pdata;
         JS_DestroyIdArray(cx, ida);
     }
 }
 
 static void
 prop_iter_trace(JSTracer *trc, JSObject *obj)
 {
     void *pdata = obj->getPrivate();
     if (!pdata)
         return;
 
-    if (JSVAL_TO_INT(obj->fslots[JSSLOT_ITER_INDEX]) < 0) {
+    if (obj->fslots[JSSLOT_ITER_INDEX].asInt32() < 0) {
         /* Native case: just mark the next property to visit. */
         ((JSScopeProperty *) pdata)->trace(trc);
     } else {
         /* Non-native case: mark each id in the JSIdArray private. */
         JSIdArray *ida = (JSIdArray *) pdata;
         for (jsint i = 0, n = ida->length; i < n; i++)
             js_TraceId(trc, ida->vector[i]);
     }
 }
 
-static JSClass prop_iter_class = {
+static Class prop_iter_class = {
     "PropertyIterator",
     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
     JSCLASS_MARK_IS_TRACE,
-    JS_PropertyStub,  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
-    JS_EnumerateStub, JS_ResolveStub,  JS_ConvertStub,  prop_iter_finalize,
+    PropertyStub,     PropertyStub,    PropertyStub,    PropertyStub,
+    EnumerateStub,    ResolveStub,     ConvertStub,     prop_iter_finalize,
     NULL,             NULL,            NULL,            NULL,
     NULL,             NULL,            JS_CLASS_TRACE(prop_iter_trace), NULL
 };
 
 JS_PUBLIC_API(JSObject *)
 JS_NewPropertyIterator(JSContext *cx, JSObject *obj)
 {
     JSObject *iterobj;
@@ -3954,96 +3956,96 @@ JS_NewPropertyIterator(JSContext *cx, JS
         index = -1;
     } else {
         /*
          * Non-native case: enumerate a JSIdArray and keep it via private.
          *
          * Note: we have to make sure that we root obj around the call to
          * JS_Enumerate to protect against multiple allocations under it.
          */
-        AutoValueRooter tvr(cx, iterobj);
+        AutoObjectRooter tvr(cx, iterobj);
         ida = JS_Enumerate(cx, obj);
         if (!ida)
             return NULL;
         pdata = ida;
         index = ida->length;
     }
 
     /* iterobj cannot escape to other threads here. */
     iterobj->setPrivate(pdata);
-    iterobj->fslots[JSSLOT_ITER_INDEX] = INT_TO_JSVAL(index);
+    iterobj->fslots[JSSLOT_ITER_INDEX].setInt32(index);
     return iterobj;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp)
 {
     jsint i;
     JSObject *obj;
     JSScope *scope;
     JSScopeProperty *sprop;
     JSIdArray *ida;
 
     CHECK_REQUEST(cx);
-    i = JSVAL_TO_INT(iterobj->fslots[JSSLOT_ITER_INDEX]);
+    i = iterobj->fslots[JSSLOT_ITER_INDEX].asInt32();
     if (i < 0) {
         /* Native case: private data is a property tree node pointer. */
         obj = iterobj->getParent();
         JS_ASSERT(obj->isNative());
         scope = obj->scope();
         sprop = (JSScopeProperty *) iterobj->getPrivate();
 
         /*
          * If the next property mapped by scope in the property tree ancestor
          * line is not enumerable, or it's an alias, skip it and keep on trying
          * to find an enumerable property that is still in scope.
          */
         while (sprop && (!sprop->enumerable() || sprop->isAlias()))
             sprop = sprop->parent;
 
         if (!sprop) {
-            *idp = JSVAL_VOID;
+            *idp = JSID_VOID;
         } else {
             iterobj->setPrivate(sprop->parent);
             *idp = sprop->id;
         }
     } 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 = JSVAL_VOID;
+            *idp = JSID_VOID;
         } else {
             *idp = ida->vector[--i];
-            iterobj->setSlot(JSSLOT_ITER_INDEX, INT_TO_JSVAL(i));
+            iterobj->setSlot(JSSLOT_ITER_INDEX, Value(i));
         }
     }
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
                jsval *vp, uintN *attrsp)
 {
     CHECK_REQUEST(cx);
-    return obj->checkAccess(cx, id, mode, vp, attrsp);
+    return obj->checkAccess(cx, id, mode, Valueify(vp), attrsp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp)
 {
     CHECK_REQUEST(cx);
-    return js_GetReservedSlot(cx, obj, index, vp);
+    return js_GetReservedSlot(cx, obj, index, Valueify(vp));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v)
 {
     CHECK_REQUEST(cx);
-    return js_SetReservedSlot(cx, obj, index, v);
+    return js_SetReservedSlot(cx, obj, index, Valueify(v));
 }
 
 #ifdef JS_THREADSAFE
 JS_PUBLIC_API(jsrefcount)
 JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals)
 {
     return JS_ATOMIC_INCREMENT(&principals->refcount);
 }
@@ -4102,17 +4104,17 @@ JS_NewFunction(JSContext *cx, JSNative n
 
     if (!name) {
         atom = NULL;
     } else {
         atom = js_Atomize(cx, name, strlen(name), 0);
         if (!atom)
             return NULL;
     }
-    return js_NewFunction(cx, NULL, native, nargs, flags, parent, atom);
+    return js_NewFunction(cx, NULL, Valueify(native), nargs, flags, parent, atom);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
 {
     CHECK_REQUEST(cx);
     if (!parent) {
         if (cx->fp)
@@ -4122,17 +4124,17 @@ JS_CloneFunctionObject(JSContext *cx, JS
         JS_ASSERT(parent);
     }
 
     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!).
          */
-        jsval v = OBJECT_TO_JSVAL(funobj);
+        Value v(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;
@@ -4152,17 +4154,17 @@ JS_CloneFunctionObject(JSContext *cx, JS
     if (FUN_FLAT_CLOSURE(fun)) {
         JS_ASSERT(funobj->dslots);
         if (!js_EnsureReservedSlots(cx, clone,
                                     fun->countInterpretedReservedSlots())) {
             return NULL;
         }
 
         JSUpvarArray *uva = fun->u.i.script->upvars();
-        JS_ASSERT(uva->length <= size_t(clone->dslots[-1]));
+        JS_ASSERT(uva->length <= clone->dslotLength());
 
         void *mark = JS_ARENA_MARK(&cx->tempPool);
         jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
         if (!names)
             return NULL;
 
         uint32 i = 0, n = uva->length;
         for (; i < n; i++) {
@@ -4226,136 +4228,132 @@ JS_GetFunctionArity(JSFunction *fun)
 JS_PUBLIC_API(JSBool)
 JS_ObjectIsFunction(JSContext *cx, JSObject *obj)
 {
     return obj->getClass() == &js_FunctionClass;
 }
 
 JS_BEGIN_EXTERN_C
 static JSBool
-js_generic_fast_native_method_dispatcher(JSContext *cx, uintN argc, jsval *vp)
-{
-    jsval fsv;
+js_generic_fast_native_method_dispatcher(JSContext *cx, uintN argc, Value *vp)
+{
+    Value fsv;
     JSFunctionSpec *fs;
-    JSObject *tmp;
-    JSFastNative native;
-
-    if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(*vp), 0, &fsv))
+    FastNative native;
+
+    if (!js_GetReservedSlot(cx, &vp->asObject(), 0, &fsv))
         return JS_FALSE;
-    fs = (JSFunctionSpec *) JSVAL_TO_PRIVATE(fsv);
+    fs = (JSFunctionSpec *) fsv.asPrivateVoidPtr();
     JS_ASSERT((~fs->flags & (JSFUN_FAST_NATIVE | JSFUN_GENERIC_NATIVE)) == 0);
 
     /*
      * We know that vp[2] is valid because JS_DefineFunctions, which is our
      * only (indirect) referrer, defined us as requiring at least one argument
      * (notice how it passes fs->nargs + 1 as the next-to-last argument to
      * JS_DefineFunction).
      */
-    if (JSVAL_IS_PRIMITIVE(vp[2])) {
+    if (vp[2].isPrimitive()) {
         /*
          * Make sure that this is an object or null, as required by the generic
          * functions.
          */
-        if (!js_ValueToObject(cx, vp[2], &tmp))
+        if (!js_ValueToObjectOrNull(cx, vp[2], &vp[2]))
             return JS_FALSE;
-        vp[2] = OBJECT_TO_JSVAL(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|.
      */
     memmove(vp + 1, vp + 2, argc * sizeof(jsval));
 
     /*
      * Follow Function.prototype.apply and .call by using the global object as
      * the 'this' param if no args.
      */
-    if (!js_ComputeThis(cx, vp + 2))
+    if (!ComputeThisFromArgv(cx, vp + 2))
         return JS_FALSE;
     /*
      * Protect against argc underflowing. By calling js_ComputeThis, we made
      * it as if the static was called with one parameter, the explicit |this|
      * object.
      */
     if (argc != 0) {
         /* Clear the last parameter in case too few arguments were passed. */
-        vp[2 + --argc] = JSVAL_VOID;
+        vp[2 + --argc].setUndefined();
     }
 
     native =
 #ifdef JS_TRACER
              (fs->flags & JSFUN_TRCINFO)
              ? JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo *, fs->call)->native
              :
 #endif
-               (JSFastNative) fs->call;
+               (FastNative) fs->call;
     return native(cx, argc, vp);
 }
 
 static JSBool
 js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj,
-                                    uintN argc, jsval *argv, jsval *rval)
-{
-    jsval fsv;
+                                    uintN argc, Value *argv, Value *rval)
+{
+    Value fsv;
     JSFunctionSpec *fs;
-    JSObject *tmp;
-
-    if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(argv[-2]), 0, &fsv))
+
+    if (!js_GetReservedSlot(cx, &argv[-2].asObject(), 0, &fsv))
         return JS_FALSE;
-    fs = (JSFunctionSpec *) JSVAL_TO_PRIVATE(fsv);
+    fs = (JSFunctionSpec *) fsv.asPrivateVoidPtr();
     JS_ASSERT((fs->flags & (JSFUN_FAST_NATIVE | JSFUN_GENERIC_NATIVE)) ==
               JSFUN_GENERIC_NATIVE);
 
     /*
      * We know that argv[0] is valid because JS_DefineFunctions, which is our
      * only (indirect) referrer, defined us as requiring at least one argument
      * (notice how it passes fs->nargs + 1 as the next-to-last argument to
      * JS_DefineFunction).
      */
-    if (JSVAL_IS_PRIMITIVE(argv[0])) {
+    if (argv[0].isPrimitive()) {
         /*
          * Make sure that this is an object or null, as required by the generic
          * functions.
          */
-        if (!js_ValueToObject(cx, argv[0], &tmp))
+        if (!js_ValueToObjectOrNull(cx, argv[0], &argv[0]))
             return JS_FALSE;
-        argv[0] = OBJECT_TO_JSVAL(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|.
      */
     memmove(argv - 1, argv, argc * sizeof(jsval));
 
     /*
      * Follow Function.prototype.apply and .call by using the global object as
      * the 'this' param if no args.
      */
-    if (!js_ComputeThis(cx, argv))
+    if (!ComputeThisFromArgv(cx, argv))
         return JS_FALSE;
-    js_GetTopStackFrame(cx)->thisv = argv[-1];
+    js_GetTopStackFrame(cx)->thisv.copy(argv[-1]);
     JS_ASSERT(cx->fp->argv == argv);
 
     /*
      * Protect against argc underflowing. By calling js_ComputeThis, we made
      * it as if the static was called with one parameter, the explicit |this|
      * object.
      */
     if (argc != 0) {
         /* Clear the last parameter in case too few arguments were passed. */
-        argv[--argc] = JSVAL_VOID;
+        argv[--argc].setUndefined();
     }
 
-    return fs->call(cx, JSVAL_TO_OBJECT(argv[-1]), argc, argv, rval);
+    return fs->call(cx, &argv[-1].asObject(), argc, Jsvalify(argv), Jsvalify(rval));
 }
 JS_END_EXTERN_C
 
 JS_PUBLIC_API(JSBool)
 JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs)
 {
     uintN flags;
     JSObject *ctor;
@@ -4375,30 +4373,31 @@ JS_DefineFunctions(JSContext *cx, JSObje
                 ctor = JS_GetConstructor(cx, obj);
                 if (!ctor)
                     return JS_FALSE;
             }
 
             flags &= ~JSFUN_GENERIC_NATIVE;
             fun = JS_DefineFunction(cx, ctor, fs->name,
                                     (flags & JSFUN_FAST_NATIVE)
-                                    ? (JSNative)
-                                      js_generic_fast_native_method_dispatcher
-                                    : js_generic_native_method_dispatcher,
+                                    ? (JSNative) js_generic_fast_native_method_dispatcher
+                                    : Jsvalify(js_generic_native_method_dispatcher),
                                     fs->nargs + 1,
                                     flags & ~JSFUN_TRCINFO);
             if (!fun)
                 return JS_FALSE;
             fun->u.n.extra = (uint16)fs->extra;
 
             /*
              * As jsapi.h notes, fs must point to storage that lives as long
              * as fun->object lives.
              */
-            if (!JS_SetReservedSlot(cx, FUN_OBJECT(fun), 0, PRIVATE_TO_JSVAL(fs)))
+            Value priv;
+            priv.setPrivateVoidPtr(fs);
+            if (!js_SetReservedSlot(cx, FUN_OBJECT(fun), 0, priv))
                 return JS_FALSE;
         }
 
         JS_ASSERT(!(flags & JSFUN_FAST_NATIVE) ||
                   (uint16)(fs->extra >> 16) <= fs->nargs);
         fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, flags);
         if (!fun)
             return JS_FALSE;
@@ -4412,31 +4411,31 @@ JS_DefineFunction(JSContext *cx, JSObjec
                   uintN nargs, uintN attrs)
 {
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     atom = js_Atomize(cx, name, strlen(name), 0);
     if (!atom)
         return NULL;
-    return js_DefineFunction(cx, obj, atom, call, nargs, attrs);
+    return js_DefineFunction(cx, obj, atom, Valueify(call), nargs, attrs);
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS_DefineUCFunction(JSContext *cx, JSObject *obj,
                     const jschar *name, size_t namelen, JSNative call,
                     uintN nargs, uintN attrs)
 {
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0);
     if (!atom)
         return NULL;
-    return js_DefineFunction(cx, obj, atom, call, nargs, attrs);
+    return js_DefineFunction(cx, obj, atom, Valueify(call), nargs, attrs);
 }
 
 JS_PUBLIC_API(JSScript *)
 JS_CompileScript(JSContext *cx, JSObject *obj,
                  const char *bytes, size_t length,
                  const char *filename, uintN lineno)
 {
     jschar *chars;
@@ -4484,17 +4483,17 @@ JS_CompileUCScript(JSContext *cx, JSObje
     JS_BEGIN_MACRO                                                            \
         if (!(result) && !((cx)->options & JSOPTION_DONT_REPORT_UNCAUGHT))    \
             js_ReportUncaughtException(cx);                                   \
     JS_END_MACRO
 
 #define LAST_FRAME_CHECKS(cx,result)                                          \
     JS_BEGIN_MACRO                                                            \
         if (!JS_IsRunning(cx)) {                                              \
-            (cx)->weakRoots.lastInternalResult = JSVAL_NULL;                  \
+            (cx)->weakRoots.lastInternalResult = NULL;                        \
             LAST_FRAME_EXCEPTION_CHECK(cx, result);                           \
         }                                                                     \
     JS_END_MACRO
 
 #define JS_OPTIONS_TO_TCFLAGS(cx)                                             \
     ((((cx)->options & JSOPTION_COMPILE_N_GO) ? TCF_COMPILE_N_GO : 0) |       \
      (((cx)->options & JSOPTION_NO_SCRIPT_RVAL) ? TCF_NO_SCRIPT_RVAL : 0))
 
@@ -4721,17 +4720,17 @@ JS_CompileUCFunctionForPrincipals(JSCont
             goto out2;
         }
     }
     fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom);
     if (!fun)
         goto out2;
 
     {
-        AutoValueRooter tvr(cx, FUN_OBJECT(fun));
+        AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
         MUST_FLOW_THROUGH("out");
 
         for (i = 0; i < nargs; i++) {
             argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0);
             if (!argAtom) {
                 fun = NULL;
                 goto out;
             }
@@ -4743,17 +4742,18 @@ JS_CompileUCFunctionForPrincipals(JSCont
 
         if (!JSCompiler::compileFunctionBody(cx, fun, principals,
                                              chars, length, filename, lineno)) {
             fun = NULL;
             goto out;
         }
 
         if (obj && funAtom &&
-            !obj->defineProperty(cx, ATOM_TO_JSID(funAtom), OBJECT_TO_JSVAL(FUN_OBJECT(fun)),
+            !obj->defineProperty(cx, ATOM_TO_JSID(funAtom),
+                                 FunObjValue(*FUN_OBJECT(fun)),
                                  NULL, NULL, JSPROP_ENUMERATE)) {
             fun = NULL;
         }
 
 #ifdef JS_SCOPE_DEPTH_METER
         if (fun && obj) {
             JSObject *pobj = obj;
             uintN depth = 1;
@@ -4816,17 +4816,17 @@ JS_DecompileFunctionBody(JSContext *cx, 
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval)
 {
     JSBool ok;
 
     CHECK_REQUEST(cx);
-    ok = js_Execute(cx, obj, script, NULL, 0, rval);
+    ok = Execute(cx, obj, script, NULL, 0, Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     return ok;
 }
 
 /* Ancient uintN nbytes is part of API/ABI, so use size_t length local. */
 JS_PUBLIC_API(JSBool)
 JS_EvaluateScript(JSContext *cx, JSObject *obj,
                   const char *bytes, uintN nbytes,
@@ -4894,59 +4894,60 @@ JS_EvaluateUCScriptForPrincipals(JSConte
                                        !rval
                                        ? TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL
                                        : TCF_COMPILE_N_GO,
                                        chars, length, NULL, filename, lineno);
     if (!script) {
         LAST_FRAME_CHECKS(cx, script);
         return JS_FALSE;
     }
-    ok = js_Execute(cx, obj, script, NULL, 0, rval);
+    ok = Execute(cx, obj, script, NULL, 0, Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     JS_DestroyScript(cx, script);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc,
                 jsval *argv, jsval *rval)
 {
     JSBool ok;
 
     CHECK_REQUEST(cx);
-    ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(FUN_OBJECT(fun)), argc, argv,
-                         rval);
+    ok = InternalCall(cx, obj, FunObjValue(*FUN_OBJECT(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);
 
     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()) &&
-                js_InternalCall(cx, obj, tvr.value(), argc, argv, rval);
+                InternalCall(cx, obj, tvr.value(), argc,
+                             Valueify(argv), Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc,
                      jsval *argv, jsval *rval)
 {
     JSBool ok;
 
     CHECK_REQUEST(cx);
-    ok = js_InternalCall(cx, obj, fval, argc, argv, rval);
+    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);
@@ -4954,23 +4955,23 @@ JS_New(JSContext *cx, JSObject *ctor, ui
     // 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;
 
-    jsval *vp = args.getvp();
-    vp[0] = OBJECT_TO_JSVAL(ctor);
-    vp[1] = JSVAL_NULL;
+    Value *vp = args.getvp();
+    SetObject(&vp[0], ctor);
+    vp[1].setNull();
     memcpy(vp + 2, argv, argc * sizeof(jsval));
 
-    JSBool ok = js_InvokeConstructor(cx, args, JS_TRUE);
-    JSObject *obj = ok ? JSVAL_TO_OBJECT(vp[0]) : NULL;
+    JSBool ok = InvokeConstructor(cx, args, JS_TRUE);
+    JSObject *obj = ok ? vp[0].asObjectOrNull() : NULL;
 
     LAST_FRAME_CHECKS(cx, ok);
     return obj;
 }
 
 JS_PUBLIC_API(JSOperationCallback)
 JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback)
 {
@@ -5573,33 +5574,33 @@ JS_IsExceptionPending(JSContext *cx)
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPendingException(JSContext *cx, jsval *vp)
 {
     CHECK_REQUEST(cx);
     if (!cx->throwing)
         return JS_FALSE;
-    *vp = cx->exception;
+    Valueify(vp)->copy(cx->exception);
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(void)
 JS_SetPendingException(JSContext *cx, jsval v)
 {
     CHECK_REQUEST(cx);
     cx->throwing = JS_TRUE;
-    cx->exception = v;
+    cx->exception.copy(Valueify(v));
 }
 
 JS_PUBLIC_API(void)
 JS_ClearPendingException(JSContext *cx)
 {
     cx->throwing = JS_FALSE;
-    cx->exception = JSVAL_VOID;
+    cx->exception.setUndefined();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ReportPendingException(JSContext *cx)
 {
     JSBool ok;
     JSPackedBool save;
 
@@ -5628,17 +5629,17 @@ JS_SaveExceptionState(JSContext *cx)
 {
     JSExceptionState *state;
 
     CHECK_REQUEST(cx);
     state = (JSExceptionState *) cx->malloc(sizeof(JSExceptionState));
     if (state) {
         state->throwing = JS_GetPendingException(cx, &state->exception);
         if (state->throwing && JSVAL_IS_GCTHING(state->exception))
-            js_AddRoot(cx, &state->exception, "JSExceptionState.exception");
+            js_AddRoot(cx, Valueify(&state->exception), "JSExceptionState.exception");
     }
     return state;
 }
 
 JS_PUBLIC_API(void)
 JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state)
 {
     CHECK_REQUEST(cx);
@@ -5652,17 +5653,17 @@ JS_RestoreExceptionState(JSContext *cx, 
 }
 
 JS_PUBLIC_API(void)
 JS_DropExceptionState(JSContext *cx, JSExceptionState *state)
 {
     CHECK_REQUEST(cx);
     if (state) {
         if (state->throwing && JSVAL_IS_GCTHING(state->exception))
-            JS_RemoveRoot(cx, &state->exception);
+            js_RemoveRoot(cx->runtime, &state->exception);
         cx->free(state);
     }
 }
 
 JS_PUBLIC_API(JSErrorReport *)
 JS_ErrorFromException(JSContext *cx, jsval v)
 {
     CHECK_REQUEST(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -46,237 +46,277 @@
 #include <stddef.h>
 #include <stdio.h>
 #include "js-config.h"
 #include "jspubtd.h"
 #include "jsutil.h"
 
 JS_BEGIN_EXTERN_C
 
-/*
- * Type tags stored in the low bits of a jsval.
- */
-typedef enum jsvaltag {
-    JSVAL_OBJECT  =             0x0,     /* untagged reference to object */
-    JSVAL_INT     =             0x1,     /* tagged 31-bit integer value */
-    JSVAL_DOUBLE  =             0x2,     /* tagged reference to double */
-    JSVAL_STRING  =             0x4,     /* tagged reference to string */
-    JSVAL_SPECIAL =             0x6      /* tagged boolean or private value */
-} jsvaltag;
-
-/* Type tag bitfield length and derived macros. */
-#define JSVAL_TAGBITS           3
-#define JSVAL_TAGMASK           ((jsval) JS_BITMASK(JSVAL_TAGBITS))
-#define JSVAL_ALIGN             JS_BIT(JSVAL_TAGBITS)
-
-/* Not a function, because we have static asserts that use it */
-#define JSVAL_TAG(v)            ((jsvaltag)((v) & JSVAL_TAGMASK))
-
-/* Not a function, because we have static asserts that use it */
-#define JSVAL_SETTAG(v, t) ((v) | (t))
-
-static JS_ALWAYS_INLINE jsval
-JSVAL_CLRTAG(jsval v)
-{
-    return v & ~(jsval)JSVAL_TAGMASK;
-}
+/* Well-known JS values, initialized on startup. */
+extern jsval JSVAL_NULL;
+extern jsval JSVAL_ZERO;
+extern jsval JSVAL_ONE;
+extern jsval JSVAL_FALSE;
+extern jsval JSVAL_TRUE;
+extern jsval JSVAL_VOID;
 
 /*
- * Well-known JS values.  The extern'd variables are initialized when the
- * first JSContext is created by JS_NewContext (see below).
+ * Exact value equality, corresponding to == on old word-sized jsvals.
+ *
+ * To have simple byte-equality we would need the keep all sizeof(Data) bytes
+ * of data in a well-defined state for every type. On 32-bit systems, this
+ * isn't true for non-doubles. On 64-bit systems, this isn't true for undefined
+ * and booleans.
+ *
+ * N.B. it is much faster to use specific queries like JSVAL_IS_NULL than
+ * IS_SAME_JSVAL(v, JSVAL_NULL).
  */
-#define JSVAL_NULL              ((jsval) 0)
-#define JSVAL_ZERO              INT_TO_JSVAL(0)
-#define JSVAL_ONE               INT_TO_JSVAL(1)
-#define JSVAL_FALSE             SPECIAL_TO_JSVAL(JS_FALSE)
-#define JSVAL_TRUE              SPECIAL_TO_JSVAL(JS_TRUE)
-#define JSVAL_VOID              SPECIAL_TO_JSVAL(2)
-
-/*
- * A "special" value is a 29-bit (for 32-bit jsval) or 61-bit (for 64-bit jsval)
- * value whose tag is JSVAL_SPECIAL.  These values include the booleans 0 and 1.
- *
- * JSVAL_VOID is a non-boolean special value, but embedders MUST NOT rely on
- * this. All other possible special values are implementation-reserved
- * and MUST NOT be constructed by any embedding of SpiderMonkey.
- */
-#define JSVAL_TO_SPECIAL(v) ((JSBool) ((v) >> JSVAL_TAGBITS))
-#define SPECIAL_TO_JSVAL(b)                                                   \
-    JSVAL_SETTAG((jsval) (b) << JSVAL_TAGBITS, JSVAL_SPECIAL)
+inline JSBool
+IS_SAME_JSVAL(const jsval *lval, const jsval *rval)
+{
+    JSValueMaskType lmask = lval->mask, rmask = rval->mask;
+    jsval_data ldata = lval->data, rdata = rval->data;
+
+    JSBool sameType = lmask == rmask;
+    JSBool noPayload = (lmask & (JSVAL_UNDEFINED_MASK |
+                                 JSVAL_MAGIC_MASK)) != 0;
+    JSBool firstWordEqual = ldata.bits.first == rdata.bits.first;
+    JSBool secondWordEqual = ldata.bits.second == rdata.bits.second;
+
+    /* N.B. JSVAL_NULL_MASK == 0. */
+#if JS_BITS_PER_WORD == 32
+    JSBool noSecondWord = (lmask == 0) |
+                          ((lmask & (JSVAL_INT32_MASK |
+                                     JSVAL_BOOLEAN_MASK |
+                                     JSVAL_STRING_MASK |
+                                     JSVAL_NONFUNOBJ_MASK |
+                                     JSVAL_FUNOBJ_MASK)) != 0);
+#elif JS_BITS_PER_WORD == 64
+    JSBool noSecondWord = (lmask & JSVAL_INT32_MASK |
+                                   JSVAL_BOOLEAN_MASK)
+#else
+# error "Unsupported word size"
+#endif
+
+    return sameType &
+           (noPayload | (firstWordEqual & (secondWordEqual | noSecondWord)));
+}
 
 /* Predicates for type testing. */
+
 static JS_ALWAYS_INLINE JSBool
-JSVAL_IS_OBJECT(jsval v)
+JSVAL_IS_NULL(jsval v)
 {
-    return JSVAL_TAG(v) == JSVAL_OBJECT;
+    return v.mask == JSVAL_NULL_MASK;
+}
+
+static JS_ALWAYS_INLINE JSBool
+JSVAL_IS_VOID(jsval v)
+{
+    return v.mask == JSVAL_UNDEFINED_MASK;
 }
 
 static JS_ALWAYS_INLINE JSBool
 JSVAL_IS_INT(jsval v)
 {
-    return v & JSVAL_INT;
+    return v.mask == JSVAL_INT32_MASK;
+}
+
+static JS_ALWAYS_INLINE jsint
+JSVAL_TO_INT(jsval v)
+{
+    JS_ASSERT(JSVAL_IS_INT(v));
+    return v.data.i32;
+}
+
+#define JSVAL_INT_BITS          32
+#define JSVAL_INT_MIN           ((jsint)0x80000000)
+#define JSVAL_INT_MAX           ((jsint)0x7fffffff)
+
+static JS_ALWAYS_INLINE bool
+INT_FITS_IN_JSVAL(jsint i)
+{
+    return JS_TRUE;
+}
+
+static JS_ALWAYS_INLINE jsval
+INT_TO_JSVAL(int32 i)
+{
+    jsval v;
+    v.mask = JSVAL_INT32_MASK;
+    v.data.i32 = i;
+    return v;
 }
 
 static JS_ALWAYS_INLINE JSBool
 JSVAL_IS_DOUBLE(jsval v)
 {
-    return JSVAL_TAG(v) == JSVAL_DOUBLE;
+    return v.mask == JSVAL_DOUBLE_MASK;
+}
+
+static JS_ALWAYS_INLINE jsdouble
+JSVAL_TO_DOUBLE(jsval v)
+{
+    JS_ASSERT(JSVAL_IS_DOUBLE(v));
+    return v.data.dbl;
+}
+
+static JS_ALWAYS_INLINE jsval
+DOUBLE_TO_JSVAL(jsdouble d)
+{
+    jsval v;
+    v.mask = JSVAL_DOUBLE_MASK;
+    v.data.dbl = d;
+    return v;
 }
 
 static JS_ALWAYS_INLINE JSBool
 JSVAL_IS_NUMBER(jsval v)
 {
-    return JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v);
+    return v.mask & (JSVAL_INT32_MASK | JSVAL_DOUBLE_MASK);
 }
 
 static JS_ALWAYS_INLINE JSBool
 JSVAL_IS_STRING(jsval v)
 {
-    return JSVAL_TAG(v) == JSVAL_STRING;
+    return v.mask == JSVAL_STRING_MASK;
+}
+
+static JS_ALWAYS_INLINE JSString *
+JSVAL_TO_STRING(jsval v)
+{
+    JS_ASSERT(JSVAL_IS_STRING(v));
+    return v.data.str;
 }
 
+static JS_ALWAYS_INLINE jsval
+STRING_TO_JSVAL(JSString *str)
+{
+    jsval v;
+    v.mask = JSVAL_STRING_MASK;
+    v.data.str = str;
+    return v;
+}
+
+/*
+ * N.B. These functions use the older meaning of "object" as "object or null".
+ * This is not a problem if code only works with jsvals or only works with
+ * js::Value, but if both uses are mixed, it is important not to get confused
+ * by the two meanings. For example, JSVAL_IS_OBJECT(v) does not imply
+ * v.isObject().
+ */
 static JS_ALWAYS_INLINE JSBool
-JSVAL_IS_SPECIAL(jsval v)
+JSVAL_IS_OBJECT(jsval v)
+{
+    return !(v.mask & ~(JSVAL_FUNOBJ_MASK | JSVAL_NONFUNOBJ_MASK));
+}
+
+static JS_ALWAYS_INLINE JSObject *
+JSVAL_TO_OBJECT(jsval v)
 {
-    return JSVAL_TAG(v) == JSVAL_SPECIAL;
+    JS_ASSERT(JSVAL_IS_OBJECT(v));
+    return v.data.obj;
+}
+
+static JS_ALWAYS_INLINE jsval
+OBJECT_TO_JSVAL(JSObject *obj)
+{
+    extern JS_PUBLIC_API(JSBool)
+    JS_ObjectIsFunction(JSContext *cx, JSObject *obj);
+
+    jsval v;
+    if (!obj)
+        v.mask = JSVAL_NULL_MASK;
+    else if (JS_ObjectIsFunction(NULL, obj))
+        v.mask = JSVAL_FUNOBJ_MASK;
+    else
+        v.mask = JSVAL_NONFUNOBJ_MASK;
+    v.data.obj = obj;
+    return v;
 }
 
 static JS_ALWAYS_INLINE JSBool
 JSVAL_IS_BOOLEAN(jsval v)
 {
-    return (v & ~((jsval)1 << JSVAL_TAGBITS)) == JSVAL_SPECIAL;
+    return v.mask == JSVAL_BOOLEAN_MASK;
 }
 
 static JS_ALWAYS_INLINE JSBool
-JSVAL_IS_NULL(jsval v)
+JSVAL_TO_BOOLEAN(jsval v)
 {
-    return v == JSVAL_NULL;
+    JS_ASSERT(JSVAL_IS_BOOLEAN(v));
+    return v.data.boo;
+}
+
+static JS_ALWAYS_INLINE jsval
+BOOLEAN_TO_JSVAL(JSBool b)
+{
+    jsval v;
+    v.data.boo = b;
+    v.mask = JSVAL_BOOLEAN_MASK;
+    return v;
 }
 
 static JS_ALWAYS_INLINE JSBool
-JSVAL_IS_VOID(jsval v)
+JSVAL_IS_TRUE(jsval v)
 {
-    return v == JSVAL_VOID;
+    return v.mask == JSVAL_BOOLEAN_MASK && v.data.boo == JS_TRUE;
+}
+
+static JS_ALWAYS_INLINE JSBool
+JSVAL_IS_FALSE(jsval v)
+{
+    return v.mask == JSVAL_BOOLEAN_MASK && v.data.boo == JS_FALSE;
 }
 
 static JS_ALWAYS_INLINE JSBool
 JSVAL_IS_PRIMITIVE(jsval v)
 {
-    return !JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v);
+    return !(v.mask & (JSVAL_NONFUNOBJ_MASK | JSVAL_FUNOBJ_MASK));
 }
 
-/* Objects, strings, and doubles are GC'ed. */
 static JS_ALWAYS_INLINE JSBool
 JSVAL_IS_GCTHING(jsval v)
 {
-    return !(v & JSVAL_INT) && JSVAL_TAG(v) != JSVAL_SPECIAL;
+    return v.mask & (JSVAL_STRING_MASK | JSVAL_NONFUNOBJ_MASK |
+                     JSVAL_FUNOBJ_MASK);
 }
 
 static JS_ALWAYS_INLINE void *
 JSVAL_TO_GCTHING(jsval v)
 {
     JS_ASSERT(JSVAL_IS_GCTHING(v));
-    return (void *) JSVAL_CLRTAG(v);
-}
-
-static JS_ALWAYS_INLINE JSObject *
-JSVAL_TO_OBJECT(jsval v)
-{
-    JS_ASSERT(JSVAL_IS_OBJECT(v));
-    return (JSObject *) JSVAL_TO_GCTHING(v);
-}
-
-static JS_ALWAYS_INLINE jsdouble *
-JSVAL_TO_DOUBLE(jsval v)
-{
-    JS_ASSERT(JSVAL_IS_DOUBLE(v));
-    return (jsdouble *) JSVAL_TO_GCTHING(v);
-}
-
-static JS_ALWAYS_INLINE JSString *
-JSVAL_TO_STRING(jsval v)
-{
-    JS_ASSERT(JSVAL_IS_STRING(v));
-    return (JSString *) JSVAL_TO_GCTHING(v);
+    return v.data.ptr;
 }
 
 static JS_ALWAYS_INLINE jsval
-OBJECT_TO_JSVAL(JSObject *obj)
-{
-    JS_ASSERT(((jsval) obj & JSVAL_TAGMASK) == JSVAL_OBJECT);
-    return (jsval) obj;
-}
-
-static JS_ALWAYS_INLINE jsval
-DOUBLE_TO_JSVAL(jsdouble *dp)
+PRIVATE_TO_JSVAL(void *ptr)
 {
-    JS_ASSERT(((jsword) dp & JSVAL_TAGMASK) == 0);
-    return JSVAL_SETTAG((jsval) dp, JSVAL_DOUBLE);
+    jsval v;
+    v.mask = JSVAL_INT32_MASK;
+    v.data.ptr = ptr;
+    return v;
 }
 
-static JS_ALWAYS_INLINE jsval
-STRING_TO_JSVAL(JSString *str)
+static JS_ALWAYS_INLINE void *
+JSVAL_TO_PRIVATE(jsval v)
 {
-    return JSVAL_SETTAG((jsval) str, JSVAL_STRING);
+    JS_ASSERT(v.mask == JSVAL_INT32_MASK);
+    return v.data.ptr;
 }
 
 /* Lock and unlock the GC thing held by a jsval. */
 #define JSVAL_LOCK(cx,v)        (JSVAL_IS_GCTHING(v)                          \
                                  ? JS_LockGCThing(cx, JSVAL_TO_GCTHING(v))    \
                                  : JS_TRUE)
 #define JSVAL_UNLOCK(cx,v)      (JSVAL_IS_GCTHING(v)                          \
                                  ? JS_UnlockGCThing(cx, JSVAL_TO_GCTHING(v))  \
                                  : JS_TRUE)
 
-/* Domain limits for the jsval int type. */
-#define JSVAL_INT_BITS          31
-#define JSVAL_INT_POW2(n)       ((jsval)1 << (n))
-#define JSVAL_INT_MIN           (-JSVAL_INT_POW2(30))
-#define JSVAL_INT_MAX           (JSVAL_INT_POW2(30) - 1)
-
-/* Not a function, because we have static asserts that use it */
-#define INT_FITS_IN_JSVAL(i)    ((jsuint)(i) - (jsuint)JSVAL_INT_MIN <=       \
-                                 (jsuint)(JSVAL_INT_MAX - JSVAL_INT_MIN))
-
-static JS_ALWAYS_INLINE jsint
-JSVAL_TO_INT(jsval v)
-{
-    JS_ASSERT(JSVAL_IS_INT(v));
-    return (jsint) v >> 1;
-}
-
-/* Not a function, because we have static asserts that use it */
-#define INT_TO_JSVAL_CONSTEXPR(i)  (((jsval)(i) << 1) | JSVAL_INT)
-
-static JS_ALWAYS_INLINE jsval
-INT_TO_JSVAL(jsint i)
-{
-    JS_ASSERT(INT_FITS_IN_JSVAL(i));
-    return INT_TO_JSVAL_CONSTEXPR(i);
-}
-
-/* Convert between boolean and jsval, asserting that inputs are valid. */
-static JS_ALWAYS_INLINE JSBool
-JSVAL_TO_BOOLEAN(jsval v)
-{
-    JS_ASSERT(v == JSVAL_TRUE || v == JSVAL_FALSE);
-    return JSVAL_TO_SPECIAL(v);
-}
-
-static JS_ALWAYS_INLINE jsval
-BOOLEAN_TO_JSVAL(JSBool b)
-{
-    JS_ASSERT(b == JS_TRUE || b == JS_FALSE);
-    return SPECIAL_TO_JSVAL(b);
-}
-
-/* A private data pointer (2-byte-aligned) can be stored as an int jsval. */
-#define JSVAL_TO_PRIVATE(v)     ((void *)((v) & ~JSVAL_INT))
-#define PRIVATE_TO_JSVAL(p)     ((jsval)(p) | JSVAL_INT)
-
 /* Property attributes, set in JSPropertySpec and passed to API functions. */
 #define JSPROP_ENUMERATE        0x01    /* property is visible to for/in loop */
 #define JSPROP_READONLY         0x02    /* not settable: assignment is no-op */
 #define JSPROP_PERMANENT        0x04    /* property cannot be deleted */
 #define JSPROP_GETTER           0x10    /* property holds getter function */
 #define JSPROP_SETTER           0x20    /* property holds setter function */
 #define JSPROP_SHARED           0x40    /* don't allocate a value slot for this
                                            property; don't copy the property on
@@ -793,17 +833,17 @@ JS_InitStandardClasses(JSContext *cx, JS
  * as usual for JSBool result-typed API entry points.
  *
  * This API can be called directly from a global object class's resolve op,
  * to define standard classes lazily.  The class's enumerate op should call
  * JS_EnumerateStandardClasses(cx, obj), to define eagerly during for..in
  * loops any classes not yet resolved lazily.
  */
 extern JS_PUBLIC_API(JSBool)
-JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id,
+JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsid id,
                         JSBool *resolved);
 
 extern JS_PUBLIC_API(JSBool)
 JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj);
 
 /*
  * Enumerate any already-resolved standard class ids into ida, or into a new
  * JSIdArray if ida is null.  Return the augmented array on success, null on
@@ -855,17 +895,17 @@ JS_InitCTypesClass(JSContext *cx, JSObje
  *
  * WARNING: These are not (yet) mandatory macros, but new code outside of the
  * engine should use them. In the Mozilla 2.0 milestone their definitions may
  * change incompatibly.
  */
 #define JS_CALLEE(cx,vp)        ((vp)[0])
 #define JS_ARGV_CALLEE(argv)    ((argv)[-2])
 #define JS_THIS(cx,vp)          JS_ComputeThis(cx, vp)
-#define JS_THIS_OBJECT(cx,vp)   ((JSObject *) JS_THIS(cx,vp))
+#define JS_THIS_OBJECT(cx,vp)   (JSVAL_TO_OBJECT(JS_THIS(cx,vp)))
 #define JS_ARGV(cx,vp)          ((vp) + 2)
 #define JS_RVAL(cx,vp)          (*(vp))
 #define JS_SET_RVAL(cx,vp,v)    (*(vp) = (v))
 
 extern JS_PUBLIC_API(jsval)
 JS_ComputeThis(JSContext *cx, jsval *vp);
 
 extern JS_PUBLIC_API(void *)
@@ -878,61 +918,92 @@ extern JS_PUBLIC_API(void)
 JS_free(JSContext *cx, void *p);
 
 extern JS_PUBLIC_API(void)
 JS_updateMallocCounter(JSContext *cx, size_t nbytes);
 
 extern JS_PUBLIC_API(char *)
 JS_strdup(JSContext *cx, const char *s);
 
-extern JS_PUBLIC_API(jsdouble *)
-JS_NewDouble(JSContext *cx, jsdouble d);
-
-extern JS_PUBLIC_API(JSBool)
-JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval);
-
-extern JS_PUBLIC_API(JSBool)
-JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval);
-
 /*
- * A JS GC root is a pointer to a JSObject *, JSString *, or jsdouble * that
- * itself points into the GC heap (more recently, we support this extension:
- * a root may be a pointer to a jsval v for which JSVAL_IS_GCTHING(v) is true).
+ * A GC root is a pointer to a jsval, JSObject *, or JSString * that itself
+ * points into the GC heap. JS_AddValueRoot takes pointers to jsvals and
+ * JS_AddGCThingRoot takes pointers to a JSObject * or JString *.
+ *
+ * Note that, since JS_Add*Root stores the address of a (jsval, JSString *, or
+ * JSObject *) variable, that variable must be alive until JS_RemoveRoot is
+ * called to remove that variable. For example, after writing:
  *
- * Therefore, you never pass JSObject *obj to JS_AddRoot(cx, obj).  You always
- * call JS_AddRoot(cx, &obj), passing obj by reference.  And later, before obj
- * or the structure it is embedded within goes out of scope or is freed, you
- * must call JS_RemoveRoot(cx, &obj).
+ *   jsval v;
+ *   JS_AddNamedRootedValue(cx, &v, "name");
+ *
+ * the caller must perform
+ *
+ *   JS_RemoveRootedValue(cx, &v);
+ *
+ * before 'v' goes out of scope.
  *
  * Also, use JS_AddNamedRoot(cx, &structPtr->memberObj, "structPtr->memberObj")
  * in preference to JS_AddRoot(cx, &structPtr->memberObj), in order to identify
  * roots by their source callsites.  This way, you can find the callsite while
  * debugging if you should fail to do JS_RemoveRoot(cx, &structPtr->memberObj)
  * before freeing structPtr's memory.
  */
 extern JS_PUBLIC_API(JSBool)
-JS_AddRoot(JSContext *cx, void *rp);
+JS_AddValueRoot(JSContext *cx, js::Value *vp);
+
+extern JS_PUBLIC_API(JSBool)
+JS_AddStringRoot(JSContext *cx, JSString **rp);
+
+extern JS_PUBLIC_API(JSBool)
+JS_AddObjectRoot(JSContext *cx, JSObject **rp);
 
 #ifdef NAME_ALL_GC_ROOTS
 #define JS_DEFINE_TO_TOKEN(def) #def
 #define JS_DEFINE_TO_STRING(def) JS_DEFINE_TO_TOKEN(def)
-#define JS_AddRoot(cx,rp) JS_AddNamedRoot((cx), (rp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__))
+#define JS_AddValueRoot(cx,vp) JS_AddNamedValueRoot((cx), (vp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__))
+#define JS_AddStringRoot(cx,rp) JS_AddNamedStringRoot((cx), (rp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__))
+#define JS_AddObjectRoot(cx,rp) JS_AddNamedObjectRoot((cx), (rp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__))
 #endif
 
 extern JS_PUBLIC_API(JSBool)
-JS_AddNamedRoot(JSContext *cx, void *rp, const char *name);
+JS_AddNamedValueRoot(JSContext *cx, js::Value *vp, const char *name);
+
+extern JS_PUBLIC_API(JSBool)
+JS_AddNamedStringRoot(JSContext *cx, JSString **rp, const char *name);
+
+extern JS_PUBLIC_API(JSBool)
+JS_AddNamedObjectRoot(JSContext *cx, JSObject **rp, const char *name);
+
+extern JS_PUBLIC_API(JSBool)
+JS_AddNamedValueRootRT(JSRuntime *rt, js::Value *vp, const char *name);
+
+extern JS_PUBLIC_API(JSBool)
+JS_AddNamedStringRootRT(JSRuntime *rt, JSString **rp, const char *name);
 
 extern JS_PUBLIC_API(JSBool)
-JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name);
+JS_AddNamedObjectRootRT(JSRuntime *rt, JSObject **rp, const char *name);
+
+extern JS_PUBLIC_API(JSBool)
+JS_RemoveValueRoot(JSContext *cx, js::Value *vp);
+
+extern JS_PUBLIC_API(JSBool)
+JS_RemoveStringRoot(JSContext *cx, JSString **rp);
 
 extern JS_PUBLIC_API(JSBool)
-JS_RemoveRoot(JSContext *cx, void *rp);
+JS_RemoveObjectRoot(JSContext *cx, JSObject **rp);
 
 extern JS_PUBLIC_API(JSBool)
-JS_RemoveRootRT(JSRuntime *rt, void *rp);
+JS_RemoveValueRootRT(JSRuntime *rt, js::Value *vp);
+
+extern JS_PUBLIC_API(JSBool)
+JS_RemoveStringRootRT(JSRuntime *rt, JSString **rp);
+
+extern JS_PUBLIC_API(JSBool)
+JS_RemoveObjectRootRT(JSRuntime *rt, JSObject **rp);
 
 /*
  * The last GC thing of each type (object, string, double, external string
  * types) created on a given context is kept alive until another thing of the
  * same type is created, using a newborn root in the context.  These newborn
  * roots help native code protect newly-created GC-things from GC invocations
  * activated before those things can be rooted using local or global roots.
  *
@@ -958,17 +1029,17 @@ JS_ClearNewbornRoots(JSContext *cx);
  * and JS_RemoveRoot to manage global roots temporarily.
  *
  * Instead, calling JS_EnterLocalRootScope and JS_LeaveLocalRootScope around
  * the body of the native hook causes the engine to allocate a local root for
  * each newborn created in between the two API calls, using a local root stack
  * associated with cx.  For example:
  *
  *    JSBool
- *    my_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+ *    my_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
  *    {
  *        JSBool ok;
  *
  *        if (!JS_EnterLocalRootScope(cx))
  *            return JS_FALSE;
  *        ok = my_GetPropertyBody(cx, obj, id, vp);
  *        JS_LeaveLocalRootScope(cx);
  *        return ok;
@@ -1035,20 +1106,25 @@ class JSAutoLocalRootScope {
     static void *operator new(size_t) CPP_THROW_NEW { return 0; };
     static void operator delete(void *, size_t) { };
 #endif
 };
 
 JS_BEGIN_EXTERN_C
 #endif
 
+typedef enum JSGCRootType {
+    JS_GC_ROOT_VALUE_PTR,
+    JS_GC_ROOT_GCTHING_PTR
+} JSGCRootType;
+
 #ifdef DEBUG
 extern JS_PUBLIC_API(void)
 JS_DumpNamedRoots(JSRuntime *rt,
-                  void (*dump)(const char *name, void *rp, void *data),
+                  void (*dump)(const char *name, void *rp, JSGCRootType type, void *data),
                   void *data);
 #endif
 
 /*
  * Call JS_MapGCRoots to map the GC's roots table using map(rp, name, data).
  * The root is pointed at by rp; if the root is unnamed, name is null; data is
  * supplied from the third parameter to JS_MapGCRoots.
  *
@@ -1058,24 +1134,28 @@ JS_DumpNamedRoots(JSRuntime *rt,
  * constants are flags; you can OR them together.
  *
  * This function acquires and releases rt's GC lock around the mapping of the
  * roots table, so the map function should run to completion in as few cycles
  * as possible.  Of course, map cannot call JS_GC, JS_MaybeGC, JS_BeginRequest,
  * or any JS API entry point that acquires locks, without double-tripping or
  * deadlocking on the GC lock.
  *
+ * The JSGCRootType parameter indicates whether rp is a pointer to a Value
+ * (which is obtained by '(Value *)rp') or a pointer to a GC-thing pointer
+ * (which is obtained by '(void **)rp').
+ *
  * JS_MapGCRoots returns the count of roots that were successfully mapped.
  */
 #define JS_MAP_GCROOT_NEXT      0       /* continue mapping entries */
 #define JS_MAP_GCROOT_STOP      1       /* stop mapping entries */
 #define JS_MAP_GCROOT_REMOVE    2       /* remove and free the current entry */
 
 typedef intN
-(* JSGCRootMapFun)(void *rp, const char *name, void *data);
+(* JSGCRootMapFun)(void *rp, JSGCRootType type, const char *name, void *data);
 
 extern JS_PUBLIC_API(uint32)
 JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data);
 
 extern JS_PUBLIC_API(JSBool)
 JS_LockGCThing(JSContext *cx, void *thing);
 
 extern JS_PUBLIC_API(JSBool)
@@ -1097,42 +1177,58 @@ JS_UnlockGCThingRT(JSRuntime *rt, void *
 extern JS_PUBLIC_API(void)
 JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
 
 /*
  * For implementors of JSMarkOp. All new code should implement JSTraceOp
  * instead.
  */
 extern JS_PUBLIC_API(void)
-JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg);
+JS_MarkGCThing(JSContext *cx, jsval v, const char *name, void *arg);
 
 /*
  * JS_CallTracer API and related macros for implementors of JSTraceOp, to
  * enumerate all references to traceable things reachable via a property or
  * other strong ref identified for debugging purposes by name or index or
  * a naming callback.
  *
  * By definition references to traceable things include non-null pointers
  * to JSObject, JSString and jsdouble and corresponding jsvals.
  *
  * See the JSTraceOp typedef in jspubtd.h.
  */
 
 /* Trace kinds to pass to JS_Tracing. */
 #define JSTRACE_OBJECT  0
-#define JSTRACE_DOUBLE  1
-#define JSTRACE_STRING  2
+#define JSTRACE_STRING  1
 
 /*
  * Use the following macros to check if a particular jsval is a traceable
  * thing and to extract the thing and its kind to pass to JS_CallTracer.
  */
-#define JSVAL_IS_TRACEABLE(v)   (JSVAL_IS_GCTHING(v) && !JSVAL_IS_NULL(v))
-#define JSVAL_TO_TRACEABLE(v)   (JSVAL_TO_GCTHING(v))
-#define JSVAL_TRACE_KIND(v)     (JSVAL_TAG(v) >> 1)
+static JS_ALWAYS_INLINE JSBool
+JSVAL_IS_TRACEABLE(jsval v)
+{
+    /* Depend on the value representation */
+    return v.mask & (JSVAL_STRING_MASK | JSVAL_NONFUNOBJ_MASK |
+                     JSVAL_FUNOBJ_MASK);
+}
+
+static JS_ALWAYS_INLINE void *
+JSVAL_TO_TRACEABLE(jsval v)
+{
+    return JSVAL_TO_GCTHING(v);
+}
+
+static JS_ALWAYS_INLINE uint32
+JSVAL_TRACE_KIND(jsval v)
+{
+    /* Depend on the value representation */
+    return (uint32)(v.mask == JSVAL_STRING_MASK);
+}
 
 struct JSTracer {
     JSContext           *context;
     JSTraceCallback     callback;
     JSTraceNamePrinter  debugPrinter;
     const void          *debugPrintArg;
     size_t              debugPrintIndex;
 };
@@ -1376,17 +1472,17 @@ JS_AddExternalStringFinalizer(JSStringFi
  * before calling this function.  Otherwise, string data will be leaked by the
  * GC, for want of a finalizer to call.
  */
 extern JS_PUBLIC_API(intN)
 JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer);
 
 /*
  * Create a new JSString whose chars member refers to external memory, i.e.,
- * memory requiring special, type-specific finalization.  The type code must
+ * memory requiring spe, type-specific finalization.  The type code must
  * be a nonnegative return value from JS_AddExternalStringFinalizer.
  */
 extern JS_PUBLIC_API(JSString *)
 JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type);
 
 /*
  * Returns the external-string finalizer index for this string, or -1 if it is
  * an "internal" (native to JS engine) string.
@@ -1557,30 +1653,21 @@ JS_IdToValue(JSContext *cx, jsid id, jsv
  */
 #define JSRESOLVE_QUALIFIED     0x01    /* resolve a qualified property id */
 #define JSRESOLVE_ASSIGNING     0x02    /* resolve on the left of assignment */
 #define JSRESOLVE_DETECTING     0x04    /* 'if (o.p)...' or '(o.p) ?...:...' */
 #define JSRESOLVE_DECLARING     0x08    /* var, const, or function prolog op */
 #define JSRESOLVE_CLASSNAME     0x10    /* class name used when constructing */
 #define JSRESOLVE_WITH          0x20    /* resolve inside a with statement */
 
-extern JS_PUBLIC_API(JSBool)
-JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
-
-extern JS_PUBLIC_API(JSBool)
-JS_EnumerateStub(JSContext *cx, JSObject *obj);
-
-extern JS_PUBLIC_API(JSBool)
-JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id);
-
-extern JS_PUBLIC_API(JSBool)
-JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
-
-extern JS_PUBLIC_API(void)
-JS_FinalizeStub(JSContext *cx, JSObject *obj);
+extern JS_PUBLIC_DATA(JSPropertyOp)  JS_PropertyStub;
+extern JS_PUBLIC_DATA(JSEnumerateOp) JS_EnumerateStub;
+extern JS_PUBLIC_DATA(JSResolveOp)   JS_ResolveStub;
+extern JS_PUBLIC_DATA(JSConvertOp)   JS_ConvertStub;
+extern JS_PUBLIC_DATA(JSFinalizeOp)  JS_FinalizeStub;
 
 struct JSConstDoubleSpec {
     jsdouble        dval;
     const char      *name;
     uint8           flags;
     uint8           spare[3];
 };
 
@@ -2847,9 +2934,738 @@ JS_ClearContextThread(JSContext *cx);
 
 #ifdef JS_GC_ZEAL
 extern JS_PUBLIC_API(void)
 JS_SetGCZeal(JSContext *cx, uint8 zeal);
 #endif
 
 JS_END_EXTERN_C
 
+/************************************************************************/
+
+#ifdef __cplusplus
+
+/*
+ * Spanky new C++ API
+ */
+
+namespace js {
+
+/*
+ * 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
+ * not need the extra 2 words needed by Value.
+ */
+class ObjPtr
+{
+  protected:
+    typedef JSValueMaskType MaskType;
+
+    static const MaskType NullMask         = JSVAL_NULL_MASK;
+    static const MaskType NonFunObjMask    = JSVAL_NONFUNOBJ_MASK;
+    static const MaskType FunObjMask       = JSVAL_FUNOBJ_MASK;
+
+    static const MaskType ObjectMask       = NonFunObjMask | FunObjMask;
+
+    MaskType mask;
+    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.
+     */
+
+    /* Accessors */
+
+    bool isNull() const {
+        return mask == NullMask;
+    }
+
+    bool isFunObj() const {
+        return mask == FunObjMask;
+    }
+
+    JSObject &asFunObj() const {
+        JS_ASSERT(isFunObj());
+        return *obj;
+    }
+
+    bool isNonFunObj() const {
+        return mask == NonFunObjMask;
+    }
+
+    JSObject &asNonFunObj() const {
+        JS_ASSERT(isNonFunObj());
+        return *obj;
+    }
+
+    bool isObject() const {
+        JS_ASSERT((mask != NullMask) == bool(mask & ObjectMask));
+        return mask != NullMask;
+    }
+
+    operator JSObject *() const {
+        return obj;
+    }
+
+    JSObject *operator->() const {
+        JS_ASSERT(isObject());
+        return obj;
+    }
+
+    JSObject &operator*() const {
+        JS_ASSERT(isObject());
+        return *obj;
+    }
+
+    /* Assignment */
+
+    void setNull() {
+        mask = NullMask;
+        obj = NULL;
+    }
+
+    void setFunObj(JSObject &o) {
+        JS_ASSERT(JS_ObjectIsFunction(NULL, &o));
+        mask = FunObjMask;
+        obj = &o;
+    }
+
+    void setNonFunObj(JSObject &o) {
+        JS_ASSERT(!JS_ObjectIsFunction(NULL, &o));
+        mask = NonFunObjMask;
+        obj = &o;
+    }
+
+    void setNonFunObjOrNull(JSObject *o) {
+        if (o) {
+            JS_ASSERT(!JS_ObjectIsFunction(NULL, o));
+            mask = NonFunObjMask;
+            obj = o;
+        } else {
+            mask = NullMask;
+            obj = o;
+        }
+    }
+};
+
+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;
+
+/*
+ * 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
+ * may be cast safely and canonically using js::Jsvalify and js::asValue.
+ */
+class Value
+{
+    /*
+     * Generally, we'd like to keep the exact representation encapsulated so
+     * that it may be tweaked in the future. Engine internals that need to
+     * break this encapsulation should be listed as friends below. Also see
+     * uses of public jsval members in jsapi.h/jspubtd.h.
+     */
+    friend bool StrictlyEqual(JSContext *, const Value &, const Value &);
+    friend bool Interpret(JSContext *);  /* grep "value representation" */
+    friend bool PrimitiveThisTest(JSFunction *, const Value &);
+    friend class PrimitiveValue;
+
+  protected:
+    /* Type masks */
+    typedef JSValueMaskType MaskType;
+    static const MaskType NullMask      = JSVAL_NULL_MASK;
+    static const MaskType UndefinedMask = JSVAL_UNDEFINED_MASK;
+    static const MaskType Int32Mask     = JSVAL_INT32_MASK;
+    static const MaskType DoubleMask    = JSVAL_DOUBLE_MASK;
+    static const MaskType StringMask    = JSVAL_STRING_MASK;
+    static const MaskType NonFunObjMask = JSVAL_NONFUNOBJ_MASK;
+    static const MaskType FunObjMask    = JSVAL_FUNOBJ_MASK;
+    static const MaskType BooleanMask   = JSVAL_BOOLEAN_MASK;
+    static const MaskType MagicMask     = JSVAL_MAGIC_MASK;
+
+    /* Standard collections of types. */
+    static const MaskType NumberMask    = Int32Mask | DoubleMask;
+    static const MaskType ObjectMask    = NonFunObjMask | FunObjMask;
+    static const MaskType GCThingMask   = ObjectMask | StringMask;
+
+    static bool isSingleton(MaskType m) { return !(m & ~UndefinedMask); }
+    static bool isObjectOrNull(MaskType m) { return !(m & ~ObjectMask); }
+
+    union Data
+    {
+        int32           i32;
+        uint32          u32;
+        double          dbl;
+        JSString        *str;
+        JSObject        *obj;
+        void            *ptr;
+        JSBool          boo;
+#ifdef DEBUG
+        JSWhyMagic      why;
+#endif
+        struct { int32 first; int32 second; } bits;
+    };
+
+    MaskType mask;
+    JS_INSERT_VALUE_PADDING()
+    Data data;
+
+    friend class AssertLayoutCompatible;
+
+    /*
+     * 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() {}
+
+    /* 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;
+    }
+
+    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;
+    }
+
+    /* Mutators */
+
+    void setNull() {
+        mask = NullMask;
+        data.obj = NULL;
+    }
+
+    void setUndefined() {
+        /* N.B. data is undefined. */
+        mask = UndefinedMask;
+    }
+
+    void setInt32(int32 i) {
+        mask = Int32Mask;
+        data.i32 = i;
+    }
+
+    int32_t &asInt32Ref() {
+        JS_ASSERT(isInt32());
+        return data.i32;
+    }
+
+    void setDouble(double d) {
+        JS_ASSERT(size_t(&data.dbl) % sizeof(double) == 0);
+        mask = DoubleMask;
+        data.dbl = d;
+    }
+
+    double &asDoubleRef() {
+        JS_ASSERT(size_t(&data.dbl) % sizeof(double) == 0);
+        JS_ASSERT(isDouble());
+        return data.dbl;
+    }
+
+    void setString(JSString *str) {
+        mask = StringMask;
+        data.str = str;
+    }
+
+    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;
+    }
+
+    /* XXX: to be removed when copying is implicit/public */
+    void copy(const Value &v) {
+        data = v.data;
+        mask = v.mask;
+    }
+
+    void swap(Value &rhs) {
+        MaskType m = mask;
+        mask = rhs.mask;
+        rhs.mask = m;
+        Data d = data;
+        data = rhs.data;
+        rhs.data = d;
+    }
+
+    /* Accessors */
+
+    bool isUndefined() const {
+        return mask == UndefinedMask;
+    }
+
+    bool isNull() const {
+        return mask == NullMask;
+    }
+
+    bool isNullOrUndefined() const {
+        return isSingleton(mask);
+    }
+
+    bool isInt32() const {
+        return mask == Int32Mask;
+    }
+
+    int32 asInt32() const {
+        JS_ASSERT(isInt32());
+        return data.i32;
+    }
+
+    bool isDouble() const {
+        return mask == DoubleMask;
+    }
+
+    double asDouble() const {
+        JS_ASSERT(size_t(&data.dbl) % sizeof(double) == 0);
+        return data.dbl;
+    }
+
+    bool isNumber() const {
+        return bool(mask & NumberMask);
+    }
+
+    double asNumber() const {
+        JS_ASSERT(isNumber());
+        return isDouble() ? asDouble() : double(asInt32());
+    }
+
+    bool isString() const {
+        return mask == StringMask;
+    }
+
+    JSString *asString() const {
+        JS_ASSERT(isString());
+        return data.str;
+    }
+
+    bool isNonFunObj() const {
+        return mask == NonFunObjMask;
+    }
+
+    JSObject &asNonFunObj() const {
+        JS_ASSERT(isNonFunObj());
+        return *data.obj;
+    }
+
+    bool isFunObj() const {
+        return mask == FunObjMask;
+    }
+
+    JSObject &asFunObj() const {
+        JS_ASSERT(isFunObj());
+        return *data.obj;
+    }
+
+    bool isObject() const {
+        return bool(mask & ObjectMask);
+    }
+
+    bool isPrimitive() const {
+        return !isObject();
+    }
+
+    JSObject &asObject() const {
+        JS_ASSERT(isObject());
+        return *data.obj;
+    }
+
+    bool isObjectOrNull() const {
+        return isObjectOrNull(mask);
+    }
+
+    JSObject *asObjectOrNull() const {
+        JS_ASSERT(isObjectOrNull());
+        return data.obj;
+    }
+
+    ObjPtr asObjPtr() const {
+        JS_ASSERT(isObjectOrNull());
+        return ObjPtr(mask, data.obj);
+    }
+
+    bool isGCThing() const {
+        return bool(mask & GCThingMask);
+    }
+
+    void *asGCThing() const {
+        JS_ASSERT(isGCThing());
+        return data.ptr;
+    }
+
+    int32 traceKind() const {
+        JS_ASSERT(isGCThing());
+        return mask == StringMask;
+    }
+
+    bool isBoolean() const {
+        return mask == BooleanMask;
+    }
+
+    bool asBoolean() const {
+        JS_ASSERT(isBoolean());
+        return data.boo;
+    }
+
+    bool isMagic() const {
+        return mask == MagicMask;
+    }
+
+    bool isMagic(JSWhyMagic why) const {
+        JS_ASSERT_IF(mask == MagicMask, data.why == why);
+        return isMagic();
+    }
+
+    JSWhyMagic whyMagic() const {
+        JS_ASSERT(mask == MagicMask);
+        return data.why;
+    }
+
+    /* See IS_SAME_JSVAL. */
+    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.
+     */
+
+    void setPrivateVoidPtr(void *p) {
+        mask = Int32Mask;
+        data.ptr = p;
+    }
+
+    void *asPrivateVoidPtr() const {
+        JS_ASSERT(mask == Int32Mask);
+        return data.ptr;
+    }
+
+    void setPrivateUint32(uint32 u) {
+        mask = Int32Mask;
+        data.u32 = u;
+    }
+
+    uint32 asPrivateUint32() const {
+        JS_ASSERT(mask == Int32Mask);
+        return data.u32;
+    }
+
+    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));
+};
+
+/*
+ * 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; }
+static JS_ALWAYS_INLINE Value *       Valueify(jsval *v)       { return (Value *)v; }
+static JS_ALWAYS_INLINE const Value * Valueify(const jsval *v) { return (const Value *)v; }
+static JS_ALWAYS_INLINE Value &       Valueify(jsval &v)       { return (Value &)v; }
+static JS_ALWAYS_INLINE const Value & Valueify(const jsval &v) { return (const Value &)v; }
+
+/*
+ * N.B. It is more efficient to use setNull/isNull/setUndefined/isUndefined
+ * than to copy/compare these global values.
+ */
+extern const Value sNullValue;
+extern const Value sUndefinedValue;
+extern const ObjPtr sNullObjPtr;
+
+/*
+ * js::Class is layout compatible and thus 
+ */
+struct Class {
+    const char          *name;
+    uint32              flags;
+
+    /* Mandatory non-null function pointer members. */
+    PropertyOp          addProperty;
+    PropertyOp          delProperty;
+    PropertyOp          getProperty;
+    PropertyOp          setProperty;
+    JSEnumerateOp       enumerate;
+    JSResolveOp         resolve;
+    ConvertOp           convert;
+    JSFinalizeOp        finalize;
+
+    /* Optionally non-null members start here. */
+    GetObjectOps        getObjectOps;
+    CheckAccessOp       checkAccess;
+    Native              call;
+    Native              construct;
+    JSXDRObjectOp       xdrObject;
+    HasInstanceOp       hasInstance;
+    JSMarkOp            mark;
+    JSReserveSlotsOp    reserveSlots;
+};
+
+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(sizeof(JSExtendedClass) == sizeof(ExtendedClass));
+
+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; }
+
+} /* namespace js */
+#endif  /* __cplusplus */
+
 #endif /* jsapi_h___ */
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -33,32 +33,34 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#define __STDC_LIMIT_MACROS
+
 /*
  * JS array class.
  *
  * Array objects begin as "dense" arrays, optimized for index-only property
  * access over a vector of slots (obj->dslots) with high load factor.  Array
  * methods optimize for denseness by testing that the object's class is
  * &js_ArrayClass, and can then directly manipulate the slots for efficiency.
  *
  * We track these pieces of metadata for arrays in dense mode:
  *  - the array's length property as a uint32, in JSSLOT_ARRAY_LENGTH,
  *  - the number of indices that are filled (non-holes), in JSSLOT_ARRAY_COUNT,
  *  - the net number of slots starting at dslots (capacity), in dslots[-1] if
  *    dslots is non-NULL.
  *
- * In dense mode, holes in the array are represented by JSVAL_HOLE.  The final
- * slot in fslots is unused.
+ * In dense mode, holes in the array are represented by (JS_ARRAY_HOLE) invalid
+ * values.  The final slot in fslots is unused.
  *
  * NB: the capacity and length of a dense array are entirely unrelated!  The
  * length may be greater than, less than, or equal to the capacity.  See
  * array_length_setter for an explanation of how the first, most surprising
  * case may occur.
  *
  * Arrays are converted to use js_SlowArrayClass when any of these conditions
  * are met:
@@ -121,58 +123,53 @@ INDEX_TOO_BIG(jsuint index)
     return index > JS_BIT(29) - 1;
 }
 
 #define INDEX_TOO_SPARSE(array, index)                                         \
     (INDEX_TOO_BIG(index) ||                                                   \
      ((index) > js_DenseArrayCapacity(array) && (index) >= MIN_SPARSE_INDEX && \
       (index) > ((array)->getArrayCount() + 1) * 4))
 
-JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval));
-
 #define ENSURE_SLOW_ARRAY(cx, obj)                                             \
     (obj->getClass() == &js_SlowArrayClass || js_MakeArraySlow(cx, obj))
 
 /*
  * Determine if the id represents an array index or an XML property index.
  *
  * An id is an array index according to ECMA by (15.4):
  *
  * "Array objects give special treatment to a certain class of property names.
  * A property name P (in the form of a string value) is an array index if and
  * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
  * to 2^32-1."
  *
  * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
- * except that by using signed 32-bit integers we miss the top half of the
+ * except that by using signed 31-bit integers we miss the top half of the
  * valid range. This function checks the string representation itself; note
  * that calling a standard conversion routine might allow strings such as
  * "08" or "4.0" as array indices, which they are not.
  */
 JSBool
-js_IdIsIndex(jsval id, jsuint *indexp)
+js_IdIsIndex(jsid id, jsuint *indexp)
 {
-    JSString *str;
-    jschar *cp;
-
-    if (JSVAL_IS_INT(id)) {
+    if (JSID_IS_INT(id)) {
         jsint i;
-        i = JSVAL_TO_INT(id);
+        i = JSID_TO_INT(id);
         if (i < 0)
             return JS_FALSE;
         *indexp = (jsuint)i;
         return JS_TRUE;
     }
 
     /* NB: id should be a string, but jsxml.c may call us with an object id. */
-    if (!JSVAL_IS_STRING(id))
+    if (!JSID_IS_ATOM(id))
         return JS_FALSE;
 
-    str = JSVAL_TO_STRING(id);
-    cp = str->chars();
+    JSString *str = ATOM_TO_STRING(JSID_TO_ATOM(id));
+    jschar *cp = str->chars();
     if (JS7_ISDEC(*cp) && str->length() < sizeof(MAXSTR)) {
         jsuint index = JS7_UNDEC(*cp++);
         jsuint oldIndex = 0;
         jsuint c = 0;
         if (index != 0) {
             while (JS7_ISDEC(*cp)) {
                 oldIndex = index;
                 c = JS7_UNDEC(*cp);
@@ -189,99 +186,101 @@ js_IdIsIndex(jsval id, jsuint *indexp)
             *indexp = index;
             return JS_TRUE;
         }
     }
     return JS_FALSE;
 }
 
 static jsuint
-ValueIsLength(JSContext *cx, jsval* vp)
+ValueIsLength(JSContext *cx, Value* vp)
 {
-    jsint i;
-    jsuint length;
-
-    if (JSVAL_IS_INT(*vp)) {
-        i = JSVAL_TO_INT(*vp);
+    if (vp->isInt32()) {
+        int32_t i = vp->asInt32();
         if (i < 0)
             goto error;
         return (jsuint) i;
     }
 
     jsdouble d;
-    if (!ValueToNumber(cx, *vp, &d))
+    if (!ValueToNumber(cx, vp, &d))
         goto error;
 
     if (JSDOUBLE_IS_NaN(d))
         goto error;
+    jsuint length;
     length = (jsuint) d;
     if (d != (jsdouble) length)
         goto error;
     return length;
 
   error:
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                          JSMSG_BAD_ARRAY_LENGTH);
-    *vp = JSVAL_NULL;
+    vp->setNull();
     return 0;
 }
 
 JSBool
 js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
 {
     if (obj->isArray()) {
         *lengthp = obj->getArrayLength();
         return true;
     }
 
     if (obj->isArguments() && !IsOverriddenArgsLength(obj)) {
         *lengthp = GetArgsLength(obj);
         return true;
     }
 
-    AutoValueRooter tvr(cx, JSVAL_NULL);
+    AutoValueRooter tvr(cx, NullValue());
     if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), tvr.addr()))
         return false;
 
-    if (JSVAL_IS_INT(tvr.value())) {
-        *lengthp = jsuint(jsint(JSVAL_TO_INT(tvr.value()))); /* jsuint cast does ToUint32 */
+    if (tvr.value().isInt32()) {
+        *lengthp = jsuint(jsint(tvr.value().asInt32())); /* jsuint cast does ToUint32 */
         return true;
     }
 
     JS_STATIC_ASSERT(sizeof(jsuint) == sizeof(uint32_t));
-    return ValueToECMAUint32(cx, tvr.value(), (uint32_t *)lengthp);
+    return ValueToECMAUint32(cx, tvr.addr(), (uint32_t *)lengthp);
 }
 
-static JSBool
-IndexToValue(JSContext *cx, jsdouble index, jsval *vp)
+static inline void
+IndexToValue(JSContext *cx, jsdouble index, Value *vp)
 {
-    return js_NewWeaklyRootedNumber(cx, index, vp);
+    int32_t i;
+    if (JSDOUBLE_IS_INT32(index, i))
+        vp->setInt32(i);
+    else
+        vp->setDouble(index);
 }
 
 JSBool JS_FASTCALL
 js_IndexToId(JSContext *cx, jsuint index, jsid *idp)
 {
     JSString *str;
 
     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, STRING_TO_JSVAL(str), idp);
+    return js_ValueToStringId(cx, Value(str), idp);
 }
 
 static JSBool
 BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
              jsid *idp)
 {
     jschar buf[10], *start;
-    JSClass *clasp;
+    Class *clasp;
     JSAtom *atom;
     JS_STATIC_ASSERT((jsuint)-1 == 4294967295U);
 
     JS_ASSERT(index > JSVAL_INT_MAX);
 
     start = JS_ARRAY_END(buf);
     do {
         --start;
@@ -298,59 +297,59 @@ BigIndexToId(JSContext *cx, JSObject *ob
      * in any case.
      */
     if (!createAtom &&
         ((clasp = obj->getClass()) == &js_SlowArrayClass ||
          clasp == &js_ArgumentsClass ||
          clasp == &js_ObjectClass)) {
         atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start);
         if (!atom) {
-            *idp = JSVAL_VOID;
+            *idp = JSID_VOID;
             return JS_TRUE;
         }
     } else {
         atom = js_AtomizeChars(cx, start, JS_ARRAY_END(buf) - start, 0);
         if (!atom)
             return JS_FALSE;
     }
 
     *idp = ATOM_TO_JSID(atom);
     return JS_TRUE;
 }
 
 static JSBool
 ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 newlen,
             bool initializeAllSlots = true)
 {
-    jsval *slots, *newslots;
+    Value *slots, *newslots;
 
     if (newlen == 0) {
         if (obj->dslots) {
             cx->free(obj->dslots - 1);
             obj->dslots = NULL;
         }
         return JS_TRUE;
     }
 
     if (newlen > MAX_DSLOTS_LENGTH) {
         js_ReportAllocationOverflow(cx);
         return JS_FALSE;
     }
 
     slots = obj->dslots ? obj->dslots - 1 : NULL;
-    newslots = (jsval *) cx->realloc(slots, (size_t(newlen) + 1) * sizeof(jsval));
+    newslots = (Value *) cx->realloc(slots, (size_t(newlen) + 1) * sizeof(Value));
     if (!newslots)
         return JS_FALSE;
 
     obj->dslots = newslots + 1;
     js_SetDenseArrayCapacity(obj, newlen);
 
     if (initializeAllSlots) {
         for (slots = obj->dslots + oldlen; slots < obj->dslots + newlen; slots++)
-            *slots = JSVAL_HOLE;
+            slots->setMagic(JS_ARRAY_HOLE);
     }
 
     return JS_TRUE;
 }
 
 /*
  * When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to grow,
  * double its capacity, to push() N elements in amortized O(N) time.
@@ -359,17 +358,17 @@ ResizeSlots(JSContext *cx, JSObject *obj
  * with a higher constant factor, and we waste less space.
  */
 #define CAPACITY_DOUBLING_MAX    (1024 * 1024)
 
 /*
  * Round up all large allocations to a multiple of this (1MB), so as not to
  * waste space if malloc gives us 1MB-sized chunks (as jemalloc does).
  */
-#define CAPACITY_CHUNK  (1024 * 1024 / sizeof(jsval))
+#define CAPACITY_CHUNK  (1024 * 1024 / sizeof(Value))
 
 static JSBool
 EnsureCapacity(JSContext *cx, JSObject *obj, uint32 newcap,
                bool initializeAllSlots = true)
 {
     uint32 oldcap = js_DenseArrayCapacity(obj);
 
     if (newcap > oldcap) {
@@ -393,206 +392,204 @@ EnsureCapacity(JSContext *cx, JSObject *
         else if (actualCapacity < ARRAY_CAPACITY_MIN)
             actualCapacity = ARRAY_CAPACITY_MIN;
         if (!ResizeSlots(cx, obj, oldcap, actualCapacity, initializeAllSlots))
             return JS_FALSE;
         if (!initializeAllSlots) {
             /*
              * Initialize the slots caller didn't actually ask for.
              */
-            for (jsval *slots = obj->dslots + newcap;
+            for (Value *slots = obj->dslots + newcap;
                  slots < obj->dslots + actualCapacity;
                  slots++) {
-                *slots = JSVAL_HOLE;
+                slots->setMagic(JS_ARRAY_HOLE);
             }
         }
     }
     return JS_TRUE;
 }
 
 static bool
 ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp)
 {
-    AutoValueRooter dval(cx);
-    if (!js_NewDoubleInRootedValue(cx, index, dval.addr()) ||
-        !js_ValueToStringId(cx, dval.value(), idp)) {
-        return JS_FALSE;
-    }
-    return JS_TRUE;
+    return js_ValueToStringId(cx, Value(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));
         return JS_TRUE;
     }
 
     if (index <= jsuint(-1)) {
         if (!BigIndexToId(cx, obj, jsuint(index), createAtom, idp))
             return JS_FALSE;
-        if (hole && JSVAL_IS_VOID(*idp))
+        if (hole && JSID_IS_VOID(*idp))
             *hole = JS_TRUE;
         return JS_TRUE;
     }
 
     return ReallyBigIndexToId(cx, index, idp);
 }
 
 /*
  * If the property at the given index exists, get its value into location
  * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
  * to JSVAL_VOID. This function assumes that the location pointed by vp is
  * properly rooted and can be used as GC-protected storage for temporaries.
  */
 static JSBool
 GetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole,
-                jsval *vp)
+                Value *vp)
 {
     JS_ASSERT(index >= 0);
+    Value *vp2;
     if (obj->isDenseArray() && index < js_DenseArrayCapacity(obj) &&
-        (*vp = obj->dslots[jsuint(index)]) != JSVAL_HOLE) {
+        !(vp2 = &obj->dslots[jsuint(index)])->isMagic(JS_ARRAY_HOLE)) {
+        vp->copy(*vp2);
         *hole = JS_FALSE;
         return JS_TRUE;
     }
 
     AutoIdRooter idr(cx);
 
     *hole = JS_FALSE;
     if (!IndexToId(cx, obj, index, hole, idr.addr()))
         return JS_FALSE;
     if (*hole) {
-        *vp = JSVAL_VOID;
+        vp->setUndefined();
         return JS_TRUE;
     }
 
     JSObject *obj2;
     JSProperty *prop;
     if (!obj->lookupProperty(cx, idr.id(), &obj2, &prop))
         return JS_FALSE;
     if (!prop) {
         *hole = JS_TRUE;
-        *vp = JSVAL_VOID;
+        vp->setUndefined();
     } else {
         obj2->dropProperty(cx, prop);
         if (!obj->getProperty(cx, idr.id(), vp))
             return JS_FALSE;
         *hole = JS_FALSE;
     }
     return JS_TRUE;
 }
 
 /*
  * Set the value of the property at the given index to v assuming v is rooted.
  */
 static JSBool
-SetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, jsval v)
+SetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, const Value &v)
 {
     JS_ASSERT(index >= 0);
 
     if (obj->isDenseArray()) {
         /* Predicted/prefetched code should favor the remains-dense case. */
         if (index <= jsuint(-1)) {
             jsuint idx = jsuint(index);
             if (!INDEX_TOO_SPARSE(obj, idx)) {
                 JS_ASSERT(idx + 1 > idx);
                 if (!EnsureCapacity(cx, obj, idx + 1))
                     return JS_FALSE;
                 if (idx >= obj->getArrayLength())
                     obj->setArrayLength(idx + 1);
-                if (obj->dslots[idx] == JSVAL_HOLE)
+                if (obj->dslots[idx].isMagic(JS_ARRAY_HOLE))
                     obj->incArrayCountBy(1);
-                obj->dslots[idx] = v;
+                obj->dslots[idx].copy(v);
                 return JS_TRUE;
             }
         }
 
         if (!js_MakeArraySlow(cx, obj))
             return JS_FALSE;
     }
 
     AutoIdRooter idr(cx);
 
     if (!IndexToId(cx, obj, index, NULL, idr.addr(), JS_TRUE))
         return JS_FALSE;
-    JS_ASSERT(!JSVAL_IS_VOID(idr.id()));
-
-    return obj->setProperty(cx, idr.id(), &v);
+    JS_ASSERT(!JSID_IS_VOID(idr.id()));
+
+    Value tmp;
+    tmp.copy(v);
+    return obj->setProperty(cx, idr.id(), &tmp);
 }
 
 static JSBool
 DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index)
 {
     JS_ASSERT(index >= 0);
     if (obj->isDenseArray()) {
         if (index <= jsuint(-1)) {
             jsuint idx = jsuint(index);
             if (!INDEX_TOO_SPARSE(obj, idx) && idx < js_DenseArrayCapacity(obj)) {
-                if (obj->dslots[idx] != JSVAL_HOLE)
+                if (!obj->dslots[idx].isMagic(JS_ARRAY_HOLE))
                     obj->decArrayCountBy(1);
-                obj->dslots[idx] = JSVAL_HOLE;
+                obj->dslots[idx].setMagic(JS_ARRAY_HOLE);
                 return JS_TRUE;
             }
         }
         return JS_TRUE;
     }
 
     AutoIdRooter idr(cx);
 
     if (!IndexToId(cx, obj, index, NULL, idr.addr()))
         return JS_FALSE;
-    if (JSVAL_IS_VOID(idr.id()))
+    if (JSID_IS_VOID(idr.id()))
         return JS_TRUE;
 
-    jsval junk;
+    Value junk;
     return obj->deleteProperty(cx, idr.id(), &junk);
 }
 
 /*
  * When hole is true, delete the property at the given index. Otherwise set
  * its value to v assuming v is rooted.
  */
 static JSBool
 SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index,
-                        JSBool hole, jsval v)
+                        JSBool hole, const Value &v)
 {
     if (hole) {
-        JS_ASSERT(JSVAL_IS_VOID(v));
+        JS_ASSERT(v.isUndefined());
         return DeleteArrayElement(cx, obj, index);
     }
     return SetArrayElement(cx, obj, index, v);
 }
 
 JSBool
 js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length)
 {
-    jsval v;
+    Value v;
     jsid id;
 
-    if (!IndexToValue(cx, length, &v))
-        return JS_FALSE;
+    IndexToValue(cx, length, &v);
     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, JSVAL_NULL);
+    AutoValueRooter tvr(cx, NullValue());
     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 !JSVAL_IS_NULL(tvr.value());
+    return !tvr.value().isNull();
 }
 
 JSBool
 js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp)
 {
     JSObject *wrappedObj = js_GetWrappedObject(cx, obj);
 
     *answerp = wrappedObj->isArguments() || wrappedObj->isArray();
@@ -611,48 +608,48 @@ js_IsArrayLike(JSContext *cx, JSObject *
  *
  * Since SpiderMonkey supports cross-class prototype-based delegation, we have
  * to be careful about the length getter and setter being called on an object
  * not of Array class. For the getter, we search obj's prototype chain for the
  * array that caused this getter to be invoked. In the setter case to overcome
  * the JSPROP_SHARED attribute, we must define a shadowing length property.
  */
 static JSBool
-array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+array_length_getter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     do {
-        if (obj->isArray())
-            return IndexToValue(cx, obj->getArrayLength(), vp);
+        if (obj->isArray()) {
+            IndexToValue(cx, obj->getArrayLength(), vp);
+            return JS_TRUE;
+        }
     } while ((obj = obj->getProto()) != NULL);
     return JS_TRUE;
 }
 
 static JSBool
-array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     jsuint newlen, oldlen, gap, index;
-    jsval junk;
+    Value junk;
 
     if (!obj->isArray()) {
         jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
 
         return obj->defineProperty(cx, lengthId, *vp, NULL, NULL, JSPROP_ENUMERATE);
     }
 
     newlen = ValueIsLength(cx, vp);
-    if (JSVAL_IS_NULL(*vp))
+    if (vp->isNull())
         return false;
     oldlen = obj->getArrayLength();
 
     if (oldlen == newlen)
         return true;
 
-    if (!IndexToValue(cx, newlen, vp))
-        return false;
-
+    IndexToValue(cx, newlen, vp);
     if (oldlen < newlen) {
         obj->setArrayLength(newlen);
         return true;
     }
 
     if (obj->isDenseArray()) {
         /* Don't reallocate if we're not actually shrinking our slots. */
         jsuint capacity = js_DenseArrayCapacity(obj);
@@ -672,23 +669,23 @@ array_length_setter(JSContext *cx, JSObj
          * correspond to indexes in the half-open range [newlen, oldlen).  See
          * bug 322135.
          */
         JSObject *iter = JS_NewPropertyIterator(cx, obj);
         if (!iter)
             return false;
 
         /* Protect iter against GC under JSObject::deleteProperty. */
-        AutoValueRooter tvr(cx, iter);
+        AutoObjectRooter tvr(cx, iter);
 
         gap = oldlen - newlen;
         for (;;) {
             if (!JS_CHECK_OPERATION_LIMIT(cx) || !JS_NextProperty(cx, iter, &id))
                 return false;
-            if (JSVAL_IS_VOID(id))
+            if (JSID_IS_VOID(id))
                 break;
             if (js_IdIsIndex(id, &index) && index - newlen < gap &&
                 !obj->deleteProperty(cx, id, &junk)) {
                 return false;
             }
         }
     }
 
@@ -705,17 +702,17 @@ IsDenseArrayId(JSContext *cx, JSObject *
 {
     JS_ASSERT(obj->isDenseArray());
 
     uint32 i;
     return id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) ||
            (js_IdIsIndex(id, &i) &&
             obj->getArrayLength() != 0 &&
             i < js_DenseArrayCapacity(obj) &&
-            obj->dslots[i] != JSVAL_HOLE);
+            !obj->dslots[i].isMagic(JS_ARRAY_HOLE));
 }
 
 static JSBool
 array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
                      JSProperty **propp)
 {
     if (!obj->isDenseArray())
         return js_LookupProperty(cx, obj, id, objp, propp);
@@ -738,94 +735,98 @@ array_lookupProperty(JSContext *cx, JSOb
 static void
 array_dropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
 {
     JS_ASSERT(IsDenseArrayId(cx, obj, (jsid) prop));
 }
 
 JSBool
 js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, JSProperty *prop,
-                             jsval *vp)
+                             Value *vp)
 {
     jsid id = (jsid) prop;
     JS_ASSERT(IsDenseArrayId(cx, obj, id));
 
     uint32 i;
     if (!js_IdIsIndex(id, &i)) {
         JS_ASSERT(id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom));
-        return IndexToValue(cx, obj->getArrayLength(), vp);
+        IndexToValue(cx, obj->getArrayLength(), vp);
+        return JS_TRUE;
     }
-    *vp = obj->dslots[i];
+    vp->copy(obj->dslots[i]);
     return JS_TRUE;
 }
 
 static JSBool
-array_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+array_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     uint32 i;
 
-    if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
-        return IndexToValue(cx, obj->getArrayLength(), vp);
+    if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
+        IndexToValue(cx, obj->getArrayLength(), vp);
+        return JS_TRUE;
+    }
 
     if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) {
-        *vp = OBJECT_TO_JSVAL(obj->getProto());
+        vp->copy(obj->getProtoValue());
         return JS_TRUE;
     }
 
     if (!obj->isDenseArray())
         return js_GetProperty(cx, obj, id, vp);
 
-    if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= js_DenseArrayCapacity(obj) ||
-        obj->dslots[i] == JSVAL_HOLE) {
+    if (!js_IdIsIndex(id, &i) || i >= js_DenseArrayCapacity(obj) ||
+        obj->dslots[i].isMagic(JS_ARRAY_HOLE)) {
         JSObject *obj2;
         JSProperty *prop;
         JSScopeProperty *sprop;
 
         JSObject *proto = obj->getProto();
         if (!proto) {
-            *vp = JSVAL_VOID;
+            vp->setUndefined();
             return JS_TRUE;
         }
 
-        *vp = JSVAL_VOID;
+        vp->setUndefined();
         if (js_LookupPropertyWithFlags(cx, proto, id, cx->resolveFlags,
                                        &obj2, &prop) < 0)
             return JS_FALSE;
 
         if (prop) {
             if (obj2->isNative()) {
                 sprop = (JSScopeProperty *) prop;
                 if (!js_NativeGet(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, vp))
                     return JS_FALSE;
             }
             obj2->dropProperty(cx, prop);
         }
         return JS_TRUE;
     }
 
-    *vp = obj->dslots[i];
+    vp->copy(obj->dslots[i]);
     return JS_TRUE;
 }
 
 static JSBool
-slowarray_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     jsuint index, length;
 
     if (!js_IdIsIndex(id, &index))
         return JS_TRUE;
     length = obj->getArrayLength();
     if (index >= length)
         obj->setArrayLength(index + 1);
     return JS_TRUE;
 }
 
-static JSBool
+/* XXX gal's patch is making this go away. */
+JSBool
 slowarray_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
-                    jsval *statep, jsid *idp);
+                    Value *statep, jsid *idp);
 
 static JSType
 array_typeOf(JSContext *cx, JSObject *obj)
 {
     return JSTYPE_OBJECT;
 }
 
 /* The same as js_ObjectOps except for the .enumerate and .call hooks. */
@@ -838,23 +839,23 @@ static JSObjectOps js_SlowArrayObjectOps
     slowarray_enumerate,    js_CheckAccess,
     array_typeOf,           js_TraceObject,
     NULL,                   NATIVE_DROP_PROPERTY,
     NULL,                   js_Construct,
     js_HasInstance,         js_Clear
 };
 
 static JSObjectOps *
-slowarray_getObjectOps(JSContext *cx, JSClass *clasp)
+slowarray_getObjectOps(JSContext *cx, Class *clasp)
 {
     return &js_SlowArrayObjectOps;
 }
 
 static JSBool
-array_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     uint32 i;
 
     if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
         return array_length_setter(cx, obj, id, vp);
 
     if (!obj->isDenseArray())
         return js_SetProperty(cx, obj, id, vp);
@@ -865,19 +866,19 @@ array_setProperty(JSContext *cx, JSObjec
         return js_SetProperty(cx, obj, id, vp);
     }
 
     if (!EnsureCapacity(cx, obj, i + 1))
         return JS_FALSE;
 
     if (i >= obj->getArrayLength())
         obj->setArrayLength(i + 1);
-    if (obj->dslots[i] == JSVAL_HOLE)
+    if (obj->dslots[i].isMagic(JS_ARRAY_HOLE))
         obj->incArrayCountBy(1);
-    obj->dslots[i] = *vp;
+    obj->dslots[i].copy(*vp);
     return JS_TRUE;
 }
 
 JSBool
 js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj)
 {
     /*
      * Walk up the prototype chain and see if this indexed element already
@@ -896,22 +897,22 @@ js_PrototypeHasIndexedProperties(JSConte
             return JS_TRUE;
     }
     return JS_FALSE;
 }
 
 #ifdef JS_TRACER
 
 static inline JSBool FASTCALL
-dense_grow(JSContext* cx, JSObject* obj, jsint i, jsval v)
+dense_grow(JSContext* cx, JSObject* obj, jsint i, const jsval &v)
 {
     /*
      * Let the interpreter worry about negative array indexes.
      */
-    JS_ASSERT((MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) == (sizeof(jsval) != sizeof(uint32)));
+    JS_ASSERT((MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) == (sizeof(Value) != sizeof(uint32)));
     if (MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) {
         /*
          * Have to check for negative values bleeding through on 64-bit machines only,
          * since we can't allocate large enough arrays for this on 32-bit machines.
          */
         if (i < 0)
             return JS_FALSE;
     }
@@ -934,17 +935,17 @@ dense_grow(JSContext* cx, JSObject* obj,
     }
 
     obj->dslots[u] = v;
     return JS_TRUE;
 }
 
 
 JSBool FASTCALL
-js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v)
+js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, const jsval &v)
 {
     JS_ASSERT(obj->isDenseArray());
     return dense_grow(cx, obj, i, v);
 }
 JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem, CONTEXT, OBJECT, INT32, JSVAL, 0,
                      nanojit::ACC_STORE_ANY)
 
 JSBool FASTCALL
@@ -983,33 +984,35 @@ js_Array_dense_setelem_double(JSContext*
 
     return dense_grow(cx, obj, i, v);
 }
 JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem_double, CONTEXT, OBJECT, INT32, DOUBLE,
                      0, nanojit::ACC_STORE_ANY)
 #endif
 
 static JSBool
-array_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
-                     JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
+array_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
+                     PropertyOp getter, PropertyOp setter, uintN attrs)
 {
     uint32 i = 0;       // init to shut GCC up
     JSBool isIndex;
 
     if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
         return JS_TRUE;
 
-    isIndex = js_IdIsIndex(ID_TO_VALUE(id), &i);
+    isIndex = js_IdIsIndex(id, &i);
     if (!isIndex || attrs != JSPROP_ENUMERATE || !obj->isDenseArray() || INDEX_TOO_SPARSE(obj, i)) {
         if (!ENSURE_SLOW_ARRAY(cx, obj))
             return JS_FALSE;
         return js_DefineProperty(cx, obj, id, value, getter, setter, attrs);
     }
 
-    return array_setProperty(cx, obj, id, &value);
+    Value tmp;
+    tmp.copy(*value);
+    return array_setProperty(cx, obj, id, &tmp);
 }
 
 static JSBool
 array_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
                     uintN *attrsp)
 {
     *attrsp = id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
         ? JSPROP_PERMANENT : JSPROP_ENUMERATE;
@@ -1021,227 +1024,60 @@ array_setAttributes(JSContext *cx, JSObj
                     uintN *attrsp)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                          JSMSG_CANT_SET_ARRAY_ATTRS);
     return JS_FALSE;
 }
 
 static JSBool
-array_deleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
+array_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval)
 {
     uint32 i;
 
     if (!obj->isDenseArray())
         return js_DeleteProperty(cx, obj, id, rval);
 
     if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
-        *rval = JSVAL_FALSE;
+        rval->setBoolean(false);
         return JS_TRUE;
     }
 
     if (js_IdIsIndex(id, &i) && i < js_DenseArrayCapacity(obj) &&
-        obj->dslots[i] != JSVAL_HOLE) {
+        !obj->dslots[i].isMagic(JS_ARRAY_HOLE)) {
         obj->decArrayCountBy(1);
-        obj->dslots[i] = JSVAL_HOLE;
+        obj->dslots[i].setMagic(JS_ARRAY_HOLE);
     }
 
-    *rval = JSVAL_TRUE;
+    rval->setBoolean(true);
     return JS_TRUE;
 }
 
-/*
- * JSObjectOps.enumerate implementation.
- *
- * For a fast array, JSENUMERATE_INIT captures in the enumeration state both
- * the length of the array and the bitmap indicating the positions of holes in
- * the array. This ensures that adding or deleting array elements does not
- * affect the sequence of indexes JSENUMERATE_NEXT returns.
- *
- * For a common case of an array without holes, to represent the state we pack
- * the (nextEnumerationIndex, arrayLength) pair as a pseudo-boolean jsval.
- * This is possible when length <= PACKED_UINT_PAIR_BITS. For arrays with
- * greater length or holes we allocate the JSIndexIterState structure and
- * store it as an int-tagged private pointer jsval. For a slow array we
- * delegate the enumeration implementation to js_Enumerate in
- * slowarray_enumerate.
- *
- * Array mutations can turn a fast array into a slow one after the enumeration
- * starts. When this happens, slowarray_enumerate receives a state created
- * when the array was fast. To distinguish such fast state from a slow state,
- * which is an int-tagged pointer that js_Enumerate creates, we set not one
- * but two lowest bits when tagging a JSIndexIterState pointer -- see
- * INDEX_ITER_TAG usage below. Thus, when slowarray_enumerate receives a state
- * tagged with JSVAL_SPECIAL or with two lowest bits set, it knows that this
- * is a fast state so it calls array_enumerate to continue enumerating the
- * indexes present in the original fast array.
- */
-
-#define PACKED_UINT_PAIR_BITS           14
-#define PACKED_UINT_PAIR_MASK           JS_BITMASK(PACKED_UINT_PAIR_BITS)
-
-#define UINT_PAIR_TO_SPECIAL_JSVAL(i,j)                                \
-    (JS_ASSERT((uint32) (i) <= PACKED_UINT_PAIR_MASK),                        \
-     JS_ASSERT((uint32) (j) <= PACKED_UINT_PAIR_MASK),                        \
-     ((jsval) (i) << (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)) |               \
-     ((jsval) (j) << (JSVAL_TAGBITS)) |                                       \
-     (jsval) JSVAL_SPECIAL)
-
-#define SPECIAL_JSVAL_TO_UINT_PAIR(v,i,j)                              \
-    (JS_ASSERT(JSVAL_IS_SPECIAL(v)),                                   \
-     (i) = (uint32) ((v) >> (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)),         \
-     (j) = (uint32) ((v) >> JSVAL_TAGBITS) & PACKED_UINT_PAIR_MASK,           \
-     JS_ASSERT((i) <= PACKED_UINT_PAIR_MASK))
-
-JS_STATIC_ASSERT(PACKED_UINT_PAIR_BITS * 2 + JSVAL_TAGBITS <= JS_BITS_PER_WORD);
-
-typedef struct JSIndexIterState {
-    uint32          index;
-    uint32          length;
-    JSBool          hasHoles;
-
-    /*
-     * Variable-length bitmap representing array's holes. It must not be
-     * accessed when hasHoles is false.
-     */
-    jsbitmap        holes[1];
-} JSIndexIterState;
-
-#define INDEX_ITER_TAG      3
-
-JS_STATIC_ASSERT(JSVAL_INT == 1);
-
-static JSBool
-array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
-                jsval *statep, jsid *idp)
-{
-    uint32 capacity, i;
-    JSIndexIterState *ii;
-
-    switch (enum_op) {
-      case JSENUMERATE_INIT:
-        JS_ASSERT(obj->isDenseArray());
-        capacity = js_DenseArrayCapacity(obj);
-        if (idp)
-            *idp = INT_TO_JSVAL(obj->getArrayCount());
-        ii = NULL;
-        for (i = 0; i != capacity; ++i) {
-            if (obj->dslots[i] == JSVAL_HOLE) {
-                if (!ii) {
-                    ii = (JSIndexIterState *)
-                         cx->malloc(offsetof(JSIndexIterState, holes) +
-                                   JS_BITMAP_SIZE(capacity));
-                    if (!ii)
-                        return JS_FALSE;
-                    ii->hasHoles = JS_TRUE;
-                    memset(ii->holes, 0, JS_BITMAP_SIZE(capacity));
-                }
-                JS_SET_BIT(ii->holes, i);
-            }
-        }
-        if (!ii) {
-            /* Array has no holes. */
-            if (capacity <= PACKED_UINT_PAIR_MASK) {
-                *statep = UINT_PAIR_TO_SPECIAL_JSVAL(0, capacity);
-                break;
-            }
-            ii = (JSIndexIterState *)
-                 cx->malloc(offsetof(JSIndexIterState, holes));
-            if (!ii)
-                return JS_FALSE;
-            ii->hasHoles = JS_FALSE;
-        }
-        ii->index = 0;
-        ii->length = capacity;
-        *statep = (jsval) ii | INDEX_ITER_TAG;
-        JS_ASSERT(*statep & JSVAL_INT);
-        break;
-
-      case JSENUMERATE_NEXT:
-        if (JSVAL_IS_SPECIAL(*statep)) {
-            SPECIAL_JSVAL_TO_UINT_PAIR(*statep, i, capacity);
-            if (i != capacity) {
-                *idp = INT_TO_JSID(i);
-                *statep = UINT_PAIR_TO_SPECIAL_JSVAL(i + 1, capacity);
-                break;
-            }
-        } else {
-            JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG);
-            ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG);
-            i = ii->index;
-            if (i != ii->length) {
-                /* Skip holes if any. */
-                if (ii->hasHoles) {
-                    while (JS_TEST_BIT(ii->holes, i) && ++i != ii->length)
-                        continue;
-                }
-                if (i != ii->length) {
-                    ii->index = i + 1;
-                    return js_IndexToId(cx, i, idp);
-                }
-            }
-        }
-        /* FALL THROUGH */
-
-      case JSENUMERATE_DESTROY:
-        if (!JSVAL_IS_SPECIAL(*statep)) {
-            JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG);
-            ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG);
-            cx->free(ii);
-        }
-        *statep = JSVAL_NULL;
-        break;
-    }
-    return JS_TRUE;
-}
-
-static JSBool
-slowarray_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
-                    jsval *statep, jsid *idp)
-{
-    JSBool ok;
-
-    /* Are we continuing an enumeration that started when we were dense? */
-    if (enum_op != JSENUMERATE_INIT) {
-        if (JSVAL_IS_SPECIAL(*statep) ||
-            (*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG) {
-            return array_enumerate(cx, obj, enum_op, statep, idp);
-        }
-        JS_ASSERT((*statep & INDEX_ITER_TAG) == JSVAL_INT);
-    }
-    ok = js_Enumerate(cx, obj, enum_op, statep, idp);
-    JS_ASSERT(*statep == JSVAL_NULL || (*statep & INDEX_ITER_TAG) == JSVAL_INT);
-    return ok;
-}
+/* XXX gal's patch is making this go away. */
+JSBool
+array_enumerate(JSContext *cx, JSObject *, JSIterateOp, Value *, jsid *);
 
 static void
 array_finalize(JSContext *cx, JSObject *obj)
 {
     if (obj->dslots)
         cx->free(obj->dslots - 1);
     obj->dslots = NULL;
 }
 
 static void
 array_trace(JSTracer *trc, JSObject *obj)
 {
-    uint32 capacity;
-    size_t i;
-    jsval v;
-
     JS_ASSERT(obj->isDenseArray());
     obj->traceProtoAndParent(trc);
 
-    capacity = js_DenseArrayCapacity(obj);
-    for (i = 0; i < capacity; i++) {
-        v = obj->dslots[i];
-        if (JSVAL_IS_TRACEABLE(v)) {
-            JS_SET_TRACING_INDEX(trc, "array_dslots", i);
-            js_CallGCMarker(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
-        }
+    uint32 capacity = js_DenseArrayCapacity(obj);
+    for (size_t i = 0; i < capacity; i++) {
+        JS_SET_TRACING_INDEX(trc, "array_dslots", i);
+        CallGCMarkerIfGCThing(trc, obj->dslots[i]);
     }
 }
 
 extern JSObjectOps js_ArrayObjectOps;
 
 static const JSObjectMap SharedArrayMap(&js_ArrayObjectOps, JSObjectMap::SHAPELESS);
 
 JSObjectOps js_ArrayObjectOps = {
@@ -1253,40 +1089,40 @@ JSObjectOps js_ArrayObjectOps = {
     array_enumerate,      js_CheckAccess,
     array_typeOf,         array_trace,
     NULL,                 array_dropProperty,
     NULL,                 NULL,
     js_HasInstance,       NULL
 };
 
 static JSObjectOps *
-array_getObjectOps(JSContext *cx, JSClass *clasp)
+array_getObjectOps(JSContext *cx, Class *clasp)
 {
     return &js_ArrayObjectOps;
 }
 
-JSClass js_ArrayClass = {
+Class js_ArrayClass = {
     "Array",
     JSCLASS_HAS_RESERVED_SLOTS(2) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array) |
     JSCLASS_NEW_ENUMERATE,
-    JS_PropertyStub,    JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
-    JS_EnumerateStub,   JS_ResolveStub,    js_TryValueOf,     array_finalize,
-    array_getObjectOps, NULL,              NULL,              NULL,
-    NULL,               NULL,              NULL,              NULL
+    PropertyStub,       PropertyStub,    PropertyStub,         PropertyStub,
+    EnumerateStub,      ResolveStub,     js_TryValueOf,        array_finalize,
+    array_getObjectOps, NULL,            NULL,                 NULL,
+    NULL,               NULL,            NULL,                 NULL
 };
 
-JSClass js_SlowArrayClass = {
+Class js_SlowArrayClass = {
     "Array",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
-    slowarray_addProperty, JS_PropertyStub, JS_PropertyStub,  JS_PropertyStub,
-    JS_EnumerateStub,      JS_ResolveStub,  js_TryValueOf,    NULL,
-    slowarray_getObjectOps, NULL,           NULL,             NULL,
-    NULL,                  NULL,            NULL,             NULL
+    slowarray_addProperty,  PropertyStub,   PropertyStub,      PropertyStub,
+    EnumerateStub,          ResolveStub,    js_TryValueOf,     NULL,
+    slowarray_getObjectOps, NULL,           NULL,              NULL,
+    NULL,                   NULL,           NULL,              NULL
 };
 
 /*
  * Convert an array object from fast-and-dense to slow-and-flexible.
  */
 JSBool
 js_MakeArraySlow(JSContext *cx, JSObject *obj)
 {
@@ -1309,31 +1145,31 @@ js_MakeArraySlow(JSContext *cx, JSObject
     JSScope *scope = JSScope::create(cx, &js_SlowArrayObjectOps, &js_SlowArrayClass, obj,
                                      emptyShape);
     if (!scope)
         return JS_FALSE;
 
     uint32 capacity = js_DenseArrayCapacity(obj);
     if (capacity) {
         scope->freeslot = obj->numSlots() + JS_INITIAL_NSLOTS;
-        obj->dslots[-1] = JS_INITIAL_NSLOTS + capacity;
+        obj->dslots[-1].setPrivateUint32(JS_INITIAL_NSLOTS + capacity);
     } else {
         scope->freeslot = obj->numSlots();
     }
 
     /* Create new properties pointing to existing values in dslots */
     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->dslots[i] == JSVAL_HOLE) {
-            obj->dslots[i] = JSVAL_VOID;
+        if (obj->dslots[i].isMagic(JS_ARRAY_HOLE)) {
+            obj->dslots[i].setUndefined();
             continue;
         }
 
         sprop = scope->addDataProperty(cx, id, JS_INITIAL_NSLOTS + i,
                                        JSPROP_ENUMERATE);
         if (!sprop)
             goto out_bad;
     }
@@ -1341,50 +1177,46 @@ js_MakeArraySlow(JSContext *cx, JSObject
     /*
      * Render our formerly-reserved count property GC-safe.  We do not need to
      * make the length slot GC-safe because it is the private slot (this is
      * statically asserted within JSObject) where the implementation can store
      * an arbitrary value.
      */
     JS_ASSERT(js_SlowArrayClass.flags & JSCLASS_HAS_PRIVATE);
     obj->voidDenseArrayCount();
-
-    /* Make sure we preserve any flags borrowing bits in classword. */
-    obj->classword ^= (jsuword) &js_ArrayClass;
-    obj->classword |= (jsuword) &js_SlowArrayClass;
-
+    obj->changeClassToSlowArray();
     obj->map = scope;
     return JS_TRUE;
 
   out_bad:
     scope->destroy(cx);
     return JS_FALSE;
 }
 
 /* Transfer ownership of buffer to returned string. */
 static inline JSBool
-BufferToString(JSContext *cx, JSCharBuffer &cb, jsval *rval)
+BufferToString(JSContext *cx, JSCharBuffer &cb, Value *rval)
 {
     JSString *str = js_NewStringFromCharBuffer(cx, cb);
     if (!str)
         return false;
-    *rval = STRING_TO_JSVAL(str);
+    rval->setString(str);
     return true;
 }
 
 #if JS_HAS_TOSOURCE
 static JSBool
-array_toSource(JSContext *cx, uintN argc, jsval *vp)
+array_toSource(JSContext *cx, uintN argc, Value *vp)
 {
     JS_CHECK_RECURSION(cx, return false);
 
-    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
     if (!obj ||
         (obj->getClass() != &js_SlowArrayClass &&
-         !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
+         !InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
         return false;
     }
 
     /* Find joins or cycles in the reachable object graph. */
     jschar *sharpchars;
     JSHashEntry *he = js_EnterSharpObject(cx, obj, NULL, &sharpchars);
     if (!he)
         return false;
@@ -1438,17 +1270,17 @@ array_toSource(JSContext *cx, uintN argc
         JSString *str;
         if (hole) {
             str = cx->runtime->emptyString;
         } else {
             str = js_ValueToSource(cx, *vp);
             if (!str)
                 goto out;
         }
-        *vp = STRING_TO_JSVAL(str);
+        vp->setString(str);
         const jschar *chars;
         size_t charlen;
         str->getCharsAndLength(chars, charlen);
 
         /* Append element to buffer. */
         if (!cb.append(chars, charlen))
             goto out;
         if (index + 1 != length) {
@@ -1474,17 +1306,17 @@ array_toSource(JSContext *cx, uintN argc
     if (!initiallySharp)
         js_LeaveSharpObject(cx, NULL);
     return ok;
 }
 #endif
 
 static JSBool
 array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
-                   JSString *sepstr, jsval *rval)
+                   JSString *sepstr, Value *rval)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     /*
      * Use HashTable entry as the cycle indicator. On first visit, create the
      * entry, and, when leaving, remove the entry.
      */
     typedef js::HashSet<JSObject *> ObjSet;
@@ -1494,21 +1326,21 @@ array_toString_sub(JSContext *cx, JSObje
         /* Not in hash table, so not a cycle. */
         if (!cx->busyArrays.add(hashp, obj)) {
             JS_ReportOutOfMemory(cx);
             return false;
         }
         genBefore = cx->busyArrays.generation();
     } else {
         /* Cycle, so return empty string. */
-        *rval = ATOM_KEY(cx->runtime->atomState.emptyAtom);
+        rval->setString(ATOM_TO_STRING(cx->runtime->atomState.emptyAtom));
         return true;
     }
 
-    AutoValueRooter tvr(cx, obj);
+    AutoObjectRooter tvr(cx, obj);
 
     /* After this point, all paths exit through the 'out' label. */
     MUST_FLOW_THROUGH("out");
     bool ok = false;
 
     /* Get characters to use for the separator. */
     static const jschar comma = ',';
     const jschar *sep;
@@ -1534,26 +1366,23 @@ array_toString_sub(JSContext *cx, JSObje
         /* Use rval to locally root each element value. */
         JSBool hole;
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetArrayElement(cx, obj, index, &hole, rval)) {
             goto out;
         }
 
         /* Get element's character string. */
-        if (!(hole || JSVAL_IS_VOID(*rval) || JSVAL_IS_NULL(*rval))) {
+        if (!(hole || rval->isNullOrUndefined())) {
             if (locale) {
                 /* Work on obj.toLocalString() instead. */
-                JSObject *robj;
-
-                if (!js_ValueToObject(cx, *rval, &robj))
+                if (!js_ValueToObjectOrNull(cx, *rval, rval))
                     goto out;
-                *rval = OBJECT_TO_JSVAL(robj);
                 JSAtom *atom = cx->runtime->atomState.toLocaleStringAtom;
-                if (!js_TryMethod(cx, robj, atom, 0, NULL, rval))
+                if (!js_TryMethod(cx, rval->asObjectOrNull(), atom, 0, NULL, rval))
                     goto out;
             }
 
             if (!js_ValueToCharBuffer(cx, *rval, cb))
                 goto out;
         }
 
         /* Append the separator. */
@@ -1573,39 +1402,35 @@ array_toString_sub(JSContext *cx, JSObje
     if (genBefore == cx->busyArrays.generation())
         cx->busyArrays.remove(hashp);
     else
         cx->busyArrays.remove(obj);
     return ok;
 }
 
 static JSBool
-array_toString(JSContext *cx, uintN argc, jsval *vp)
+array_toString(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj;
-
-    obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
     if (!obj ||
         (obj->getClass() != &js_SlowArrayClass &&
-         !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
+         !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, jsval *vp)
+array_toLocaleString(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj;
-
-    obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
     if (!obj ||
         (obj->getClass() != &js_SlowArrayClass &&
-         !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
+         !InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
         return JS_FALSE;
     }
 
     /*
      *  Passing comma here as the separator. Need a way to get a
      *  locale-specific version.
      */
     return array_toString_sub(cx, obj, JS_TRUE, NULL, vp);
@@ -1617,17 +1442,17 @@ enum TargetElementsType {
 };
 
 enum SourceVectorType {
     SourceVectorAllValues,
     SourceVectorMayContainHoles
 };
 
 static JSBool
-InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, jsval *vector,
+InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Value *vector,
                   TargetElementsType targetType, SourceVectorType vectorType)
 {
     JS_ASSERT(count < MAXINDEX);
 
     /*
      * Optimize for dense arrays so long as adding the given set of elements
      * wouldn't otherwise make the array slow.
      */
@@ -1656,96 +1481,92 @@ InitArrayElements(JSContext *cx, JSObjec
         jsuint newlen = start + count;
         JS_ASSERT(jsdouble(start) + count == jsdouble(newlen));
         if (!EnsureCapacity(cx, obj, newlen))
             return JS_FALSE;
 
         if (newlen > obj->getArrayLength())
             obj->setArrayLength(newlen);
 
-        JS_ASSERT(count < size_t(-1) / sizeof(jsval));
+        JS_ASSERT(count < size_t(-1) / sizeof(Value));
         if (targetType == TargetElementsMayContainValues) {
             jsuint valueCount = 0;
             for (jsuint i = 0; i < count; i++) {
-                 if (obj->dslots[start + i] != JSVAL_HOLE)
+                 if (!obj->dslots[start + i].isMagic(JS_ARRAY_HOLE))
                      valueCount++;
             }
             JS_ASSERT(obj->getArrayCount() >= valueCount);
             obj->decArrayCountBy(valueCount);
         }
-        memcpy(obj->dslots + start, vector, sizeof(jsval) * count);
+        memcpy(obj->dslots + start, vector, sizeof(Value) * count);
         if (vectorType == SourceVectorAllValues) {
             obj->incArrayCountBy(count);
         } else {
             jsuint valueCount = 0;
             for (jsuint i = 0; i < count; i++) {
-                 if (obj->dslots[start + i] != JSVAL_HOLE)
+                 if (!obj->dslots[start + i].isMagic(JS_ARRAY_HOLE))
                      valueCount++;
             }
             obj->incArrayCountBy(valueCount);
         }
-        JS_ASSERT_IF(count != 0, obj->dslots[newlen - 1] != JSVAL_HOLE);
+        JS_ASSERT_IF(count != 0, !obj->dslots[newlen - 1].isMagic());
         return JS_TRUE;
     }
 
-    jsval* end = vector + count;
+    Value* end = vector + count;
     while (vector != end && start < MAXINDEX) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !SetArrayElement(cx, obj, start++, *vector++)) {
             return JS_FALSE;
         }
     }
 
     if (vector == end)
         return JS_TRUE;
 
     /* 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);
-    jsval tmp[2] = {JSVAL_NULL, JSVAL_NULL};
-    AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(tmp), tmp);
-    if (!js_NewDoubleInRootedValue(cx, MAXINDEX, &tmp[0]))
-        return JS_FALSE;
-    jsdouble *dp = JSVAL_TO_DOUBLE(tmp[0]);
-    JS_ASSERT(*dp == MAXINDEX);
+    AutoValueRooter tvr(cx);
     AutoIdRooter idr(cx);
+    Value idval(double(MAXINDEX));
     do {
-        tmp[1] = *vector++;
-        if (!js_ValueToStringId(cx, tmp[0], idr.addr()) ||
-            !obj->setProperty(cx, idr.id(), &tmp[1])) {
+        tvr.addr()->copy(*vector++);
+        if (!js_ValueToStringId(cx, idval, idr.addr()) ||
+            !obj->setProperty(cx, idr.id(), tvr.addr())) {
             return JS_FALSE;
         }
-        *dp += 1;
+        idval.asDoubleRef() += 1;
     } while (vector != end);
 
     return JS_TRUE;
 }
 
 static JSBool
-InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const jsval *vector,
+InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector,
                 bool holey = false)
 {
     JS_ASSERT(obj->isArray());
 
     obj->setArrayLength(length);
 
     if (vector) {
         if (!EnsureCapacity(cx, obj, length))
             return JS_FALSE;
 
         jsuint count = length;
         if (!holey) {
-            memcpy(obj->dslots, vector, length * sizeof (jsval));
+            memcpy(obj->dslots, vector, length * sizeof (Value));
         } else {
             for (jsuint i = 0; i < length; i++) {
-                if (vector[i] == JSVAL_HOLE)
+                if (vector[i].isMagic(JS_ARRAY_HOLE))
                     --count;
-                obj->dslots[i] = vector[i];
+                obj->dslots[i].copy(vector[i]);
             }
         }
         obj->setArrayCount(count);
     } else {
         obj->setArrayCount(0);
     }
     return JS_TRUE;
 }
@@ -1773,41 +1594,39 @@ Array_p_toString(JSContext* cx, JSObject
     return JSVAL_TO_STRING(tvr.value());
 }
 #endif
 
 /*
  * Perl-inspired join, reverse, and sort.
  */
 static JSBool
-array_join(JSContext *cx, uintN argc, jsval *vp)
+array_join(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *str;
-    JSObject *obj;
-
-    if (argc == 0 || JSVAL_IS_VOID(vp[2])) {
+    if (argc == 0 || vp[2].isUndefined()) {
         str = NULL;
     } else {
         str = js_ValueToString(cx, vp[2]);
         if (!str)
             return JS_FALSE;
-        vp[2] = STRING_TO_JSVAL(str);
+        vp[2].setString(str);
     }
-    obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
     return obj && array_toString_sub(cx, obj, JS_FALSE, str, vp);
 }
 
 static JSBool
-array_reverse(JSContext *cx, uintN argc, jsval *vp)
+array_reverse(JSContext *cx, uintN argc, Value *vp)
 {
     jsuint len;
-    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &len))
         return JS_FALSE;
-    *vp = OBJECT_TO_JSVAL(obj);
+    SetObject(vp, *obj);
 
     if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) {
         /* An empty array or an array with no elements is already reversed. */
         if (len == 0 || !obj->dslots)
             return JS_TRUE;
 
         /*
          * It's actually surprisingly complicated to reverse an array due to the
@@ -1816,23 +1635,20 @@ array_reverse(JSContext *cx, uintN argc,
          * be a common operation than other array mass-mutation methods, so for
          * now just take a probably-small memory hit (in the absence of too many
          * holes in the array at its start) and ensure that the capacity is
          * sufficient to hold all the elements in the array if it were full.
          */
         if (!EnsureCapacity(cx, obj, len))
             return JS_FALSE;
 
-        jsval* lo = &obj->dslots[0];
-        jsval* hi = &obj->dslots[len - 1];
-        for (; lo < hi; lo++, hi--) {
-             jsval tmp = *lo;
-             *lo = *hi;
-             *hi = tmp;
-        }
+        Value* lo = &obj->dslots[0];
+        Value* hi = &obj->dslots[len - 1];
+        for (; lo < hi; lo++, hi--)
+            lo->swap(*hi);
 
         /*
          * Per ECMA-262, don't update the length of the array, even if the new
          * array has trailing holes (and thus the original array began with
          * holes).
          */
         return JS_TRUE;
     }
@@ -1843,58 +1659,58 @@ array_reverse(JSContext *cx, uintN argc,
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetArrayElement(cx, obj, i, &hole, tvr.addr()) ||
             !GetArrayElement(cx, obj, len - i - 1, &hole2, vp) ||
             !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.value()) ||
             !SetOrDeleteArrayElement(cx, obj, i, hole2, *vp)) {
             return false;
         }
     }
-    *vp = OBJECT_TO_JSVAL(obj);
+    JS_ASSERT(obj == &vp->asObject());
     return true;
 }
 
 typedef struct MSortArgs {
     size_t       elsize;
     JSComparator cmp;
     void         *arg;
-    JSBool       fastcopy;
+    JSBool       isValue;
 } MSortArgs;
 
 /* Helper function for js_MergeSort. */
 static JSBool
 MergeArrays(MSortArgs *msa, void *src, void *dest, size_t run1, size_t run2)
 {
     void *arg, *a, *b, *c;
     size_t elsize, runtotal;
     int cmp_result;
     JSComparator cmp;
-    JSBool fastcopy;
+    JSBool isValue;
 
     runtotal = run1 + run2;
 
     elsize = msa->elsize;
     cmp = msa->cmp;
     arg = msa->arg;
-    fastcopy = msa->fastcopy;
+    isValue = msa->isValue;
 
 #define CALL_CMP(a, b) \
     if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE;
 
     /* Copy runs already in sorted order. */
     b = (char *)src + run1 * elsize;
     a = (char *)b - elsize;
     CALL_CMP(a, b);
     if (cmp_result <= 0) {
         memcpy(dest, src, runtotal * elsize);
         return JS_TRUE;
     }
 
 #define COPY_ONE(p,q,n) \
-    (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n))
+    (isValue ? (((Value*)p)->copy(*((Value*)q))) : (void)memcpy(p, q, n))
 
     a = src;
     c = dest;
     for (; runtotal != 0; runtotal--) {
         JSBool from_a = run2 == 0;
         if (!from_a && run1 != 0) {
             CALL_CMP(a,b);
             from_a = cmp_result <= 0;
@@ -1916,31 +1732,28 @@ MergeArrays(MSortArgs *msa, void *src, v
 
     return JS_TRUE;
 }
 
 /*
  * This sort is stable, i.e. sequence of equal elements is preserved.
  * See also bug #224128.
  */
-JSBool
+bool
 js_MergeSort(void *src, size_t nel, size_t elsize,
-             JSComparator cmp, void *arg, void *tmp)
+             JSComparator cmp, void *arg, void *tmp, JSBool isValue)
 {
     void *swap, *vec1, *vec2;
     MSortArgs msa;
     size_t i, j, lo, hi, run;
-    JSBool fastcopy;
     int cmp_result;
 
     /* Avoid memcpy overhead for word-sized and word-aligned elements. */
-    fastcopy = (elsize == sizeof(jsval) &&
-                (((jsuword) src | (jsuword) tmp) & JSVAL_ALIGN) == 0);
 #define COPY_ONE(p,q,n) \
-    (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n))
+    (isValue ? (((Value*)p)->copy(*(Value*)q)) : (void)memcpy(p, q, n))
 #define CALL_CMP(a, b) \
     if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE;
 #define INS_SORT_INT 4
 
     /*
      * Apply insertion sort to small chunks to reduce the number of merge
      * passes needed.
      */
@@ -1968,17 +1781,17 @@ js_MergeSort(void *src, size_t nel, size
         }
     }
 #undef CALL_CMP
 #undef COPY_ONE
 
     msa.elsize = elsize;
     msa.cmp = cmp;
     msa.arg = arg;
-    msa.fastcopy = fastcopy;
+    msa.isValue = isValue;
 
     vec1 = src;
     vec2 = tmp;
     for (run = INS_SORT_INT; run < nel; run *= 2) {
         for (lo = 0; lo < nel; lo += 2 * run) {
             hi = lo + run;
             if (hi >= nel) {
                 memcpy((char *)vec2 + lo * elsize, (char *)vec1 + lo * elsize,
@@ -1999,53 +1812,53 @@ js_MergeSort(void *src, size_t nel, size
         memcpy(src, tmp, nel * elsize);
 
     return JS_TRUE;
 }
 
 struct CompareArgs
 {
     JSContext       *context;
-    jsval           fval;
+    Value           fval;
     InvokeArgsGuard args;
 
-    CompareArgs(JSContext *cx, jsval fval)
+    CompareArgs(JSContext *cx, const Value &fval)
       : context(cx), fval(fval)
     {}
 };
 
 static JS_REQUIRES_STACK JSBool
 sort_compare(void *arg, const void *a, const void *b, int *result)
 {
-    jsval av = *(const jsval *)a, bv = *(const jsval *)b;
+    const Value *av = (const Value *)a, *bv = (const Value *)b;
     CompareArgs *ca = (CompareArgs *) arg;
     JSContext *cx = ca->context;
 
     /*
      * array_sort deals with holes and undefs on its own and they should not
      * come here.
      */
-    JS_ASSERT(!JSVAL_IS_VOID(av));
-    JS_ASSERT(!JSVAL_IS_VOID(bv));
+    JS_ASSERT(!av->isMagic() && !av->isUndefined());
+    JS_ASSERT(!av->isMagic() && !bv->isUndefined());
 
     if (!JS_CHECK_OPERATION_LIMIT(cx))
         return JS_FALSE;
 
-    jsval *invokevp = ca->args.getvp();
-    jsval *sp = invokevp;
-    *sp++ = ca->fval;
-    *sp++ = JSVAL_NULL;
-    *sp++ = av;
-    *sp++ = bv;
-
-    if (!js_Invoke(cx, ca->args, 0))
+    Value *invokevp = ca->args.getvp();
+    Value *sp = invokevp;
+    sp++->copy(ca->fval);
+    sp++->setNull();
+    sp++->copy(*av);
+    sp++->copy(*bv);
+
+    if (!Invoke(cx, ca->args, 0))
         return JS_FALSE;
 
     jsdouble cmp;
-    if (!ValueToNumber(cx, *invokevp, &cmp))
+    if (!ValueToNumber(cx, invokevp, &cmp))
         return JS_FALSE;
 
     /* Clamp cmp to -1, 0, 1. */
     *result = 0;
     if (!JSDOUBLE_IS_NaN(cmp) && cmp != 0)
         *result = cmp > 0 ? 1 : -1;
 
     /*
@@ -2064,97 +1877,89 @@ static inline JS_IGNORE_STACK JSComparat
 comparator_stack_cast(JSRedComparator func)
 {
     return func;
 }
 
 static int
 sort_compare_strings(void *arg, const void *a, const void *b, int *result)
 {
-    jsval av = *(const jsval *)a, bv = *(const jsval *)b;
-
-    JS_ASSERT(JSVAL_IS_STRING(av));
-    JS_ASSERT(JSVAL_IS_STRING(bv));
+    const Value *av = (const Value *)a, *bv = (const Value *)b;
+
+    JS_ASSERT(av->isString());
+    JS_ASSERT(bv->isString());
     if (!JS_CHECK_OPERATION_LIMIT((JSContext *)arg))
         return JS_FALSE;
 
-    *result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv));
+    *result = (int) js_CompareStrings(av->asString(), bv->asString());
     return JS_TRUE;
 }
 
-/*
- * The array_sort function below assumes JSVAL_NULL is zero in order to
- * perform initialization using memset.  Other parts of SpiderMonkey likewise
- * "know" that JSVAL_NULL is zero; this static assertion covers all cases.
- */
-JS_STATIC_ASSERT(JSVAL_NULL == 0);
-
 static JSBool
-array_sort(JSContext *cx, uintN argc, jsval *vp)
+array_sort(JSContext *cx, uintN argc, Value *vp)
 {
-    jsval fval;
     jsuint len, newlen, i, undefs;
     size_t elemsize;
     JSString *str;
 
-    jsval *argv = JS_ARGV(cx, vp);
+    Value *argv = JS_ARGV(cx, vp);
+    Value fval;
     if (argc > 0) {
-        if (JSVAL_IS_PRIMITIVE(argv[0])) {
+        if (argv[0].isPrimitive()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SORT_ARG);
             return false;
         }
-        fval = argv[0];     /* non-default compare function */
+        fval.copy(argv[0]);     /* non-default compare function */
     } else {
-        fval = JSVAL_NULL;
+        fval.setNull();
     }
 
-    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &len))
         return false;
     if (len == 0) {
-        *vp = OBJECT_TO_JSVAL(obj);
+        SetObject(vp, *obj);
         return true;
     }
 
     /*
-     * We need a temporary array of 2 * len jsvals to hold the array elements
+     * 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.
      */
 #if JS_BITS_PER_WORD == 32
-    if (size_t(len) > size_t(-1) / (2 * sizeof(jsval))) {
+    if (size_t(len) > size_t(-1) / (2 * sizeof(Value))) {
         js_ReportAllocationOverflow(cx);
         return false;
     }
 #endif
 
     /*
      * Initialize vec as a root. We will clear elements of vec one by
      * one while increasing the rooted amount of vec when we know that the
      * property at the corresponding index exists and its value must be rooted.
      *
      * In this way when sorting a huge mostly sparse array we will not
      * access the tail of vec corresponding to properties that do not
      * exist, allowing OS to avoiding committing RAM. See bug 330812.
-     *
-     * After this point control must flow through label out: to exit.
      */
     {
-        jsval *vec = (jsval *) cx->malloc(2 * size_t(len) * sizeof(jsval));
+        Value *vec = (Value *) cx->malloc(2 * size_t(len) * sizeof(Value));
         if (!vec)
             return false;
 
-        struct AutoFreeVector {
-            AutoFreeVector(JSContext *cx, jsval *&vec) : cx(cx), vec(vec) { }
+        DEFINE_LOCAL_CLASS_OF_STATIC_FUNCTION(AutoFreeVector) {
+            JSContext *const cx;
+            Value *&vec;
+           public:
+            AutoFreeVector(JSContext *cx, Value *&vec) : cx(cx), vec(vec) { }
             ~AutoFreeVector() {
                 cx->free(vec);
             }
-            JSContext * const cx;
-            jsval *&vec;
         } free(cx, vec);
 
         AutoArrayRooter tvr(cx, 0, vec);
 
         /*
          * By ECMA 262, 15.4.4.11, a property that does not exist (which we
          * call a "hole") is always greater than an existing property with
          * value undefined and that is always greater than any other property.
@@ -2166,138 +1971,137 @@ array_sort(JSContext *cx, uintN argc, js
         newlen = 0;
         bool allStrings = true;
         for (i = 0; i < len; i++) {
             if (!JS_CHECK_OPERATION_LIMIT(cx))
                 return false;
 
             /* Clear vec[newlen] before including it in the rooted set. */
             JSBool hole;
-            vec[newlen] = JSVAL_NULL;
+            vec[newlen].setNull();
             tvr.changeLength(newlen + 1);
             if (!GetArrayElement(cx, obj, i, &hole, &vec[newlen]))
                 return false;
 
             if (hole)
                 continue;
 
-            if (JSVAL_IS_VOID(vec[newlen])) {
+            if (vec[newlen].isUndefined()) {
                 ++undefs;
                 continue;
             }
 
-            allStrings = allStrings && JSVAL_IS_STRING(vec[newlen]);
+            allStrings = allStrings && vec[newlen].isString();
 
             ++newlen;
         }
 
         if (newlen == 0)
             return true; /* The array has only holes and undefs. */
 
         /*
          * The first newlen elements of vec are copied from the array object
          * (above). The remaining newlen positions are used as GC-rooted scratch
          * space for mergesort. We must clear the space before including it to
-         * the root set covered by tvr.count. We assume JSVAL_NULL==0 to optimize
-         * initialization using memset.
+         * the root set covered by tvr.count.
          */
-        jsval *mergesort_tmp = vec + newlen;
+        Value *mergesort_tmp = vec + newlen;
         PodZero(mergesort_tmp, newlen);
         tvr.changeLength(newlen * 2);
 
         /* Here len == 2 * (newlen + undefs + number_of_holes). */
-        if (fval == JSVAL_NULL) {
+        if (fval.isNull()) {
             /*
              * Sort using the default comparator converting all elements to
              * strings.
              */
             if (allStrings) {
-                elemsize = sizeof(jsval);
+                elemsize = sizeof(Value);
             } else {
                 /*
                  * To avoid string conversion on each compare we do it only once
                  * prior to sorting. But we also need the space for the original
                  * values to recover the sorting result. To reuse
                  * sort_compare_strings we move the original values to the odd
                  * indexes in vec, put the string conversion results in the even
-                 * indexes and pass 2 * sizeof(jsval) as an element size to the
+                 * indexes and pass 2 * sizeof(Value) as an element size to the
                  * sorting function. In this way sort_compare_strings will only
                  * see the string values when it casts the compare arguments as
-                 * pointers to jsval.
+                 * pointers to Value.
                  *
                  * This requires doubling the temporary storage including the
                  * scratch space for the merge sort. Since vec already contains
                  * the rooted scratch space for newlen elements at the tail, we
                  * can use it to rearrange and convert to strings first and try
                  * realloc only when we know that we successfully converted all
                  * the elements.
                  */
 #if JS_BITS_PER_WORD == 32
-                if (size_t(newlen) > size_t(-1) / (4 * sizeof(jsval))) {
+                if (size_t(newlen) > size_t(-1) / (4 * sizeof(Value))) {
                     js_ReportAllocationOverflow(cx);
                     return false;
                 }
 #endif
 
                 /*
                  * Rearrange and string-convert the elements of the vector from
                  * the tail here and, after sorting, move the results back
                  * starting from the start to prevent overwrite the existing
                  * elements.
                  */
                 i = newlen;
                 do {
                     --i;
                     if (!JS_CHECK_OPERATION_LIMIT(cx))
                         return false;
-                    jsval v = vec[i];
+                    const Value &v = vec[i];
                     str = js_ValueToString(cx, v);
                     if (!str)
                         return false;
-                    vec[2 * i] = STRING_TO_JSVAL(str);
-                    vec[2 * i + 1] = v;
+                    vec[2 * i].setString(str);
+                    vec[2 * i + 1].copy(v);
                 } while (i != 0);
 
                 JS_ASSERT(tvr.array == vec);
-                vec = (jsval *) cx->realloc(vec, 4 * size_t(newlen) * sizeof(jsval));
+                vec = (Value *) cx->realloc(vec, 4 * size_t(newlen) * sizeof(Value));
                 if (!vec) {
-                    vec = tvr.array;
+                    vec = tvr.array;  /* N.B. AutoFreeVector */
                     return false;
                 }
                 mergesort_tmp = vec + 2 * newlen;
-                PodZero(mergesort_tmp, newlen * 2);
+                PodZero(mergesort_tmp, 2 * newlen);
                 tvr.changeArray(vec, newlen * 4);
-                elemsize = 2 * sizeof(jsval);
+                elemsize = 2 * sizeof(Value);
             }
             if (!js_MergeSort(vec, size_t(newlen), elemsize,
-                              sort_compare_strings, cx, mergesort_tmp)) {
+                              sort_compare_strings, cx, mergesort_tmp, true)) {
                 return false;
             }
             if (!allStrings) {
                 /*
                  * We want to make the following loop fast and to unroot the
                  * cached results of toString invocations before the operation
                  * callback has a chance to run the GC. For this reason we do
                  * not call JS_CHECK_OPERATION_LIMIT in the loop.
                  */
                 i = 0;
                 do {
-                    vec[i] = vec[2 * i + 1];
+                    vec[i].copy(vec[2 * i + 1]);
                 } while (++i != newlen);
             }
         } else {
             LeaveTrace(cx);
 
             CompareArgs ca(cx, fval);
             if (!cx->stack().pushInvokeArgs(cx, 2, ca.args))
                 return false;
 
-            if (!js_MergeSort(vec, size_t(newlen), sizeof(jsval),
+            if (!js_MergeSort(vec, size_t(newlen), sizeof(Value),
                               comparator_stack_cast(sort_compare),
-                              &ca, mergesort_tmp)) {
+                              &ca, mergesort_tmp, true)) {
                 return false;
             }
         }
 
         /*
          * We no longer need to root the scratch space for the merge sort, so
          * unroot it now to make the job of a potential GC under
          * InitArrayElements easier.
@@ -2307,73 +2111,77 @@ array_sort(JSContext *cx, uintN argc, js
                                SourceVectorAllValues)) {
             return false;
         }
     }
 
     /* Set undefs that sorted after the rest of elements. */
     while (undefs != 0) {
         --undefs;
-        if (!JS_CHECK_OPERATION_LIMIT(cx) || !SetArrayElement(cx, obj, newlen++, JSVAL_VOID))
+        if (!JS_CHECK_OPERATION_LIMIT(cx) ||
+            !SetArrayElement(cx, obj, newlen++, sUndefinedValue)) {
             return false;
+        }
     }
 
     /* 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;
     }
-    *vp = OBJECT_TO_JSVAL(obj);
+    SetObject(vp, *obj);
     return true;
 }
 
 /*
  * Perl-inspired push, pop, shift, unshift, and splice methods.
  */
 static JSBool
-array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
     jsuint length;
 
     if (!js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
     if (!InitArrayElements(cx, obj, length, argc, argv, TargetElementsMayContainValues,
                            SourceVectorAllValues)) {
         return JS_FALSE;
     }
 
     /* Per ECMA-262, return the new array length. */
     jsdouble newlength = length + jsdouble(argc);
-    if (!IndexToValue(cx, newlength, rval))
-        return JS_FALSE;
+    IndexToValue(cx, newlength, rval);
     return js_SetLengthProperty(cx, obj, newlength);
 }
 
 static JSBool
-array_push1_dense(JSContext* cx, JSObject* obj, jsval v, jsval *rval)
+array_push1_dense(JSContext* cx, JSObject* obj, const Value &v, Value *rval)
 {
     uint32 length = obj->getArrayLength();
     if (INDEX_TOO_SPARSE(obj, length)) {
         if (!js_MakeArraySlow(cx, obj))
             return JS_FALSE;
-        return array_push_slowly(cx, obj, 1, &v, rval);
+        Value tmp;
+        tmp.copy(v);
+        return array_push_slowly(cx, obj, 1, &tmp, rval);
     }
 
     if (!EnsureCapacity(cx, obj, length + 1))
         return JS_FALSE;
     obj->setArrayLength(length + 1);
 
-    JS_ASSERT(obj->dslots[length] == JSVAL_HOLE);
+    JS_ASSERT(obj->dslots[length].isMagic(JS_ARRAY_HOLE));
     obj->incArrayCountBy(1);
-    obj->dslots[length] = v;
-    return IndexToValue(cx, obj->getArrayLength(), rval);
+    obj->dslots[length].copy(v);
+    IndexToValue(cx, obj->getArrayLength(), rval);
+    return JS_TRUE;
 }
 
 JSBool JS_FASTCALL
-js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v)
+js_ArrayCompPush(JSContext *cx, JSObject *obj, const Value &v)
 {
     JS_ASSERT(obj->isDenseArray());
     uint32_t length = obj->getArrayLength();
     JS_ASSERT(length <= js_DenseArrayCapacity(obj));
 
     if (length == js_DenseArrayCapacity(obj)) {
         if (length > JS_ARGS_LENGTH_MAX) {
             JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
@@ -2381,17 +2189,17 @@ js_ArrayCompPush(JSContext *cx, JSObject
             return JS_FALSE;
         }
 
         if (!EnsureCapacity(cx, obj, length + 1))
             return JS_FALSE;
     }
     obj->setArrayLength(length + 1);
     obj->incArrayCountBy(1);
-    obj->dslots[length] = v;
+    obj->dslots[length].copy(v);
     return JS_TRUE;
 }
 JS_DEFINE_CALLINFO_3(extern, BOOL, js_ArrayCompPush, CONTEXT, OBJECT, JSVAL, 0,
                      nanojit::ACC_STORE_ANY)
 
 #ifdef JS_TRACER
 static jsval FASTCALL
 Array_p_push1(JSContext* cx, JSObject* obj, jsval v)
@@ -2403,61 +2211,59 @@ Array_p_push1(JSContext* cx, JSObject* o
         return tvr.value();
     }
     SetBuiltinError(cx);
     return JSVAL_VOID;
 }
 #endif
 
 static JSBool
-array_push(JSContext *cx, uintN argc, jsval *vp)
+array_push(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj;
-
     /* Insist on one argument and obj of the expected class. */
-    obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(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);
 }
 
 static JSBool
-array_pop_slowly(JSContext *cx, JSObject* obj, jsval *vp)
+array_pop_slowly(JSContext *cx, JSObject* obj, Value *vp)
 {
     jsuint index;
     JSBool hole;
 
     if (!js_GetLengthProperty(cx, obj, &index))
         return JS_FALSE;
     if (index == 0) {
-        *vp = JSVAL_VOID;
+        vp->setUndefined();
     } else {
         index--;
 
         /* Get the to-be-deleted property's value into vp. */
         if (!GetArrayElement(cx, obj, index, &hole, vp))
             return JS_FALSE;
         if (!hole && !DeleteArrayElement(cx, obj, index))
             return JS_FALSE;
     }
     return js_SetLengthProperty(cx, obj, index);
 }
 
 static JSBool
-array_pop_dense(JSContext *cx, JSObject* obj, jsval *vp)
+array_pop_dense(JSContext *cx, JSObject* obj, Value *vp)
 {
     jsuint index;
     JSBool hole;
 
     index = obj->getArrayLength();
     if (index == 0) {
-        *vp = JSVAL_VOID;
+        vp->setUndefined();
         return JS_TRUE;
     }
     index--;
     if (!GetArrayElement(cx, obj, index, &hole, vp))
         return JS_FALSE;
     if (!hole && !DeleteArrayElement(cx, obj, index))
         return JS_FALSE;
     obj->setArrayLength(index);
@@ -2475,61 +2281,58 @@ Array_p_pop(JSContext* cx, JSObject* obj
         return tvr.value();
     }
     SetBuiltinError(cx);
     return JSVAL_VOID;
 }
 #endif
 
 static JSBool
-array_pop(JSContext *cx, uintN argc, jsval *vp)
+array_pop(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj;
-
-    obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(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, jsval *vp)
+array_shift(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj;
     jsuint length, i;
     JSBool hole;
 
-    obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
     if (length == 0) {
-        *vp = JSVAL_VOID;
+        vp->setUndefined();
     } else {
         length--;
 
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
             length < js_DenseArrayCapacity(obj)) {
             if (JS_LIKELY(obj->dslots != NULL)) {
-                *vp = obj->dslots[0];
-                if (*vp == JSVAL_HOLE)
-                    *vp = JSVAL_VOID;
+                vp->copy(obj->dslots[0]);
+                if (vp->isMagic(JS_ARRAY_HOLE))
+                    vp->setUndefined();
                 else
                     obj->decArrayCountBy(1);
-                memmove(obj->dslots, obj->dslots + 1, length * sizeof(jsval));
-                obj->dslots[length] = JSVAL_HOLE;
+                memmove(obj->dslots, obj->dslots + 1, length * sizeof(Value));
+                obj->dslots[length].setMagic(JS_ARRAY_HOLE);
             } else {
                 /*
                  * We don't need to modify the indexed properties of an empty array
                  * with an explicitly set non-zero length when shift() is called on
                  * it, but note fallthrough to reduce the length by one.
                  */
                 JS_ASSERT(obj->getArrayCount() == 0);
-                *vp = JSVAL_VOID;
+                vp->setUndefined();
             }
         } else {
             /* Get the to-be-deleted property's value into vp ASAP. */
             if (!GetArrayElement(cx, obj, 0, &hole, vp))
                 return JS_FALSE;
 
             /* Slide down the array above the first element. */
             AutoValueRooter tvr(cx);
@@ -2545,40 +2348,39 @@ array_shift(JSContext *cx, uintN argc, j
             if (!hole && !DeleteArrayElement(cx, obj, length))
                 return JS_FALSE;
         }
     }
     return js_SetLengthProperty(cx, obj, length);
 }
 
 static JSBool
-array_unshift(JSContext *cx, uintN argc, jsval *vp)
+array_unshift(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj;
-    jsval *argv;
+    Value *argv;
     jsuint length;
     JSBool hole;
     jsdouble last, newlen;
 
-    obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(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) &&
                 !INDEX_TOO_SPARSE(obj, unsigned(newlen + argc))) {
                 JS_ASSERT(newlen + argc == length + argc);
                 if (!EnsureCapacity(cx, obj, length + argc))
                     return JS_FALSE;
-                memmove(obj->dslots + argc, obj->dslots, length * sizeof(jsval));
+                memmove(obj->dslots + argc, obj->dslots, length * sizeof(Value));
                 for (uint32 i = 0; i < argc; i++)
-                    obj->dslots[i] = JSVAL_HOLE;
+                    obj->dslots[i].setMagic(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()) ||
@@ -2594,50 +2396,48 @@ array_unshift(JSContext *cx, uintN argc,
             return JS_FALSE;
 
         newlen += argc;
         if (!js_SetLengthProperty(cx, obj, newlen))
             return JS_FALSE;
     }
 
     /* Follow Perl by returning the new array length. */
-    return IndexToValue(cx, newlen, vp);
+    IndexToValue(cx, newlen, vp);
+    return JS_TRUE;
 }
 
 static JSBool
-array_splice(JSContext *cx, uintN argc, jsval *vp)
+array_splice(JSContext *cx, uintN argc, Value *vp)
 {
-    jsval *argv;
-    JSObject *obj;
     jsuint length, begin, end, count, delta, last;
     JSBool hole;
-    JSObject *obj2;
 
     /*
      * Create a new array value to return.  Our ECMA v2 proposal specs
      * that splice always returns an array value, even when given no
      * arguments.  We think this is best because it eliminates the need
      * for callers to do an extra test to handle the empty splice case.
      */
-    obj2 = js_NewArrayObject(cx, 0, NULL);
+    JSObject *obj2 = js_NewArrayObject(cx, 0, NULL);
     if (!obj2)
         return JS_FALSE;
-    *vp = OBJECT_TO_JSVAL(obj2);
+    vp->setNonFunObj(*obj2);
 
     /* Nothing to do if no args.  Otherwise get length. */
     if (argc == 0)
         return JS_TRUE;
-    argv = JS_ARGV(cx, vp);
-    obj = JS_THIS_OBJECT(cx, vp);
+    Value *argv = JS_ARGV(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(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))
+    if (!ValueToNumber(cx, argv, &d))
         return JS_FALSE;
     d = js_DoubleToInteger(d);
     if (d < 0) {
         d += length;
         if (d < 0)
             d = 0;
     } else if (d > length) {
         d = length;
@@ -2647,30 +2447,30 @@ array_splice(JSContext *cx, uintN argc, 
     argv++;
 
     /* Convert the second argument from a count into a fencepost index. */
     delta = length - begin;
     if (argc == 0) {
         count = delta;
         end = length;
     } else {
-        if (!ValueToNumber(cx, *argv, &d))
+        if (!ValueToNumber(cx, argv, &d))
             return JS_FALSE;
         d = js_DoubleToInteger(d);
         if (d < 0)
             d = 0;
         else if (d > delta)
             d = delta;
         count = (jsuint)d;
         end = begin + count;
         argc--;
         argv++;
     }
 
-    AutoValueRooter tvr(cx, JSVAL_NULL);
+    AutoValueRooter tvr(cx, NullValue());
 
     /* 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 <= js_DenseArrayCapacity(obj)) {
             if (!InitArrayObject(cx, obj2, count, obj->dslots + begin,
                                  obj->getArrayCount() != obj->getArrayLength())) {
@@ -2694,26 +2494,26 @@ array_splice(JSContext *cx, uintN argc, 
     }
 
     /* Find the direction (up or down) to copy and make way for argv. */
     if (argc > count) {
         delta = (jsuint)argc - count;
         last = length;
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
             length <= js_DenseArrayCapacity(obj) &&
-            (length == 0 || obj->dslots[length - 1] != JSVAL_HOLE)) {
+            (length == 0 || !obj->dslots[length - 1].isMagic(JS_ARRAY_HOLE))) {
             if (!EnsureCapacity(cx, obj, length + delta))
                 return JS_FALSE;
             /* (uint) end could be 0, so we can't use a vanilla >= test. */
             while (last-- > end) {
-                jsval srcval = obj->dslots[last];
-                jsval* dest = &obj->dslots[last + delta];
-                if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE)
+                const Value &src = obj->dslots[last];
+                Value &dst = obj->dslots[last + delta];
+                if (dst.isMagic(JS_ARRAY_HOLE) && !src.isMagic(JS_ARRAY_HOLE))
                     obj->incArrayCountBy(1);
-                *dest = srcval;
+                dst.copy(src);
             }
             obj->setArrayLength(obj->getArrayLength() + delta);
         } else {
             /* (uint) end could be 0, so we can't use a vanilla >= test. */
             while (last-- > end) {
                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                     !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
                     !SetOrDeleteArrayElement(cx, obj, last + delta, hole, tvr.value())) {
@@ -2723,21 +2523,21 @@ array_splice(JSContext *cx, uintN argc, 
         }
         length += delta;
     } else if (argc < count) {
         delta = count - (jsuint)argc;
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
             length <= js_DenseArrayCapacity(obj)) {
             /* (uint) end could be 0, so we can't use a vanilla >= test. */
             for (last = end; last < length; last++) {
-                jsval srcval = obj->dslots[last];
-                jsval* dest = &obj->dslots[last - delta];
-                if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE)
+                const Value &src = obj->dslots[last];
+                Value &dst = obj->dslots[last - delta];
+                if (dst.isMagic(JS_ARRAY_HOLE) && !src.isMagic(JS_ARRAY_HOLE))
                     obj->incArrayCountBy(1);
-                *dest = srcval;
+                dst.copy(src);
             }
         } else {
             for (last = end; last < length; last++) {
                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                     !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
                     !SetOrDeleteArrayElement(cx, obj, last - delta, hole, tvr.value())) {
                     return JS_FALSE;
                 }
@@ -2754,79 +2554,75 @@ array_splice(JSContext *cx, uintN argc, 
                              SourceVectorAllValues) &&
            js_SetLengthProperty(cx, obj, length);
 }
 
 /*
  * Python-esque sequence operations.
  */
 static JSBool
-array_concat(JSContext *cx, uintN argc, jsval *vp)
+array_concat(JSContext *cx, uintN argc, Value *vp)
 {
-    jsval *argv, v;
-    JSObject *aobj, *nobj;
-    jsuint length, alength, slot;
-    uintN i;
-    JSBool hole;
-
     /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
-    argv = JS_ARGV(cx, vp) - 1;
-    JS_ASSERT(JS_THIS_OBJECT(cx, vp) == JSVAL_TO_OBJECT(argv[0]));
+    Value *p = JS_ARGV(cx, vp) - 1;
 
     /* Create a new Array object and root it using *vp. */
-    aobj = JS_THIS_OBJECT(cx, vp);
+    JSObject *aobj = ComputeThisObjectFromVp(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
          * where length is <= capacity, nobj and aobj will have the same
          * capacity.
          */
         length = aobj->getArrayLength();
         jsuint capacity = js_DenseArrayCapacity(aobj);
         nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->dslots,
                                  aobj->getArrayCount() != length);
         if (!nobj)
             return JS_FALSE;
         nobj->setArrayLength(length);
-        *vp = OBJECT_TO_JSVAL(nobj);
+        vp->setNonFunObj(*nobj);
         if (argc == 0)
             return JS_TRUE;
         argc--;
-        argv++;
+        p++;
     } else {
         nobj = js_NewArrayObject(cx, 0, NULL);
         if (!nobj)
             return JS_FALSE;
-        *vp = OBJECT_TO_JSVAL(nobj);
+        vp->setNonFunObj(*nobj);
         length = 0;
     }
 
-    AutoValueRooter tvr(cx, JSVAL_NULL);
+    AutoValueRooter tvr(cx, NullValue());
 
     /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
-    for (i = 0; i <= argc; i++) {
+    for (uintN i = 0; i <= argc; i++) {
         if (!JS_CHECK_OPERATION_LIMIT(cx))
             return false;
-        v = argv[i];
-        if (!JSVAL_IS_PRIMITIVE(v)) {
+        const Value &v = p[i];
+        if (v.isObject()) {
             JSObject *wobj;
 
-            aobj = JSVAL_TO_OBJECT(v);
+            aobj = &v.asObject();
             wobj = js_GetWrappedObject(cx, aobj);
             if (wobj->isArray()) {
                 jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
                 if (!aobj->getProperty(cx, id, tvr.addr()))
                     return false;
-                alength = ValueIsLength(cx, tvr.addr());
-                if (JSVAL_IS_NULL(tvr.value()))
+                jsuint alength = ValueIsLength(cx, tvr.addr());
+                if (tvr.value().isNull())
                     return false;
-                for (slot = 0; slot < alength; slot++) {
+                for (jsuint slot = 0; slot < alength; slot++) {
+                    JSBool hole;
                     if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                         !GetArrayElement(cx, aobj, slot, &hole, tvr.addr())) {
                         return false;
                     }
 
                     /*
                      * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent
                      * properties.
@@ -2845,47 +2641,47 @@ array_concat(JSContext *cx, uintN argc, 
             return false;
         length++;
     }
 
     return js_SetLengthProperty(cx, nobj, length);
 }
 
 static JSBool
-array_slice(JSContext *cx, uintN argc, jsval *vp)
+array_slice(JSContext *cx, uintN argc, Value *vp)
 {
-    jsval *argv;
-    JSObject *nobj, *obj;
+    Value *argv;
+    JSObject *nobj;
     jsuint length, begin, end, slot;
     JSBool hole;
 
     argv = JS_ARGV(cx, vp);
 
-    obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(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))
+        if (!ValueToNumber(cx, &argv[0], &d))
             return JS_FALSE;
         d = js_DoubleToInteger(d);
         if (d < 0) {
             d += length;
             if (d < 0)
                 d = 0;
         } else if (d > length) {
             d = length;
         }
         begin = (jsuint)d;
 
         if (argc > 1) {
-            if (!ValueToNumber(cx, argv[1], &d))
+            if (!ValueToNumber(cx, &argv[1], &d))
                 return JS_FALSE;
             d = js_DoubleToInteger(d);
             if (d < 0) {
                 d += length;
                 if (d < 0)
                     d = 0;
             } else if (d > length) {
                 d = length;
@@ -2898,25 +2694,25 @@ array_slice(JSContext *cx, uintN argc, j
         begin = end;
 
     if (obj->isDenseArray() && end <= js_DenseArrayCapacity(obj) &&
         !js_PrototypeHasIndexedProperties(cx, obj)) {
         nobj = js_NewArrayObject(cx, end - begin, obj->dslots + begin,
                                  obj->getArrayCount() != obj->getArrayLength());
         if (!nobj)
             return JS_FALSE;
-        *vp = OBJECT_TO_JSVAL(nobj);
+        vp->setNonFunObj(*nobj);
         return JS_TRUE;
     }
 
     /* Create a new Array object and root it using *vp. */
     nobj = js_NewArrayObject(cx, 0, NULL);
     if (!nobj)
         return JS_FALSE;
-    *vp = OBJECT_TO_JSVAL(nobj);
+    vp->setNonFunObj(*nobj);
 
     AutoValueRooter tvr(cx);
     for (slot = begin; slot < end; slot++) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetArrayElement(cx, obj, slot, &hole, tvr.addr())) {
             return JS_FALSE;
         }
         if (!hole && !SetArrayElement(cx, nobj, slot - begin, tvr.value()))
@@ -2924,38 +2720,37 @@ array_slice(JSContext *cx, uintN argc, j
     }
 
     return js_SetLengthProperty(cx, nobj, end - begin);
 }
 
 #if JS_HAS_ARRAY_EXTRAS
 
 static JSBool
-array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, jsval *vp)
+array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp)
 {
-    JSObject *obj;
     jsuint length, i, stop;
-    jsval tosearch;
+    Value tosearch;
     jsint direction;
     JSBool hole;
 
-    obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(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] : JSVAL_VOID;
+        tosearch.copy((argc != 0) ? vp[2] : sUndefinedValue);
     } else {
         jsdouble start;
 
-        tosearch = vp[2];
-        if (!ValueToNumber(cx, vp[3], &start))
+        tosearch.copy(vp[2]);
+        if (!ValueToNumber(cx, &vp[3], &start))
             return JS_FALSE;
         start = js_DoubleToInteger(start);
         if (start < 0) {
             start += length;
             if (start < 0) {
                 if (isLast)
                     goto not_found;
                 i = 0;
@@ -2979,36 +2774,38 @@ array_indexOfHelper(JSContext *cx, JSBoo
         direction = 1;
     }
 
     for (;;) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetArrayElement(cx, obj, (jsuint)i, &hole, vp)) {
             return JS_FALSE;
         }
-        if (!hole && js_StrictlyEqual(cx, *vp, tosearch))
-            return js_NewNumberInRootedValue(cx, i, vp);
+        if (!hole && StrictlyEqual(cx, *vp, tosearch)) {
+            Uint32ToValue(i, vp);
+            return JS_TRUE;
+        }
         if (i == stop)
             goto not_found;
         i += direction;
     }
 
   not_found:
-    *vp = INT_TO_JSVAL(-1);
+    vp->setInt32(-1);
     return JS_TRUE;
 }
 
 static JSBool
-array_indexOf(JSContext *cx, uintN argc, jsval *vp)
+array_indexOf(JSContext *cx, uintN argc, Value *vp)
 {
     return array_indexOfHelper(cx, JS_FALSE, argc, vp);
 }
 
 static JSBool
-array_lastIndexOf(JSContext *cx, uintN argc, jsval *vp)
+array_lastIndexOf(JSContext *cx, uintN argc, Value *vp)
 {
     return array_indexOfHelper(cx, JS_TRUE, argc, vp);
 }
 
 /* Order is important; extras that take a predicate funarg must follow MAP. */
 typedef enum ArrayExtraMode {
     FOREACH,
     REDUCE,
@@ -3017,32 +2814,32 @@ typedef enum ArrayExtraMode {
     FILTER,
     SOME,
     EVERY
 } ArrayExtraMode;
 
 #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)
 
 static JSBool
-array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp)
+array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
 {
-    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    JSObject *obj = ComputeThisObjectFromVp(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.
      */
     if (argc == 0) {
-        js_ReportMissingArg(cx, vp, 0);
+        js_ReportMissingArg(cx, *vp, 0);
         return JS_FALSE;
     }
-    jsval *argv = vp + 2;
+    Value *argv = vp + 2;
     JSObject *callable = js_ValueToCallableObject(cx, &argv[0], JSV2F_SEARCH_STACK);
     if (!callable)
         return JS_FALSE;
 
     /*
      * Set our initial return condition, used for zero-length array cases
      * (and pre-size our map return to match our known length, for all cases).
      */
@@ -3060,17 +2857,17 @@ array_extra(JSContext *cx, ArrayExtraMod
         /* FALL THROUGH */
       case REDUCE:
         if (length == 0 && argc == 1) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_EMPTY_ARRAY_REDUCE);
             return JS_FALSE;
         }
         if (argc >= 2) {
-            *vp = argv[1];
+            vp->copy(argv[1]);
         } else {
             JSBool hole;
             do {
                 if (!GetArrayElement(cx, obj, start, &hole, vp))
                     return JS_FALSE;
                 start += step;
             } while (hole && start != end);
 
@@ -3082,37 +2879,37 @@ array_extra(JSContext *cx, ArrayExtraMod
         }
         break;
       case MAP:
       case FILTER:
         newlen = (mode == MAP) ? length : 0;
         newarr = js_NewArrayObject(cx, newlen, NULL);
         if (!newarr)
             return JS_FALSE;
-        *vp = OBJECT_TO_JSVAL(newarr);
+        vp->setNonFunObj(*newarr);
         break;
       case SOME:
-        *vp = JSVAL_FALSE;
+        vp->setBoolean(false);
         break;
       case EVERY:
-        *vp = JSVAL_TRUE;
+        vp->setBoolean(true);
         break;
       case FOREACH:
-        *vp = JSVAL_VOID;
+        vp->setUndefined();
         break;
     }
 
     if (length == 0)
         return JS_TRUE;
 
     JSObject *thisp;
     if (argc > 1 && !REDUCE_MODE(mode)) {
-        if (!js_ValueToObject(cx, argv[1], &thisp))
+        if (!js_ValueToObjectOrNull(cx, argv[1], &argv[1]))
             return JS_FALSE;
-        argv[1] = OBJECT_TO_JSVAL(thisp);
+        thisp = argv[1].asObjectOrNull();
     } else {
         thisp = NULL;
     }
 
     /*
      * For all but REDUCE, we call with 3 args (value, index, array). REDUCE
      * requires 4 args (accum, value, index, array).
      */
@@ -3121,151 +2918,154 @@ array_extra(JSContext *cx, ArrayExtraMod
 
     InvokeArgsGuard args;
     if (!cx->stack().pushInvokeArgs(cx, argc, args))
         return JS_FALSE;
 
     MUST_FLOW_THROUGH("out");
     JSBool ok = JS_TRUE;
     JSBool cond;
-    jsval *invokevp = args.getvp();
-
+    Value *invokevp = args.getvp();
+
+    Value calleev(callable);
+    Value thisv(thisp);
+    Value objv(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());
+             GetArrayElement(cx, &objv.asObject(), i, &hole, tvr.addr());
         if (!ok)
             goto out;
         if (hole)
             continue;
 
         /*
          * Push callable and 'this', then args. We must do this for every
          * iteration around the loop since js_Invoke uses invokevp[0] for return
          * value storage, while some native functions use invokevp[1] for local
          * rooting.
          */
-        jsval *sp = invokevp;
-        *sp++ = OBJECT_TO_JSVAL(callable);
-        *sp++ = OBJECT_TO_JSVAL(thisp);
+        Value *sp = invokevp;
+        sp++->copy(calleev);
+        sp++->copy(thisv);
         if (REDUCE_MODE(mode))
-            *sp++ = *vp;
-        *sp++ = tvr.value();
-        *sp++ = INT_TO_JSVAL(i);
-        *sp++ = OBJECT_TO_JSVAL(obj);
+            sp++->copy(*vp);
+        sp++->copy(tvr.value());
+        sp++->setInt32(i);
+        sp++->copy(objv);
 
         /* Do the call. */
-        ok = js_Invoke(cx, args, 0);
+        ok = Invoke(cx, args, 0);
         if (!ok)
             break;
 
         if (mode > MAP)
             cond = js_ValueToBoolean(*invokevp);
 #ifdef __GNUC__ /* quell GCC overwarning */
         else
             cond = JS_FALSE;
 #endif
 
         switch (mode) {
           case FOREACH:
             break;
           case REDUCE:
           case REDUCE_RIGHT:
-            *vp = *invokevp;
+            vp->copy(*invokevp);
             break;
           case MAP:
             ok = SetArrayElement(cx, newarr, i, *invokevp);
             if (!ok)
                 goto out;
             break;
           case FILTER:
             if (!cond)
                 break;
             /* The filter passed *args.getvp(), so push it onto our result. */
             ok = SetArrayElement(cx, newarr, newlen++, tvr.value());
             if (!ok)
                 goto out;
             break;
           case SOME:
             if (cond) {
-                *vp = JSVAL_TRUE;
+                vp->setBoolean(true);
                 goto out;
             }
             break;
           case EVERY:
             if (!cond) {
-                *vp = JSVAL_FALSE;
+                vp->setBoolean(false);
                 goto out;
             }
             break;
         }
     }
 
   out:
     if (ok && mode == FILTER)
         ok = js_SetLengthProperty(cx, newarr, newlen);
     return ok;
 }
 
 static JSBool
-array_forEach(JSContext *cx, uintN argc, jsval *vp)
+array_forEach(JSContext *cx, uintN argc, Value *vp)
 {
     return array_extra(cx, FOREACH, argc, vp);
 }
 
 static JSBool
-array_map(JSContext *cx, uintN argc, jsval *vp)
+array_map(JSContext *cx, uintN argc, Value *vp)
 {
     return array_extra(cx, MAP, argc, vp);
 }
 
 static JSBool
-array_reduce(JSContext *cx, uintN argc, jsval *vp)
+array_reduce(JSContext *cx, uintN argc, Value *vp)
 {
     return array_extra(cx, REDUCE, argc, vp);
 }
 
 static JSBool
-array_reduceRight(JSContext *cx, uintN argc, jsval *vp)
+array_reduceRight(JSContext *cx, uintN argc, Value *vp)
 {
     return array_extra(cx, REDUCE_RIGHT, argc, vp);
 }
 
 static JSBool
-array_filter(JSContext *cx, uintN argc, jsval *vp)
+array_filter(JSContext *cx, uintN argc, Value *vp)
 {
     return array_extra(cx, FILTER, argc, vp);
 }
 
 static JSBool
-array_some(JSContext *cx, uintN argc, jsval *vp)
+array_some(JSContext *cx, uintN argc, Value *vp)
 {
     return array_extra(cx, SOME, argc, vp);
 }
 
 static JSBool
-array_every(JSContext *cx, uintN argc, jsval *vp)
+array_every(JSContext *cx, uintN argc, Value *vp)
 {
     return array_extra(cx, EVERY, argc, vp);
 }
 #endif
 
 static JSBool
-array_isArray(JSContext *cx, uintN argc, jsval *vp)
+array_isArray(JSContext *cx, uintN argc, Value *vp)
 {
-    *vp = BOOLEAN_TO_JSVAL(argc > 0 &&
-                           !JSVAL_IS_PRIMITIVE(vp[2]) &&
-                           js_GetWrappedObject(cx, JSVAL_TO_OBJECT(vp[2]))->isArray());
+    vp->setBoolean(argc > 0 &&
+                   vp[2].isObject() &&
+                   js_GetWrappedObject(cx, &vp[2].asObject())->isArray());
     return JS_TRUE;
 }
 
 static JSPropertySpec array_props[] = {
     {js_length_str,   -1,   JSPROP_SHARED | JSPROP_PERMANENT,
-                            array_length_getter,    array_length_setter},
+     Jsvalify(array_length_getter), Jsvalify(array_length_setter)},
     {0,0,0,0,0}
 };
 
 JS_DEFINE_TRCINFO_1(array_toString,
     (2, (static, STRING_FAIL, Array_p_toString, CONTEXT, THIS,      0, nanojit::ACC_STORE_ANY)))
 JS_DEFINE_TRCINFO_1(array_join,
     (3, (static, STRING_FAIL, Array_p_join, CONTEXT, THIS, STRING,  0, nanojit::ACC_STORE_ANY)))
 JS_DEFINE_TRCINFO_1(array_push,
@@ -3310,41 +3110,41 @@ static JSFunctionSpec array_methods[] = 
 };
 
 static JSFunctionSpec array_static_methods[] = {
     JS_FN("isArray",            array_isArray,      1,0),
     JS_FS_END
 };
 
 JSBool
-js_Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+js_Array(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
     jsuint length;
-    const jsval *vector;
+    const Value *vector;
 
     /* If called without new, replace obj with a new Array object. */
     if (!JS_IsConstructing(cx)) {
         obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL);
         if (!obj)
             return JS_FALSE;
-        *rval = OBJECT_TO_JSVAL(obj);
+        rval->setNonFunObj(*obj);
     }
 
     if (argc == 0) {
         length = 0;
         vector = NULL;
     } else if (argc > 1) {
         length = (jsuint) argc;
         vector = argv;
-    } else if (!JSVAL_IS_NUMBER(argv[0])) {
+    } else if (!argv[0].isNumber()) {
         length = 1;
         vector = argv;
     } else {
         length = ValueIsLength(cx, &argv[0]);
-        if (JSVAL_IS_NULL(argv[0]))
+        if (argv[0].isNull())
             return JS_FALSE;
         vector = NULL;
     }
     return InitArrayObject(cx, obj, length, vector);
 }
 
 JSObject* JS_FASTCALL
 js_NewEmptyArray(JSContext* cx, JSObject* proto)
@@ -3352,23 +3152,22 @@ js_NewEmptyArray(JSContext* cx, JSObject
     JS_ASSERT(proto->isArray());
 
     JSObject* obj = js_NewGCObject(cx);
     if (!obj)
         return NULL;
 
     /* Initialize all fields of JSObject. */
     obj->map = const_cast<JSObjectMap *>(&SharedArrayMap);
-    obj->classword = jsuword(&js_ArrayClass);
-    obj->setProto(proto);
+    obj->initArrayClass();
+    obj->setProto(NonFunObjPtr(*proto));
     obj->setParent(proto->getParent());
 
     obj->setArrayLength(0);
-    obj->setArrayCount(0);
-    obj->voidArrayUnused();
+    obj->voidDenseArrayCount();
     obj->dslots = NULL;
     return obj;
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, nanojit::ACC_STORE_ANY)
 #endif
 
 JSObject* JS_FASTCALL
@@ -3401,40 +3200,40 @@ js_NewArrayWithSlots(JSContext* cx, JSOb
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewArrayWithSlots, CONTEXT, OBJECT, UINT32, 0,
                      nanojit::ACC_STORE_ANY)
 #endif
 
 JSObject *
 js_InitArrayClass(JSContext *cx, JSObject *obj)
 {
-    JSObject *proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1,
+    JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1,
                                    array_props, array_methods, NULL, array_static_methods);
 
     /* Initialize the Array prototype object so it gets a length property. */
     if (!proto || !InitArrayObject(cx, proto, 0, NULL))
         return NULL;
     return proto;
 }
 
 JSObject *
-js_NewArrayObject(JSContext *cx, jsuint length, const jsval *vector, bool holey)
+js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector, bool holey)
 {
     JSObject *obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL);
     if (!obj)
         return NULL;
 
     /*
      * If this fails, the global object was not initialized and its class does
      * not have JSCLASS_IS_GLOBAL.
      */
     JS_ASSERT(obj->getProto());
 
     {
-        AutoValueRooter tvr(cx, obj);
+        AutoObjectRooter tvr(cx, obj);
         if (!InitArrayObject(cx, obj, length, vector, holey))
             obj = NULL;
     }
 
     /* Set/clear newborn root, in case we lost it.  */
     cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj;
     return obj;
 }
@@ -3445,17 +3244,17 @@ js_NewSlowArrayObject(JSContext *cx)
     JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL);
     if (obj)
         obj->setArrayLength(0);
     return obj;
 }
 
 #ifdef DEBUG_ARRAYS
 JSBool
-js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
     uintN i;
     JSObject *array;
 
     for (i = 0; i < argc; i++) {
         char *bytes;
 
         bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[i],
@@ -3493,24 +3292,24 @@ js_CoerceArrayToCanvasImageData(JSObject
         return JS_FALSE;
 
     length = obj->getArrayLength();
     if (length < offset + count)
         return JS_FALSE;
 
     JSUint8 *dp = dest;
     for (uintN i = offset; i < offset+count; i++) {
-        jsval v = obj->dslots[i];
-        if (JSVAL_IS_INT(v)) {
-            jsint vi = JSVAL_TO_INT(v);
+        const Value &v = obj->dslots[i];
+        if (v.isInt32()) {
+            jsint vi = v.asInt32();
             if (jsuint(vi) > 255)
                 vi = (vi < 0) ? 0 : 255;
             *dp++ = JSUint8(vi);
-        } else if (JSVAL_IS_DOUBLE(v)) {
-            jsdouble vd = *JSVAL_TO_DOUBLE(v);
+        } else if (v.isDouble()) {
+            jsdouble vd = v.asDouble();
             if (!(vd >= 0)) /* Not < so that NaN coerces to 0 */
                 *dp++ = 0;
             else if (vd > 255)
                 *dp++ = 255;
             else {
                 jsdouble toTruncate = vd + 0.5;
                 JSUint8 val = JSUint8(toTruncate);
 
@@ -3537,23 +3336,23 @@ js_CoerceArrayToCanvasImageData(JSObject
             return JS_FALSE;
         }
     }
 
     return JS_TRUE;
 }
 
 JS_FRIEND_API(JSObject *)
-js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, jsval **vector)
+js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, Value **vector)
 {
     JSObject *obj = js_NewArrayObject(cx, capacity, NULL);
     if (!obj)
         return NULL;
 
-    AutoValueRooter tvr(cx, obj);
+    AutoObjectRooter tvr(cx, obj);
     if (!EnsureCapacity(cx, obj, capacity, JS_FALSE))
         obj = NULL;
 
     /* Set/clear newborn root, in case we lost it.  */
     cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj;
     if (!obj)
         return NULL;
 
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -41,24 +41,22 @@
 #define jsarray_h___
 /*
  * JS Array interface.
  */
 #include "jsprvtd.h"
 #include "jspubtd.h"
 #include "jsobj.h"
 
-JS_BEGIN_EXTERN_C
-
 #define ARRAY_CAPACITY_MIN      7
 
 extern JSBool
-js_IdIsIndex(jsval id, jsuint *indexp);
+js_IdIsIndex(jsid id, jsuint *indexp);
 
-extern JSClass js_ArrayClass, js_SlowArrayClass;
+extern js::Class js_ArrayClass, js_SlowArrayClass;
 
 inline bool
 JSObject::isDenseArray() const
 {
     return getClass() == &js_ArrayClass;
 }
 
 inline bool
@@ -107,38 +105,38 @@ js_InitContextBusyArrayTable(JSContext *
 /*
  * Creates a new array with the given length and proto (NB: NULL is not
  * translated to Array.prototype), with len slots preallocated.
  */
 extern JSObject * JS_FASTCALL
 js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len);
 
 extern JSObject *
-js_NewArrayObject(JSContext *cx, jsuint length, const jsval *vector, bool holey = false);
+js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector, bool holey = false);
 
 /* Create an array object that starts out already made slow/sparse. */
 extern JSObject *
 js_NewSlowArrayObject(JSContext *cx);
 
 extern JSBool
 js_MakeArraySlow(JSContext *cx, JSObject *obj);
 
 static JS_INLINE uint32
 js_DenseArrayCapacity(JSObject *obj)
 {
     JS_ASSERT(obj->isDenseArray());
-    return obj->dslots ? (uint32) obj->dslots[-1] : 0;
+    return obj->dslots ? obj->dslotLength() : 0;
 }
 
 static JS_INLINE void
 js_SetDenseArrayCapacity(JSObject *obj, uint32 capacity)
 {
     JS_ASSERT(obj->isDenseArray());
     JS_ASSERT(obj->dslots);
-    obj->dslots[-1] = (jsval) capacity;
+    obj->dslots[-1].setPrivateUint32(capacity);
 }
 
 extern JSBool
 js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
 
 extern JSBool
 js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length);
 
@@ -162,31 +160,33 @@ js_IsArrayLike(JSContext *cx, JSObject *
  * JS-specific merge sort function.
  */
 typedef JSBool (*JSComparator)(void *arg, const void *a, const void *b,
                                int *result);
 /*
  * NB: vec is the array to be sorted, tmp is temporary space at least as big
  * as vec. Both should be GC-rooted if appropriate.
  *
+ * isValue should true iff vec points to an array of js::Value
+ *
  * The sorted result is in vec. vec may be in an inconsistent state if the
  * comparator function cmp returns an error inside a comparison, so remember
  * to check the return value of this function.
  */
-extern JSBool
+extern bool
 js_MergeSort(void *vec, size_t nel, size_t elsize, JSComparator cmp,
-             void *arg, void *tmp);
+             void *arg, void *tmp, bool isValue);
 
 #ifdef DEBUG_ARRAYS
 extern JSBool
-js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
+js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, js::Value *argv, js::Value *rval);
 #endif
 
 extern JSBool JS_FASTCALL
-js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v);
+js_ArrayCompPush(JSContext *cx, JSObject *obj, const js::Value &v);
 
 /*
  * Fast dense-array-to-buffer conversion for use by canvas.
  *
  * If the array is a dense array, fill [offset..offset+count] values into
  * destination, assuming that types are consistent.  Return JS_TRUE if
  * successful, otherwise JS_FALSE -- note that the destination buffer may be
  * modified even if JS_FALSE is returned (e.g. due to finding an inappropriate
@@ -206,31 +206,29 @@ js_CoerceArrayToCanvasImageData(JSObject
 JSBool
 js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj);
 
 /*
  * Utility to access the value from the id returned by array_lookupProperty.
  */
 JSBool
 js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, JSProperty *prop,
-                             jsval *vp);
+                             js::Value *vp);
 
 /* Array constructor native. Exposed only so the JIT can know its address. */
 JSBool
-js_Array(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
+js_Array(JSContext* cx, JSObject* obj, uintN argc, js::Value* argv, js::Value* rval);
 
 /*
  * Friend api function that allows direct creation of an array object with a
  * given capacity.  Non-null return value means allocation of the internal
  * buffer for a capacity of at least |capacity| succeeded.  A pointer to the
  * first element of this internal buffer is returned in the |vector| out
  * parameter.  The caller promises to fill in the first |capacity| values
  * starting from that pointer immediately after this function returns and
  * without triggering GC (so this method is allowed to leave those
  * uninitialized) and to set them to non-JSVAL_HOLE values, so that the
  * resulting array has length and count both equal to |capacity|.
  */
 JS_FRIEND_API(JSObject *)
-js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, jsval **vector);
-
-JS_END_EXTERN_C
+js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, js::Value **vector);
 
 #endif /* jsarray_h___ */
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -32,16 +32,18 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#define __STDC_LIMIT_MACROS
+
 /*
  * JS atom table.
  */
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsutil.h" /* Added by JSIFY */
@@ -53,17 +55,23 @@
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsparse.h"
 #include "jsscan.h"
 #include "jsstr.h"
 #include "jsversion.h"
+#include "jsxml.h"
+
 #include "jsstrinlines.h"
+#include "jsatominlines.h"
+#include "jsobjinlines.h"
+
+using namespace js;
 
 /*
  * ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems.
  */
 JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4);
 JS_STATIC_ASSERT(sizeof(JSAtom *) == JS_BYTES_PER_WORD);
 
 /*
@@ -80,17 +88,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, ATOM_KEY(atom));
+    return js_ValueToPrintableString(cx, Value(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
@@ -264,22 +272,22 @@ const char js_current_str[]          = "
 
 /*
  * JSAtomState.doubleAtoms and JSAtomState.stringAtoms hashtable entry. To
  * support pinned and interned string atoms, we use the lowest bits of the
  * keyAndFlags field to store ATOM_PINNED and ATOM_INTERNED flags.
  */
 typedef struct JSAtomHashEntry {
     JSDHashEntryHdr hdr;
-    jsuword         keyAndFlags;
+    jsboxedword     keyAndFlags;
 } JSAtomHashEntry;
 
 #define ATOM_ENTRY_FLAG_MASK            (ATOM_PINNED | ATOM_INTERNED)
 
-JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JSVAL_ALIGN);
+JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JSBOXEDWORD_ALIGN);
 
 /*
  * Helper macros to access and modify JSAtomHashEntry.
  */
 #define TO_ATOM_ENTRY(hdr)              ((JSAtomHashEntry *) hdr)
 #define ATOM_ENTRY_KEY(entry)                                                 \
     ((void *)((entry)->keyAndFlags & ~ATOM_ENTRY_FLAG_MASK))
 #define ATOM_ENTRY_FLAGS(entry)                                               \
@@ -531,18 +539,18 @@ js_locked_atom_tracer(JSDHashTable *tabl
     JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
     JSTracer *trc = (JSTracer *)arg;
 
     if (entry->keyAndFlags == 0) {
         /* Ignore uninitialized entries during tracing. */
         return JS_DHASH_NEXT;
     }
     JS_SET_TRACING_INDEX(trc, "locked_atom", (size_t)number);
-    js_CallGCMarker(trc, ATOM_ENTRY_KEY(entry),
-                    IS_STRING_TABLE(table) ? JSTRACE_STRING : JSTRACE_DOUBLE);
+    CallGCMarker(trc, ATOM_ENTRY_KEY(entry),
+                 IS_STRING_TABLE(table) ? JSTRACE_STRING : JSTRACE_DOUBLE);
     return JS_DHASH_NEXT;
 }
 
 static JSDHashOperator
 js_pinned_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
                         uint32 number, void *arg)
 {
     JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
@@ -551,17 +559,17 @@ js_pinned_atom_tracer(JSDHashTable *tabl
 
     JS_ASSERT(IS_STRING_TABLE(table));
     if (flags & (ATOM_PINNED | ATOM_INTERNED)) {
         JS_SET_TRACING_INDEX(trc,
                              flags & ATOM_PINNED
                              ? "pinned_atom"
                              : "interned_atom",
                              (size_t)number);
-        js_CallGCMarker(trc, ATOM_ENTRY_KEY(entry), JSTRACE_STRING);
+        CallGCMarker(trc, ATOM_ENTRY_KEY(entry), JSTRACE_STRING);
     }
     return JS_DHASH_NEXT;
 }
 
 void
 js_TraceAtomState(JSTracer *trc, JSBool allAtoms)
 {
     JSRuntime *rt = trc->context->runtime;
@@ -614,30 +622,30 @@ js_SweepAtomState(JSContext *cx)
 JSAtom *
 js_AtomizeDouble(JSContext *cx, jsdouble d)
 {
     JSAtomState *state;
     JSDHashTable *table;
     JSAtomHashEntry *entry;
     uint32 gen;
     jsdouble *key;
-    jsval v;
+    jsboxedword w;
 
     state = &cx->runtime->atomState;
     table = &state->doubleAtoms;
 
     JS_LOCK(cx, &state->lock);
     entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, &d, JS_DHASH_ADD));
     if (!entry)
         goto failed_hash_add;
     if (entry->keyAndFlags == 0) {
         gen = ++table->generation;
         JS_UNLOCK(cx, &state->lock);
 
-        key = js_NewWeaklyRootedDouble(cx, d);
+        key = js_NewWeaklyRootedDoubleAtom(cx, d);
         if (!key)
             return NULL;
 
         JS_LOCK(cx, &state->lock);
         if (table->generation == gen) {
             JS_ASSERT(entry->keyAndFlags == 0);
         } else {
             entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key,
@@ -647,49 +655,49 @@ js_AtomizeDouble(JSContext *cx, jsdouble
             if (entry->keyAndFlags != 0)
                 goto finish;
             ++table->generation;
         }
         INIT_ATOM_ENTRY(entry, key);
     }
 
   finish:
-    v = DOUBLE_TO_JSVAL((jsdouble *)ATOM_ENTRY_KEY(entry));
-    cx->weakRoots.lastAtom = v;
+    w = DOUBLE_TO_JSBOXEDWORD((jsdouble *)ATOM_ENTRY_KEY(entry));
+    cx->weakRoots.lastAtom = (JSAtom *)w;
     JS_UNLOCK(cx, &state->lock);
 
-    return (JSAtom *)v;
+    return (JSAtom *)w;
 
   failed_hash_add:
     JS_UNLOCK(cx, &state->lock);
     JS_ReportOutOfMemory(cx);
     return NULL;
 }
 
 JSAtom *
 js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
 {
-    jsval v;
+    JSAtom *atom;
     JSAtomState *state;
     JSDHashTable *table;
     JSAtomHashEntry *entry;
     JSString *key;
     uint32 gen;
 
     JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
     JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
 
     if (str->isAtomized())
-        return (JSAtom *) STRING_TO_JSVAL(str);
+        return STRING_TO_ATOM(str);
 
     size_t length = str->length();
     if (length == 1) {
         jschar c = str->chars()[0];
         if (c < UNIT_STRING_LIMIT)
-            return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c));
+            return STRING_TO_ATOM(JSString::unitString(c));
     }
 
     /*
      * Here we know that JSString::intStringTable covers only 256 (or at least
      * not 1000 or more) chars. We rely on order here to resolve the unit vs.
      * int string atom identity issue by giving priority to unit strings for
      * '0' through '9' (see JSString::intString in jsstrinlines.h).
      */
@@ -700,17 +708,17 @@ js_AtomizeString(JSContext *cx, JSString
         if ('1' <= chars[0] && chars[0] <= '9' &&
             '0' <= chars[1] && chars[1] <= '9' &&
             (length == 2 || ('0' <= chars[2] && chars[2] <= '9'))) {
             jsint i = (chars[0] - '0') * 10 + chars[1] - '0';
 
             if (length == 3)
                 i = i * 10 + chars[2] - '0'; 
             if (jsuint(i) < INT_STRING_LIMIT)
-                return (JSAtom *) STRING_TO_JSVAL(JSString::intString(i));
+                return STRING_TO_ATOM(JSString::intString(i));
         }
     }
 
     state = &cx->runtime->atomState;
     table = &state->stringAtoms;
 
     JS_LOCK(cx, &state->lock);
     entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD));
@@ -770,20 +778,20 @@ js_AtomizeString(JSContext *cx, JSString
         }
         INIT_ATOM_ENTRY(entry, key);
         key->flatSetAtomized();
     }
 
   finish:
     ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED));
     JS_ASSERT(key->isAtomized());
-    v = STRING_TO_JSVAL(key);
-    cx->weakRoots.lastAtom = v;
+    atom = STRING_TO_ATOM(key);
+    cx->weakRoots.lastAtom = atom;
     JS_UNLOCK(cx, &state->lock);
-    return (JSAtom *)v;
+    return atom;
 
   failed_hash_add:
     JS_UNLOCK(cx, &state->lock);
     JS_ReportOutOfMemory(cx);
     return NULL;
 }
 
 JSAtom *
@@ -837,49 +845,49 @@ js_GetExistingStringAtom(JSContext *cx, 
 {
     JSString str, *str2;
     JSAtomState *state;
     JSDHashEntryHdr *hdr;
 
     if (length == 1) {
         jschar c = *chars;
         if (c < UNIT_STRING_LIMIT)
-            return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c));
+            return STRING_TO_ATOM(JSString::unitString(c));
     }
 
     str.initFlat((jschar *)chars, length);
     state = &cx->runtime->atomState;
 
     JS_LOCK(cx, &state->lock);
     hdr = JS_DHashTableOperate(&state->stringAtoms, &str, JS_DHASH_LOOKUP);
     str2 = JS_DHASH_ENTRY_IS_BUSY(hdr)
            ? (JSString *)ATOM_ENTRY_KEY(TO_ATOM_ENTRY(hdr))
            : NULL;
     JS_UNLOCK(cx, &state->lock);
 
-    return str2 ? (JSAtom *)STRING_TO_JSVAL(str2) : NULL;
+    return str2 ? STRING_TO_ATOM(str2) : NULL;
 }
 
 JSBool
-js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp)
+js_AtomizePrimitiveValue(JSContext *cx, jsboxedword w, JSAtom **atomp)
 {
     JSAtom *atom;
 
-    if (JSVAL_IS_STRING(v)) {
-        atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0);
+    if (JSBOXEDWORD_IS_STRING(w)) {
+        atom = js_AtomizeString(cx, JSBOXEDWORD_TO_STRING(w), 0);
         if (!atom)
             return JS_FALSE;
-    } else if (JSVAL_IS_DOUBLE(v)) {
-        atom = js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(v));
+    } else if (JSBOXEDWORD_IS_DOUBLE(w)) {
+        atom = js_AtomizeDouble(cx, *JSBOXEDWORD_TO_DOUBLE(w));
         if (!atom)
             return JS_FALSE;
     } else {
-        JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_BOOLEAN(v) ||
-                  JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v));
-        atom = (JSAtom *)v;
+        JS_ASSERT(JSBOXEDWORD_IS_INT(w) || JSBOXEDWORD_IS_BOOLEAN(w) ||
+                  JSBOXEDWORD_IS_NULL(w) || JSBOXEDWORD_IS_VOID(w));
+        atom = (JSAtom *)w;
     }
     *atomp = atom;
     return JS_TRUE;
 }
 
 #ifdef DEBUG
 
 static JSDHashOperator
@@ -1260,8 +1268,101 @@ js_InitAtomMap(JSContext *cx, JSAtomMap 
         JS_HashTableEnumerateEntries(al->table, js_map_atom, vector);
     } else {
         do {
             vector[ALE_INDEX(ale)] = ALE_ATOM(ale);
         } while ((ale = ALE_NEXT(ale)) != NULL);
     }
     al->clear();
 }
+
+#if JS_HAS_XML_SUPPORT
+bool
+js_InternNonIntElementIdSlow(JSContext *cx, JSObject *obj, const Value &idval,
+                             jsid *idp)
+{
+    JS_ASSERT(idval.isObject());
+    if (obj->isXML()) {
+        *idp = OBJECT_TO_JSID(&idval.asObject());
+        return true;
+    }
+
+    if (!js_IsFunctionQName(cx, &idval.asObject(), idp))
+        return JS_FALSE;
+    if (*idp != 0)
+        return true;
+
+    return js_ValueToStringId(cx, idval, idp);
+}
+
+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);
+        return true;
+    }
+
+    if (!js_IsFunctionQName(cx, &idval.asObject(), idp))
+        return JS_FALSE;
+    if (*idp != 0) {
+        vp->copy(IdToValue(*idp));
+        return true;
+    }
+
+    if (js_ValueToStringId(cx, idval, idp)) {
+        vp->setString(ATOM_TO_STRING(JSID_TO_ATOM(*idp)));
+        return true;
+    }
+    return false;
+}
+#endif
+
+namespace js {
+
+bool
+ValueToId(JSContext *cx, const Value *vp, jsid *idp)
+{
+    int32_t i;
+    if (ValueFitsInInt32(*vp, &i) && INT32_FITS_IN_JSID(i)) {
+        *idp = INT_TO_JSID(i);
+        return true;
+    }
+
+#if JS_HAS_XML_SUPPORT
+    if (vp->isObject()) {
+        Class *clasp = vp->asObject().getClass();
+        if (JS_UNLIKELY(clasp == &js_QNameClass.base ||
+                        clasp == &js_AttributeNameClass ||
+                        clasp == &js_AnyNameClass)) {
+            *idp = OBJECT_TO_JSID(&vp->asObject());
+            return true;
+        }
+    }
+#endif
+
+    return js_ValueToStringId(cx, *vp, idp);
+}
+
+/*
+ * 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));
+    else if (JSID_IS_ATOM(id))
+        v.setString(ATOM_TO_STRING(JSID_TO_ATOM(id)));
+    else if (JSID_IS_NULL(id))
+        v.setNull();
+    else
+        SetObject(&v, *JSID_TO_OBJECT(id));
+    return v;
+}
+
+} /* namespace js */
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -47,28 +47,28 @@
 #include "jstypes.h"
 #include "jshash.h" /* Added by JSIFY */
 #include "jsdhash.h"
 #include "jsapi.h"
 #include "jsprvtd.h"
 #include "jspubtd.h"
 #include "jslock.h"
 
-JS_BEGIN_EXTERN_C
-
 #define ATOM_PINNED     0x1       /* atom is pinned against GC */
 #define ATOM_INTERNED   0x2       /* pinned variant for JS_Intern* API */
 #define ATOM_NOCOPY     0x4       /* don't copy atom string bytes */
 #define ATOM_TMPSTR     0x8       /* internal, to avoid extra string */
 
-#define ATOM_KEY(atom)            ((jsval)(atom))
-#define ATOM_IS_DOUBLE(atom)      JSVAL_IS_DOUBLE(ATOM_KEY(atom))
-#define ATOM_TO_DOUBLE(atom)      JSVAL_TO_DOUBLE(ATOM_KEY(atom))
-#define ATOM_IS_STRING(atom)      JSVAL_IS_STRING(ATOM_KEY(atom))
-#define ATOM_TO_STRING(atom)      JSVAL_TO_STRING(ATOM_KEY(atom))
+#define ATOM_KEY(atom)            ((jsboxedword)(atom))
+#define ATOM_IS_DOUBLE(atom)      JSBOXEDWORD_IS_DOUBLE(ATOM_KEY(atom))
+#define ATOM_TO_DOUBLE(atom)      JSBOXEDWORD_TO_DOUBLE(ATOM_KEY(atom))
+#define ATOM_IS_STRING(atom)      JSBOXEDWORD_IS_STRING(ATOM_KEY(atom))
+#define ATOM_TO_STRING(atom)      JSBOXEDWORD_TO_STRING(ATOM_KEY(atom))
+#define STRING_TO_ATOM(str)       (JS_ASSERT(str->isAtomized()),              \
+                                   (JSAtom *)STRING_TO_JSBOXEDWORD(str))
 
 #if JS_BYTES_PER_WORD == 4
 # define ATOM_HASH(atom)          ((JSHashNumber)(atom) >> 2)
 #elif JS_BYTES_PER_WORD == 8
 # define ATOM_HASH(atom)          (((JSHashNumber)(jsuword)(atom) >> 3) ^     \
                                    (JSHashNumber)((jsuword)(atom) >> 32))
 #else
 # error "Unsupported configuration"
@@ -83,17 +83,17 @@ extern const char *
 js_AtomToPrintableString(JSContext *cx, JSAtom *atom);
 
 struct JSAtomListElement {
     JSHashEntry         entry;
 };
 
 #define ALE_ATOM(ale)   ((JSAtom *) (ale)->entry.key)
 #define ALE_INDEX(ale)  (jsatomid(uintptr_t((ale)->entry.value)))
-#define ALE_VALUE(ale)  ((jsval) (ale)->entry.value)
+#define ALE_VALUE(ale)  ((jsboxedword) (ale)->entry.value)
 #define ALE_NEXT(ale)   ((JSAtomListElement *) (ale)->entry.next)
 
 /*
  * In an upvars list, ALE_DEFN(ale)->resolve() is the outermost definition the
  * name may reference. If a with block or a function that calls eval encloses
  * the use, the name may end up referring to something else at runtime.
  */
 #define ALE_DEFN(ale)   ((JSDefinition *) (ale)->entry.value)
@@ -114,18 +114,16 @@ struct JSAtomListElement {
  * or AME alternatives.
  */
 struct JSAtomSet {
     JSHashEntry         *list;          /* literals indexed for mapping */
     JSHashTable         *table;         /* hash table if list gets too long */
     jsuint              count;          /* count of indexed literals */
 };
 
-#ifdef __cplusplus
-
 struct JSAtomList : public JSAtomSet
 {
 #ifdef DEBUG
     const JSAtomSet* set;               /* asserted null in mutating methods */
 #endif
 
     JSAtomList() {
         list = NULL; table = NULL; count = 0;
@@ -193,18 +191,16 @@ class JSAtomListIterator {
     void reset() {
         next = (JSAtomListElement *) list->list;
         index = 0;
     }
 
     JSAtomListElement* operator ()();
 };
 
-#endif /* __cplusplus */
-
 struct JSAtomMap {
     JSAtom              **vector;       /* array of ptrs to indexed atoms */
     jsatomid            length;         /* count of (to-be-)indexed atoms */
 };
 
 struct JSAtomState {
     JSDHashTable        stringAtoms;    /* hash table with shared strings */
     JSDHashTable        doubleAtoms;    /* hash table with shared doubles */
@@ -480,28 +476,48 @@ js_AtomizeChars(JSContext *cx, const jsc
  */
 extern JSAtom *
 js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length);
 
 /*
  * This variant handles all primitive values.
  */
 JSBool
-js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp);
+js_AtomizePrimitiveValue(JSContext *cx, jsboxedword w, JSAtom **atomp);
 
 #ifdef DEBUG
 
 extern JS_FRIEND_API(void)
 js_DumpAtoms(JSContext *cx, FILE *fp);
 
 #endif
 
+inline bool
+js_ValueToAtom(JSContext *cx, const js::Value &v, JSAtom **atomp);
+
+inline bool
+js_ValueToStringId(JSContext *cx, const js::Value &v, jsid *idp);
+
+inline bool
+js_InternNonIntElementId(JSContext *cx, JSObject *obj, const js::Value &idval,
+                         jsid *idp);
+inline bool
+js_InternNonIntElementId(JSContext *cx, JSObject *obj, const js::Value &idval,
+                         jsid *idp, js::Value *vp);
 /*
  * For all unmapped atoms recorded in al, add a mapping from the atom's index
  * to its address. map->length must already be set to the number of atoms in
  * the list and map->vector must point to pre-allocated memory.
  */
 extern void
 js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al);
 
-JS_END_EXTERN_C
+namespace js {
+
+Value
+IdToValue(jsid id);
+
+bool
+ValueToId(JSContext *cx, const Value &v, jsid *idp);
+
+} /* namespace js */
 
 #endif /* jsatom_h___ */
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -41,55 +41,103 @@
 #define jsatominlines_h___
 
 #include "jsatom.h"
 #include "jsnum.h"
 
 /*
  * Convert v to an atomized string and wrap it as an id.
  */
-inline JSBool
-js_ValueToStringId(JSContext *cx, jsval v, jsid *idp)
+inline bool
+js_ValueToAtom(JSContext *cx, const js::Value &v, JSAtom **atomp)
 {
     JSString *str;
     JSAtom *atom;
 
     /*
      * Optimize for the common case where v is an already-atomized string. The
      * comment in jsstr.h before JSString::flatSetAtomized explains why this is
      * thread-safe. The extra rooting via lastAtom (which would otherwise be
      * done in js_js_AtomizeString) ensures the caller that the resulting id at
      * is least weakly rooted.
      */
-    if (JSVAL_IS_STRING(v)) {
-        str = JSVAL_TO_STRING(v);
+    if (v.isString()) {
+        str = v.asString();
         if (str->isAtomized()) {
-            cx->weakRoots.lastAtom = v;
-            *idp = ATOM_TO_JSID((JSAtom *) v);
-            return JS_TRUE;
+            cx->weakRoots.lastAtom = *atomp = STRING_TO_ATOM(str);
+            return true;
         }
     } else {
         str = js_ValueToString(cx, v);
         if (!str)
-            return JS_FALSE;
+            return false;
     }
     atom = js_AtomizeString(cx, str, 0);
     if (!atom)
-        return JS_FALSE;
-    *idp = ATOM_TO_JSID(atom);
-    return JS_TRUE;
+        return false;
+    *atomp = atom;
+    return true;
 }
 
-inline JSBool
+inline bool
+js_ValueToStringId(JSContext *cx, const js::Value &v, jsid *idp)
+{
+    JSAtom *atom;
+    if (js_ValueToAtom(cx, v, &atom)) {
+        *idp = ATOM_TO_JSID(atom);
+        return true;
+    }
+    return false;
+}
+
+inline bool
 js_Int32ToId(JSContext* cx, int32 index, jsid* id)
 {
-    if (INT_FITS_IN_JSVAL(index)) {
+    if (INT32_FITS_IN_JSID(index)) {
         *id = INT_TO_JSID(index);
-        JS_ASSERT(INT_JSID_TO_JSVAL(*id) == INT_TO_JSVAL(index));
-        return JS_TRUE;
+        return true;
     }
     JSString* str = js_NumberToString(cx, index);
     if (!str)
-        return JS_FALSE;
-    return js_ValueToStringId(cx, STRING_TO_JSVAL(str), id);
+        return false;
+    return js_ValueToStringId(cx, js::Value(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()));
+
+#if JS_HAS_XML_SUPPORT
+    extern bool js_InternNonIntElementIdSlow(JSContext *, JSObject *,
+                                             const js::Value &, jsid *);
+    if (idval.isObject())
+        return js_InternNonIntElementIdSlow(cx, obj, idval, idp);
+#endif
+
+    return js_ValueToStringId(cx, idval, idp);
+}
+
+inline bool
+js_InternNonIntElementId(JSContext *cx, JSObject *obj, const js::Value &idval,
+                         jsid *idp, js::Value *vp)
+{
+    JS_ASSERT_IF(idval.isInt32(), !INT32_FITS_IN_JSID(idval.asInt32()));
+
+#if JS_HAS_XML_SUPPORT
+    extern bool js_InternNonIntElementIdSlow(JSContext *, JSObject *,
+                                             const js::Value &,
+                                             jsid *, js::Value *);
+    if (idval.isObject())
+        return js_InternNonIntElementIdSlow(cx, obj, idval, idp, vp);
+#endif
+
+    JSAtom *atom;
+    if (js_ValueToAtom(cx, idval, &atom)) {
+        *idp = ATOM_TO_JSID(atom);
+        vp->setString(ATOM_TO_STRING(atom));
+        return true;
+    }
+    return false;
 }
 
 #endif /* jsatominlines_h___ */
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -32,16 +32,18 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#define __STDC_LIMIT_MACROS
+
 /*
  * JS boolean implementation.
  */
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsutil.h" /* Added by JSIFY */
 #include "jsapi.h"
 #include "jsatom.h"
@@ -49,116 +51,112 @@
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsstr.h"
 #include "jsvector.h"
 
+#include "jsobjinlines.h"
 
-/* Check pseudo-booleans values. */
-JS_STATIC_ASSERT(!(JSVAL_TRUE & JSVAL_HOLE_FLAG));
-JS_STATIC_ASSERT(!(JSVAL_FALSE & JSVAL_HOLE_FLAG));
-JS_STATIC_ASSERT(!(JSVAL_VOID & JSVAL_HOLE_FLAG));
-JS_STATIC_ASSERT((JSVAL_HOLE & JSVAL_HOLE_FLAG));
-JS_STATIC_ASSERT((JSVAL_HOLE & ~JSVAL_HOLE_FLAG) == JSVAL_VOID);
-JS_STATIC_ASSERT(!(JSVAL_ARETURN & JSVAL_HOLE_FLAG));
+using namespace js;
 
-JSClass js_BooleanClass = {
+Class js_BooleanClass = {
     "Boolean",
     JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean),
-    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
-    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   NULL,
+    PropertyStub,  PropertyStub,  PropertyStub,  PropertyStub,
+    EnumerateStub, ResolveStub,   ConvertStub,   NULL,
     JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 #if JS_HAS_TOSOURCE
 #include "jsprf.h"
 
 static JSBool
-bool_toSource(JSContext *cx, uintN argc, jsval *vp)
+bool_toSource(JSContext *cx, uintN argc, Value *vp)
 {
-    jsval v;
+    Value v;
     char buf[32];
     JSString *str;
 
     if (!js_GetPrimitiveThis(cx, vp, &js_BooleanClass, &v))
         return JS_FALSE;
-    JS_ASSERT(JSVAL_IS_BOOLEAN(v));
+    JS_ASSERT(v.isBoolean());
     JS_snprintf(buf, sizeof buf, "(new %s(%s))",
                 js_BooleanClass.name,
-                JS_BOOLEAN_STR(JSVAL_TO_BOOLEAN(v)));
+                JS_BOOLEAN_STR(v.asBoolean()));
     str = JS_NewStringCopyZ(cx, buf);
     if (!str)
         return JS_FALSE;
-    *vp = STRING_TO_JSVAL(str);
+    vp->setString(str);
     return JS_TRUE;
 }
 #endif
 
 static JSBool
-bool_toString(JSContext *cx, uintN argc, jsval *vp)
+bool_toString(JSContext *cx, uintN argc, Value *vp)
 {
-    jsval v;
+    Value v;
     JSAtom *atom;
     JSString *str;
 
     if (!js_GetPrimitiveThis(cx, vp, &js_BooleanClass, &v))
         return JS_FALSE;
-    JS_ASSERT(JSVAL_IS_BOOLEAN(v));
-    atom = cx->runtime->atomState.booleanAtoms[JSVAL_TO_BOOLEAN(v) ? 1 : 0];
+    JS_ASSERT(v.isBoolean());
+    atom = cx->runtime->atomState.booleanAtoms[v.asBoolean() ? 1 : 0];
     str = ATOM_TO_STRING(atom);
     if (!str)
         return JS_FALSE;
-    *vp = STRING_TO_JSVAL(str);
+    vp->setString(str);
     return JS_TRUE;
 }
 
 static JSBool
-bool_valueOf(JSContext *cx, uintN argc, jsval *vp)
+bool_valueOf(JSContext *cx, uintN argc, Value *vp)
 {
     return js_GetPrimitiveThis(cx, vp, &js_BooleanClass, vp);
 }
 
 static JSFunctionSpec boolean_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,  bool_toSource,  0, JSFUN_THISP_BOOLEAN),
 #endif
     JS_FN(js_toString_str,  bool_toString,  0, JSFUN_THISP_BOOLEAN),
     JS_FN(js_valueOf_str,   bool_valueOf,   0, JSFUN_THISP_BOOLEAN),
     JS_FN(js_toJSON_str,    bool_valueOf,   0, JSFUN_THISP_BOOLEAN),
     JS_FS_END
 };
 
 static JSBool
-Boolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+Boolean(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
-    jsval bval;
+    Value bval;
 
-    bval = (argc != 0)
-           ? BOOLEAN_TO_JSVAL(js_ValueToBoolean(argv[0]))
-           : JSVAL_FALSE;
+    if (argc != 0)
+        bval.setBoolean(js_ValueToBoolean(argv[0]));
+    else
+        bval.setBoolean(false);
     if (!JS_IsConstructing(cx))
-        *rval = bval;
+        rval->copy(bval);
     else
-        obj->fslots[JSSLOT_PRIMITIVE_THIS] = bval;
+        obj->fslots[JSSLOT_PRIMITIVE_THIS].copy(bval);
     return true;
 }
 
 JSObject *
 js_InitBooleanClass(JSContext *cx, JSObject *obj)
 {
     JSObject *proto;
 
-    proto = JS_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1,
-                        NULL, boolean_methods, NULL, NULL);
+    proto = js_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1,
+                         NULL, boolean_methods, NULL, NULL);
     if (!proto)
         return NULL;
-    proto->fslots[JSSLOT_PRIMITIVE_THIS] = JSVAL_FALSE;
+    proto->fslots[JSSLOT_PRIMITIVE_THIS].setBoolean(false);
     return proto;
 }
 
 JSString *
 js_BooleanToString(JSContext *cx, JSBool b)
 {
     return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]);
 }
@@ -166,27 +164,27 @@ js_BooleanToString(JSContext *cx, JSBool
 /* This function implements E-262-3 section 9.8, toString. */
 JSBool
 js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharBuffer &cb)
 {
     return b ? js_AppendLiteral(cb, "true") : js_AppendLiteral(cb, "false");
 }
 
 JSBool
-js_ValueToBoolean(jsval v)
+js_ValueToBoolean(const Value &v)
 {
-    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
+    if (v.isNullOrUndefined())
         return JS_FALSE;
-    if (JSVAL_IS_OBJECT(v))
+    if (v.isObject())
         return JS_TRUE;
-    if (JSVAL_IS_STRING(v))
-        return JSVAL_TO_STRING(v)->length() != 0;
-    if (JSVAL_IS_INT(v))
-        return JSVAL_TO_INT(v) != 0;
-    if (JSVAL_IS_DOUBLE(v)) {
+    if (v.isString())
+        return v.asString()->length() != 0;
+    if (v.isInt32())
+        return v.asInt32() != 0;
+    if (v.isDouble()) {
         jsdouble d;
 
-        d = *JSVAL_TO_DOUBLE(v);
+        d = v.asDouble();
         return !JSDOUBLE_IS_NaN(d) && d != 0;
     }
-    JS_ASSERT(JSVAL_IS_BOOLEAN(v));
-    return JSVAL_TO_BOOLEAN(v);
+    JS_ASSERT(v.isBoolean());
+    return v.asBoolean();
 }
--- a/js/src/jsbool.h
+++ b/js/src/jsbool.h
@@ -40,45 +40,23 @@
 #ifndef jsbool_h___
 #define jsbool_h___
 /*
  * JS boolean interface.
  */
 
 #include "jsapi.h"
 
-JS_BEGIN_EXTERN_C
-
-/*
- * Special values, not visible to script but used internally by the engine.
- *
- * JSVAL_HOLE is a useful value for identifying a hole in an array.  It's also
- * used in the interpreter to represent "no exception pending".  In general it
- * can be used to represent "no value".
- *
- * A JSVAL_HOLE can be cheaply converted to undefined without affecting any
- * other boolean (or special value) by masking out JSVAL_HOLE_FLAG.
- *
- * JSVAL_ARETURN is used to throw asynchronous return for generator.close().
- *
- * NB: SPECIAL_TO_JSVAL(2) is JSVAL_VOID (see jsapi.h).
- */
-#define JSVAL_HOLE_FLAG jsval(4 << JSVAL_TAGBITS)
-#define JSVAL_HOLE      (JSVAL_VOID | JSVAL_HOLE_FLAG)
-#define JSVAL_ARETURN   SPECIAL_TO_JSVAL(8)
-
-extern JSClass js_BooleanClass;
+extern js::Class js_BooleanClass;
 
 extern JSObject *
 js_InitBooleanClass(JSContext *cx, JSObject *obj);
 
 extern JSString *
 js_BooleanToString(JSContext *cx, JSBool b);
 
 extern JSBool
 js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharBuffer &cb);
 
 extern JSBool
-js_ValueToBoolean(jsval v);
-
-JS_END_EXTERN_C
+js_ValueToBoolean(const js::Value &v);
 
 #endif /* jsbool_h___ */
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -217,17 +217,17 @@ struct ClosureVarInfo;
 #define _JS_CTYPE_OBJECT_RETRY      _JS_CTYPE(JSObject *,             _JS_PTR, --, --, FAIL_NULL)
 #define _JS_CTYPE_OBJECT_FAIL       _JS_CTYPE(JSObject *,             _JS_PTR, --, --, FAIL_STATUS)
 #define _JS_CTYPE_CONSTRUCTOR_RETRY _JS_CTYPE(JSObject *,             _JS_PTR, --, --, FAIL_NULL | \
                                                                                   JSTN_CONSTRUCTOR)
 #define _JS_CTYPE_REGEXP            _JS_CTYPE(JSObject *,             _JS_PTR, "","r", INFALLIBLE)
 #define _JS_CTYPE_SCOPEPROP         _JS_CTYPE(JSScopeProperty *,      _JS_PTR, --, --, INFALLIBLE)
 #define _JS_CTYPE_INTERPSTATE       _JS_CTYPE(InterpState *,          _JS_PTR, --, --, INFALLIBLE)
 #define _JS_CTYPE_FRAGMENT          _JS_CTYPE(nanojit::Fragment *,    _JS_PTR, --, --, INFALLIBLE)
-#define _JS_CTYPE_CLASS             _JS_CTYPE(JSClass *,              _JS_PTR, --, --, INFALLIBLE)
+#define _JS_CTYPE_CLASS             _JS_CTYPE(js::Class *,            _JS_PTR, --, --, INFALLIBLE)
 #define _JS_CTYPE_DOUBLEPTR         _JS_CTYPE(double *,               _JS_PTR, --, --, INFALLIBLE)
 #define _JS_CTYPE_CHARPTR           _JS_CTYPE(char *,                 _JS_PTR, --, --, INFALLIBLE)
 #define _JS_CTYPE_APNPTR            _JS_CTYPE(ArgsPrivateNative *,    _JS_PTR, --, --, INFALLIBLE)
 #define _JS_CTYPE_CVIPTR            _JS_CTYPE(const ClosureVarInfo *, _JS_PTR, --, --, INFALLIBLE)
 #define _JS_CTYPE_FRAMEINFO         _JS_CTYPE(FrameInfo *,            _JS_PTR, --, --, INFALLIBLE)
 
 #define _JS_EXPAND(tokens)  tokens
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -33,16 +33,18 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#define __STDC_LIMIT_MACROS
+
 /*
  * JS execution context.
  */
 #include <new>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
@@ -99,17 +101,17 @@ MarkLocalRoots(JSTracer *trc, JSLocalRoo
 #ifdef DEBUG
 JS_REQUIRES_STACK bool
 CallStack::contains(const JSStackFrame *fp) const
 {
     JS_ASSERT(inContext());
     JSStackFrame *start;
     JSStackFrame *stop;
     if (isSuspended()) {
-        start = suspendedFrame;
+        start = suspendedFrame();
         stop = initialFrame->down;
     } else {
         start = cx->fp;
         stop = cx->activeCallStack()->initialFrame->down;
     }
     for (JSStackFrame *f = start; f != stop; f = f->down) {
         if (f == fp)
             return true;
@@ -124,74 +126,74 @@ StackSpace::init()
     void *p;
 #ifdef XP_WIN
     p = VirtualAlloc(NULL, sCapacityBytes, MEM_RESERVE, PAGE_READWRITE);
     if (!p)
         return false;
     void *check = VirtualAlloc(p, sCommitBytes, MEM_COMMIT, PAGE_READWRITE);
     if (p != check)
         return false;
-    base = reinterpret_cast<jsval *>(p);
+    base = reinterpret_cast<Value *>(p);
     commitEnd = base + sCommitVals;
     end = base + sCapacityVals;
 #else
     JS_ASSERT(sCapacityBytes % getpagesize() == 0);
     p = mmap(NULL, sCapacityBytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     if (p == MAP_FAILED)
         return false;
-    base = reinterpret_cast<jsval *>(p);
+    base = reinterpret_cast<Value *>(p);
     end = base + sCapacityVals;
 #endif
     return true;
 }
 
 void
 StackSpace::finish()
 {
 #ifdef XP_WIN
-    VirtualFree(base, (commitEnd - base) * sizeof(jsval), MEM_DECOMMIT);
+    VirtualFree(base, (commitEnd - base) * sizeof(Value), MEM_DECOMMIT);
     VirtualFree(base, 0, MEM_RELEASE);
 #else
     munmap(base, sCapacityBytes);
 #endif
 }
 
 #ifdef XP_WIN
 JS_FRIEND_API(bool)
-StackSpace::bumpCommit(jsval *from, ptrdiff_t nvals) const
+StackSpace::bumpCommit(Value *from, ptrdiff_t nvals) const
 {
     JS_ASSERT(end - from >= nvals);
-    jsval *newCommit = commitEnd;
-    jsval *request = from + nvals;
+    Value *newCommit = commitEnd;
+    Value *request = from + nvals;
 
     /* Use a dumb loop; will probably execute once. */
     JS_ASSERT((end - newCommit) % sCommitVals == 0);
     do {
         newCommit += sCommitVals;
         JS_ASSERT((end - newCommit) >= 0);
     } while(newCommit < request);
 
     /* Cast safe because sCapacityBytes is small. */
-    int32 size = (int32)(newCommit - commitEnd) * sizeof(jsval);
+    int32 size = (int32)(newCommit - commitEnd) * sizeof(Value);
 
     if (!VirtualAlloc(commitEnd, size, MEM_COMMIT, PAGE_READWRITE))
         return false;
     commitEnd = newCommit;
     return true;
 }
 #endif
 
 JS_REQUIRES_STACK void
 StackSpace::mark(JSTracer *trc)
 {
     /*
      * The correctness/completeness of marking depends on the continuity
      * invariants described by the CallStack and StackSpace definitions.
      */
-    jsval *end = firstUnused();
+    Value *end = firstUnused();
     for (CallStack *cs = currentCallStack; cs; cs = cs->getPreviousInThread()) {
         if (!cs->inContext()) {
             /* Mark slots/args trailing off callstack. */
             JS_ASSERT(end == cs->getInitialArgEnd());
             TraceValues(trc, cs->getInitialArgBegin(), cs->getInitialArgEnd(), "stack");
         } else  {
             /* This may be the only pointer to the initialVarObj. */
             if (cs->getInitialVarObj())
@@ -214,24 +216,24 @@ StackSpace::mark(JSTracer *trc)
         }
         end = cs->previousCallStackEnd();
     }
 }
 
 JS_REQUIRES_STACK bool
 StackSpace::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard &ag)
 {
-    jsval *start = firstUnused();
+    Value *start = firstUnused();
     uintN vplen = 2 + argc;
     ptrdiff_t nvals = ValuesPerCallStack + vplen;
     if (!ensureSpace(cx, start, nvals))
         return false;
-    jsval *vp = start + ValuesPerCallStack;
-    jsval *vpend = vp + vplen;
-    memset(vp, 0, vplen * sizeof(jsval)); /* Init so GC-safe on exit. */
+    Value *vp = start + ValuesPerCallStack;
+    Value *vpend = vp + vplen;
+    memset(vp, 0, vplen * sizeof(*vp)); /* Init so GC-safe on exit. */
 
     CallStack *cs = new(start) CallStack;
     cs->setInitialArgEnd(vpend);
     cs->setPreviousInThread(currentCallStack);
     currentCallStack = cs;
 
     ag.cx = cx;
     ag.cs = cs;
@@ -259,26 +261,26 @@ InvokeFrameGuard::InvokeFrameGuard()
  */
 bool
 StackSpace::getInvokeFrame(JSContext *cx, const InvokeArgsGuard &ag,
                            uintN nmissing, uintN nslots,
                            InvokeFrameGuard &fg) const
 {
     if (ag.cs) {
         JS_ASSERT(ag.cs == currentCallStack && !ag.cs->inContext());
-        jsval *start = ag.cs->getInitialArgEnd();
+        Value *start = ag.cs->getInitialArgEnd();
         ptrdiff_t nvals = nmissing + ValuesPerStackFrame + nslots;
         if (!ensureSpace(cx, start, nvals))
             return false;
         fg.fp = reinterpret_cast<JSStackFrame *>(start + nmissing);
         return true;
     }
 
     JS_ASSERT(isCurrent(cx) && currentCallStack->isActive());
-    jsval *start = cx->regs->sp;
+    Value *start = cx->regs->sp;
     ptrdiff_t nvals = nmissing + ValuesPerCallStack + ValuesPerStackFrame + nslots;
     if (!ensureSpace(cx, start, nvals))
         return false;
     fg.cs = new(start + nmissing) CallStack;
     fg.fp = reinterpret_cast<JSStackFrame *>(fg.cs + 1);
     return true;
 }
 
@@ -337,17 +339,17 @@ ExecuteFrameGuard::~ExecuteFrameGuard()
  * To maintain a 1 to 0..1 relationship between callstacks and js_Interpret
  * activations, we push a callstack even if it wasn't otherwise necessary.
  */
 JS_REQUIRES_STACK bool
 StackSpace::getExecuteFrame(JSContext *cx, JSStackFrame *down,
                             uintN vplen, uintN nslots,
                             ExecuteFrameGuard &fg) const
 {
-    jsval *start = firstUnused();
+    Value *start = firstUnused();
     ptrdiff_t nvals = ValuesPerCallStack + vplen + ValuesPerStackFrame + nslots;
     if (!ensureSpace(cx, start, nvals))
         return false;
 
     fg.cs = new(start) CallStack;
     fg.vp = start + ValuesPerCallStack;
     fg.fp = reinterpret_cast<JSStackFrame *>(fg.vp + vplen);
     fg.down = down;
@@ -373,17 +375,17 @@ StackSpace::popExecuteFrame(JSContext *c
     JS_ASSERT(isCurrent(cx) && cx->hasActiveCallStack());
     cx->popCallStackAndFrame();
     currentCallStack = currentCallStack->getPreviousInThread();
 }
 
 JS_REQUIRES_STACK void
 StackSpace::getSynthesizedSlowNativeFrame(JSContext *cx, CallStack *&cs, JSStackFrame *&fp)
 {
-    jsval *start = firstUnused();
+    Value *start = firstUnused();
     JS_ASSERT(size_t(end - start) >= ValuesPerCallStack + ValuesPerStackFrame);
     cs = new(start) CallStack;
     fp = reinterpret_cast<JSStackFrame *>(cs + 1);
 }
 
 JS_REQUIRES_STACK void
 StackSpace::pushSynthesizedSlowNativeFrame(JSContext *cx, CallStack *cs, JSStackFrame *fp,
                                            JSFrameRegs &regs)
@@ -407,26 +409,26 @@ StackSpace::popSynthesizedSlowNativeFram
     currentCallStack = currentCallStack->getPreviousInThread();
 }
 
 /*
  * When a pair of down-linked stack frames are in the same callstack, the
  * up-frame's address is the top of the down-frame's stack, modulo missing
  * arguments.
  */
-static inline jsval *
+static inline Value *
 InlineDownFrameSP(JSStackFrame *up)
 {
     JS_ASSERT(up->fun && up->script);
-    jsval *sp = up->argv + up->argc;
+    Value *sp = up->argv + up->argc;
 #ifdef DEBUG
     uint16 nargs = up->fun->nargs;
     uintN argc = up->argc;
     uintN missing = argc < nargs ? nargs - argc : 0;
-    JS_ASSERT(sp == (jsval *)up - missing);
+    JS_ASSERT(sp == (Value *)up - missing);
 #endif
     return sp;
 }
 
 JS_REQUIRES_STACK
 FrameRegsIter::FrameRegsIter(JSContext *cx)
 {
     curcs = cx->getCurrentCallStack();
@@ -1346,17 +1348,17 @@ js_WaitForGC(JSRuntime *rt)
 
 #endif
 
 static JSDHashNumber
 resolving_HashKey(JSDHashTable *table, const void *ptr)
 {
     const JSResolvingKey *key = (const JSResolvingKey *)ptr;
 
-    return (JSDHashNumber(uintptr_t(key->obj)) >> JSVAL_TAGBITS) ^ key->id;
+    return (JSDHashNumber(uintptr_t(key->obj)) >> JSBOXEDWORD_TAGBITS) ^ key->id;
 }
 
 JS_PUBLIC_API(JSBool)
 resolving_MatchEntry(JSDHashTable *table,
                      const JSDHashEntryHdr *hdr,
                      const void *ptr)
 {
     const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
@@ -1463,25 +1465,25 @@ js_EnterLocalRootScope(JSContext *cx)
         lrs->rootCount = 0;
         lrs->topChunk = &lrs->firstChunk;
         lrs->firstChunk.down = NULL;
         td->gcFreeLists.moveTo(&lrs->gcFreeLists);
         td->localRootStack = lrs;
     }
 
     /* Push lrs->scopeMark to save it for restore when leaving. */
-    int mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
+    int mark = js_PushLocalRoot(cx, lrs, (void *)lrs->scopeMark);
     if (mark < 0)
         return JS_FALSE;
     lrs->scopeMark = (uint32) mark;
     return true;
 }
 
 void
-js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
+js_LeaveLocalRootScopeWithResult(JSContext *cx, const Value &rval)
 {
     JSLocalRootStack *lrs;
     uint32 mark, m, n;
     JSLocalRootChunk *lrc;
 
     /* Defend against buggy native callers. */
     lrs = JS_THREAD_DATA(cx)->localRootStack;
     JS_ASSERT(lrs && lrs->rootCount != 0);
@@ -1507,28 +1509,28 @@ js_LeaveLocalRootScopeWithResult(JSConte
     /*
      * Pop the scope, restoring lrs->scopeMark.  If rval is a GC-thing, push
      * it on the caller's scope, or store it in lastInternalResult if we are
      * leaving the outermost scope.  We don't need to allocate a new lrc
      * because we can overwrite the old mark's slot with rval.
      */
     lrc = lrs->topChunk;
     m = mark & JSLRS_CHUNK_MASK;
-    lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]);
-    if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) {
+    lrs->scopeMark = (uint32) lrc->roots[m];
+    if (rval.isGCThing()) {
         if (mark == 0) {
-            cx->weakRoots.lastInternalResult = rval;
+            cx->weakRoots.lastInternalResult = rval.asGCThing();
         } else {
             /*
              * Increment m to avoid the "else if (m == 0)" case below.  If
              * rval is not a GC-thing, that case would take care of freeing
              * any chunk that contained only the old mark.  Since rval *is*
              * a GC-thing here, we want to reuse that old mark's slot.
              */
-            lrc->roots[m++] = rval;
+            lrc->roots[m++] = rval.asGCThing();
             ++mark;
         }
     }
     lrs->rootCount = (uint32) mark;
 
     /*
      * Free the stack eagerly, risking malloc churn.  The alternative would
      * require an lrs->entryCount member, maintained by Enter and Leave, and
@@ -1546,76 +1548,71 @@ js_LeaveLocalRootScopeWithResult(JSConte
         js_free(lrs);
     } else if (m == 0) {
         lrs->topChunk = lrc->down;
         js_free(lrc);
     }
 }
 
 void
-js_ForgetLocalRoot(JSContext *cx, jsval v)
+js_ForgetLocalRoot(JSContext *cx, void *thing)
 {
-    JSLocalRootStack *lrs;
-    uint32 i, j, m, n, mark;
-    JSLocalRootChunk *lrc, *lrc2;
-    jsval top;
-
-    lrs = JS_THREAD_DATA(cx)->localRootStack;
+    JSLocalRootStack *lrs = JS_THREAD_DATA(cx)->localRootStack;
     JS_ASSERT(lrs && lrs->rootCount);
     if (!lrs || lrs->rootCount == 0)
         return;
 
     /* Prepare to pop the top-most value from the stack. */
-    n = lrs->rootCount - 1;
-    m = n & JSLRS_CHUNK_MASK;
-    lrc = lrs->topChunk;
-    top = lrc->roots[m];
+    uint32 n = lrs->rootCount - 1;
+    uint32 m = n & JSLRS_CHUNK_MASK;
+    JSLocalRootChunk *lrc = lrs->topChunk;
+    void *top = lrc->roots[m];
 
     /* Be paranoid about calls on an empty scope. */
-    mark = lrs->scopeMark;
+    uint32 mark = lrs->scopeMark;
     JS_ASSERT(mark < n);
     if (mark >= n)
         return;
 
-    /* If v was not the last root pushed in the top scope, find it. */
-    if (top != v) {
-        /* Search downward in case v was recently pushed. */
-        i = n;
-        j = m;
-        lrc2 = lrc;
+    /* If thing was not the last root pushed in the top scope, find it. */
+    if (top != thing) {
+        /* Search downward in case thing was recently pushed. */
+        uint32 i = n;
+        uint32 j = m;
+        JSLocalRootChunk *lrc2 = lrc;
         while (--i > mark) {
             if (j == 0)
                 lrc2 = lrc2->down;
             j = i & JSLRS_CHUNK_MASK;
-            if (lrc2->roots[j] == v)
+            if (lrc2->roots[j] == thing)
                 break;
         }
 
         /* If we didn't find v in this scope, assert and bail out. */
         JS_ASSERT(i != mark);
         if (i == mark)
             return;
 
         /* Swap top and v so common tail code can pop v. */
         lrc2->roots[j] = top;
     }
 
     /* Pop the last value from the stack. */
-    lrc->roots[m] = JSVAL_NULL;
+    lrc->roots[m] = NULL;
     lrs->rootCount = n;
     if (m == 0) {
         JS_ASSERT(n != 0);
         JS_ASSERT(lrc != &lrs->firstChunk);
         lrs->topChunk = lrc->down;
         cx->free(lrc);
     }
 }
 
 int
-js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
+js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, void *thing)
 {
     uint32 n, m;
     JSLocalRootChunk *lrc;
 
     n = lrs->rootCount;
     m = n & JSLRS_CHUNK_MASK;
     if (n == 0 || m != 0) {
         /*
@@ -1638,45 +1635,42 @@ js_PushLocalRoot(JSContext *cx, JSLocalR
         if (!lrc) {
             js_ReportOutOfMemory(cx);
             return -1;
         }
         lrc->down = lrs->topChunk;
         lrs->topChunk = lrc;
     }
     lrs->rootCount = n + 1;
-    lrc->roots[m] = v;
+    lrc->roots[m] = thing;
     return (int) n;
 }
 
 static void
 MarkLocalRoots(JSTracer *trc, JSLocalRootStack *lrs)
 {
-    uint32 n, m, mark;
-    JSLocalRootChunk *lrc;
-    jsval v;
-
-    n = lrs->rootCount;
+    uint32 n = lrs->rootCount;
     if (n == 0)
         return;
 
-    mark = lrs->scopeMark;
-    lrc = lrs->topChunk;
+    uint32 mark = lrs->scopeMark;
+    JSLocalRootChunk *lrc = lrs->topChunk;
     do {
+        uint32 m;
         while (--n > mark) {
             m = n & JSLRS_CHUNK_MASK;
-            v = lrc->roots[m];
-            JS_ASSERT(JSVAL_IS_GCTHING(v) && v != JSVAL_NULL);
+            void *thing = lrc->roots[m];
+            JS_ASSERT(thing != NULL);
             JS_SET_TRACING_INDEX(trc, "local_root", n);
-            js_CallValueTracerIfGCThing(trc, v);
+            CallGCMarkerForGCThing(trc, thing);
             if (m == 0)
                 lrc = lrc->down;
         }
         m = n & JSLRS_CHUNK_MASK;
-        mark = JSVAL_TO_INT(lrc->roots[m]);
+        mark = (uint32)lrc->roots[m];
         if (m == 0)
             lrc = lrc->down;
     } while (n != 0);
     JS_ASSERT(!lrc);
 }
 
 static void
 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
@@ -2109,82 +2103,83 @@ js_ReportErrorAgain(JSContext *cx, const
 
 void
 js_ReportIsNotDefined(JSContext *cx, const char *name)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
 }
 
 JSBool
-js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v,
+js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, const Value &v,
                            JSString *fallback)
 {
     char *bytes;
     JSBool ok;
 
-    bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
+    bytes = js_DecompileValueGenerator(cx, spindex, Jsvalify(&v), fallback);
     if (!bytes)
         return JS_FALSE;
 
     if (strcmp(bytes, js_undefined_str) == 0 ||
         strcmp(bytes, js_null_str) == 0) {
         ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
                                           js_GetErrorMessage, NULL,
                                           JSMSG_NO_PROPERTIES, bytes,
                                           NULL, NULL);
-    } else if (JSVAL_IS_VOID(v)) {
+    } else if (v.isUndefined()) {
         ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
                                           js_GetErrorMessage, NULL,
                                           JSMSG_UNEXPECTED_TYPE, bytes,
                                           js_undefined_str, NULL);
     } else {
-        JS_ASSERT(JSVAL_IS_NULL(v));
+        JS_ASSERT(v.isNull());
         ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
                                           js_GetErrorMessage, NULL,
                                           JSMSG_UNEXPECTED_TYPE, bytes,
                                           js_null_str, NULL);
     }
 
     cx->free(bytes);
     return ok;
 }
 
 void
-js_ReportMissingArg(JSContext *cx, jsval *vp, uintN arg)
+js_ReportMissingArg(JSContext *cx, const Value &v, uintN arg)
 {
     char argbuf[11];
     char *bytes;
     JSAtom *atom;
 
     JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
     bytes = NULL;
-    if (VALUE_IS_FUNCTION(cx, *vp)) {
-        atom = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp))->atom;
-        bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp,
+    if (v.isFunObj()) {
+        atom = GET_FUNCTION_PRIVATE(cx, &v.asFunObj())->atom;
+        bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
+                                           Jsvalify(&v),
                                            ATOM_TO_STRING(atom));
         if (!bytes)
             return;
     }
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                          JSMSG_MISSING_FUN_ARG, argbuf,
                          bytes ? bytes : "");
     cx->free(bytes);
 }
 
 JSBool
 js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
-                         intN spindex, jsval v, JSString *fallback,
+                         intN spindex, const Value &v, JSString *fallback,
                          const char *arg1, const char *arg2)
 {
     char *bytes;
     JSBool ok;
 
     JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
     JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
-    bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
+    bytes = js_DecompileValueGenerator(cx, spindex, Jsvalify(&v), fallback);
     if (!bytes)
         return JS_FALSE;
 
     ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
                                       NULL, errorNumber, bytes, arg1, arg2);
     cx->free(bytes);
     return ok;
 }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -280,72 +280,81 @@ class CallStack
     CallStack           *previousInContext;
 
     /* Link for StackSpace callstack stack mentioned in StackSpace comment. */
     CallStack           *previousInThread;
 
     /* The first frame executed in this callstack. null iff cx is null */
     JSStackFrame        *initialFrame;
 
-    /* If this callstack is suspended, the top of the callstack. */
-    JSStackFrame        *suspendedFrame;
+    /*
+     * Two values packed together to get sizeof(Value) alignment.
+     * suspendedFrame: if this callstack is suspended, the top of the callstack.
+     * saved: if this callstack is suspended, whether it is also saved.
+     */
+    AlignedPtrAndFlag<JSStackFrame> suspendedFrameAndSaved;
 
     /* If this callstack is suspended, |cx->regs| when it was suspended. */
     JSFrameRegs         *suspendedRegs;
 
-    /* This callstack was suspended by JS_SaveFrameChain. */
-    bool                saved;
-
     /* End of arguments before the first frame. See StackSpace comment. */
-    jsval               *initialArgEnd;
+    js::Value           *initialArgEnd;
 
     /* The varobj on entry to initialFrame. */
     JSObject            *initialVarObj;
 
+    JSStackFrame *suspendedFrame() const {
+        return suspendedFrameAndSaved.ptr();
+    }
+
+    bool saved() const {
+        return suspendedFrameAndSaved.flag();
+    }
+
   public:
     CallStack()
       : cx(NULL), previousInContext(NULL), previousInThread(NULL),
-        initialFrame(NULL), suspendedFrame(NULL), saved(false),
+        initialFrame(NULL), suspendedFrameAndSaved(NULL, false),
         initialArgEnd(NULL), initialVarObj(NULL)
     {}
 
     /* Safe casts guaranteed by the contiguous-stack layout. */
 
-    jsval *previousCallStackEnd() const {
-        return (jsval *)this;
+    Value *previousCallStackEnd() const {
+        return (Value *)this;
     }
 
-    jsval *getInitialArgBegin() const {
-        return (jsval *)(this + 1);
+    Value *getInitialArgBegin() const {
+        return (Value *)(this + 1);
     }
 
     /* The three mutually exclusive states of a callstack */
 
     bool inContext() const {
         JS_ASSERT(!!cx == !!initialFrame);
-        JS_ASSERT_IF(!initialFrame, !suspendedFrame && !saved);
+        JS_ASSERT_IF(!initialFrame, !suspendedFrame() && !saved());
         return cx;
     }
 
     bool isActive() const {
-        JS_ASSERT_IF(suspendedFrame, inContext());
-        return initialFrame && !suspendedFrame;
+        JS_ASSERT_IF(suspendedFrame(), inContext());
+        return initialFrame && !suspendedFrame();
     }
 
     bool isSuspended() const {
-        JS_ASSERT_IF(!suspendedFrame, !saved);
-        JS_ASSERT_IF(suspendedFrame, inContext());
-        return suspendedFrame;
+        JS_ASSERT_IF(!suspendedFrame(), !saved());
+        JS_ASSERT_IF(suspendedFrame(), inContext());
+        return !!suspendedFrame();
     }
 
     /* Substate of suspended, queryable in any state. */
 
     bool isSaved() const {
-        JS_ASSERT_IF(saved, isSuspended());
-        return saved;
+        JS_ASSERT_IF(saved(), isSuspended());
+        return saved();
     }
 
     /* Transitioning between inContext <--> isActive */
 
     void joinContext(JSContext *cx, JSStackFrame *f) {
         JS_ASSERT(!inContext());
         this->cx = cx;
         initialFrame = f;
@@ -360,47 +369,47 @@ class CallStack
     JSContext *maybeContext() const {
         return cx;
     }
 
     /* Transitioning between isActive <--> isSuspended */
 
     void suspend(JSStackFrame *fp, JSFrameRegs *regs) {
         JS_ASSERT(fp && isActive() && contains(fp));
-        suspendedFrame = fp;
+        suspendedFrameAndSaved.set(fp, false);
         suspendedRegs = regs;
     }
 
     void resume() {
-        JS_ASSERT(suspendedFrame);
-        suspendedFrame = NULL;
+        JS_ASSERT(suspendedFrame());
+        suspendedFrameAndSaved.set(NULL, false);
     }
 
     /* When isSuspended, transitioning isSaved <--> !isSaved */
 
     void save(JSStackFrame *fp, JSFrameRegs *regs) {
-        JS_ASSERT(!saved);
+        JS_ASSERT(!saved());
         suspend(fp, regs);
-        saved = true;
+        suspendedFrameAndSaved.setFlag();
     }
 
     void restore() {
-        JS_ASSERT(saved);
-        saved = false;
+        JS_ASSERT(saved());
+        suspendedFrameAndSaved.unsetFlag();
         resume();
     }
 
     /* Data available when !inContext */
 
-    void setInitialArgEnd(jsval *v) {
+    void setInitialArgEnd(Value *v) {
         JS_ASSERT(!inContext() && !initialArgEnd);
         initialArgEnd = v;
     }
 
-    jsval *getInitialArgEnd() const {
+    Value *getInitialArgEnd() const {
         JS_ASSERT(!inContext() && initialArgEnd);
         return initialArgEnd;
     }
 
     /* Data available when inContext */
 
     JSStackFrame *getInitialFrame() const {
         JS_ASSERT(inContext());
@@ -408,25 +417,25 @@ class CallStack
     }
 
     inline JSStackFrame *getCurrentFrame() const;
 
     /* Data available when isSuspended. */
 
     JSStackFrame *getSuspendedFrame() const {
         JS_ASSERT(isSuspended());
-        return suspendedFrame;
+        return suspendedFrame();
     }
 
     JSFrameRegs *getSuspendedRegs() const {
         JS_ASSERT(isSuspended());
         return suspendedRegs;
     }
 
-    jsval *getSuspendedSP() const {
+    js::Value *getSuspendedSP() const {
         JS_ASSERT(isSuspended());
         return suspendedRegs->sp;
     }
 
     /* JSContext / js::StackSpace bookkeeping. */
 
     void setPreviousInContext(CallStack *cs) {
         previousInContext = cs;
@@ -455,36 +464,36 @@ class CallStack
     }
 
 #ifdef DEBUG
     JS_REQUIRES_STACK bool contains(const JSStackFrame *fp) const;
 #endif
 
 };
 
-JS_STATIC_ASSERT(sizeof(CallStack) % sizeof(jsval) == 0);
-static const size_t ValuesPerCallStack = sizeof(CallStack) / sizeof(jsval);
+JS_STATIC_ASSERT(sizeof(CallStack) % sizeof(Value) == 0);
+static const size_t ValuesPerCallStack = sizeof(CallStack) / sizeof(Value);
 
 /*
  * The ternary constructor is used when arguments are already pushed on the
  * stack (as the sp of the current frame), which should only happen from within
  * js_Interpret. Otherwise, see StackSpace::pushInvokeArgs. 
  */
 class InvokeArgsGuard
 {
     friend class StackSpace;
     JSContext       *cx;
     CallStack       *cs;  /* null implies nothing pushed */
-    jsval           *vp;
+    Value           *vp;
     uintN           argc;
   public:
     inline InvokeArgsGuard();
-    inline InvokeArgsGuard(jsval *vp, uintN argc);
+    inline InvokeArgsGuard(Value *vp, uintN argc);
     inline ~InvokeArgsGuard();
-    jsval *getvp() const { return vp; }
+    Value *getvp() const { return vp; }
     uintN getArgc() const { JS_ASSERT(vp != NULL); return argc; }
 };
 
 /* See StackSpace::pushInvokeFrame. */
 class InvokeFrameGuard
 {
     friend class StackSpace;
     JSContext       *cx;  /* null implies nothing pushed */
@@ -497,23 +506,23 @@ class InvokeFrameGuard
 };
 
 /* See StackSpace::pushExecuteFrame. */
 class ExecuteFrameGuard
 {
     friend class StackSpace;
     JSContext       *cx;  /* null implies nothing pushed */
     CallStack       *cs;
-    jsval           *vp;
+    Value           *vp;
     JSStackFrame    *fp;
     JSStackFrame    *down;
   public:
     ExecuteFrameGuard();
     JS_REQUIRES_STACK ~ExecuteFrameGuard();
-    jsval *getvp() const { return vp; }
+    Value *getvp() const { return vp; }
     JSStackFrame *getFrame() const { return fp; }
 };
 
 /*
  * Thread stack layout
  *
  * Each JSThreadData has one associated StackSpace object which allocates all
  * callstacks for the thread. StackSpace performs all such allocations in a
@@ -532,17 +541,17 @@ class ExecuteFrameGuard
  *            initial frame                 current frame ------.  if regs,
  *           .------------.                           |         |  regs->sp
  *           |            V                           V         V
  *   |callstack| slots |frame| slots |frame| slots |frame| slots|
  *                       |  ^          |  ^          |
  *                ? <----'  `----------'  `----------'
  *
  * Moreover, the bytes between a callstack and its first frame and between two
- * adjacent frames in a callstack are GC-able jsvals. If the current frame's
+ * adjacent frames in a callstack are GC-able Values. If the current frame's
  * regs pointer is null (e.g., native frame), there are no final slots.
  *
  * An empty callstack roots the initial slots before the initial frame is
  * pushed and after the initial frame has been popped (perhaps to be followed
  * by subsequent initial frame pushes/pops...).
  *
  *           initialArgEnd
  *           .---------.
@@ -565,56 +574,56 @@ class ExecuteFrameGuard
  * Since different contexts can arbitrarily interleave execution in a single
  * thread, these stacks are different enough that a callstack needs both
  * "previousInThread" and "previousInContext". (Not completely different; there
  * is an order-preserving injection from each context's callstack-ordering to
  * that of the context's thread.)
  */
 class StackSpace
 {
-    jsval *base;
+    Value *base;
 #ifdef XP_WIN
-    mutable jsval *commitEnd;
+    mutable Value *commitEnd;
 #endif
-    jsval *end;
+    Value *end;
     CallStack *currentCallStack;
 
     /* Although guards are friends, XGuard should only call popX(). */
     friend class InvokeArgsGuard;
-    JS_REQUIRES_STACK inline void popInvokeArgs(JSContext *cx, jsval *vp);
+    JS_REQUIRES_STACK inline void popInvokeArgs(JSContext *cx, Value *vp);
     friend class InvokeFrameGuard;
     JS_REQUIRES_STACK void popInvokeFrame(JSContext *cx, CallStack *maybecs);
     friend class ExecuteFrameGuard;
     JS_REQUIRES_STACK void popExecuteFrame(JSContext *cx);
 
     /* Return a pointer to the first unused slot. */
     JS_REQUIRES_STACK
-    inline jsval *firstUnused() const;
+    inline Value *firstUnused() const;
 
 #ifdef DEBUG
     inline bool isCurrent(JSContext *cx) const;
     CallStack *getCurrentCallStack() const { return currentCallStack; }
 #endif
 
     /*
      * Return whether nvals can be allocated from the top of the stack.
      * N.B. the caller must ensure |from == firstUnused()|.
      */
-    inline bool ensureSpace(JSContext *maybecx, jsval *from, ptrdiff_t nvals) const;
+    inline bool ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
 
 #ifdef XP_WIN
     /* Commit more memory from the reserved stack space. */
-    JS_FRIEND_API(bool) bumpCommit(jsval *from, ptrdiff_t nvals) const;
+    JS_FRIEND_API(bool) bumpCommit(Value *from, ptrdiff_t nvals) const;
 #endif
 
   public:
     static const size_t sCapacityVals   = 512 * 1024;
-    static const size_t sCapacityBytes  = sCapacityVals * sizeof(jsval);
+    static const size_t sCapacityBytes  = sCapacityVals * sizeof(Value);
     static const size_t sCommitVals     = 16 * 1024;
-    static const size_t sCommitBytes    = sCommitVals * sizeof(jsval);
+    static const size_t sCommitBytes    = sCommitVals * sizeof(Value);
 
     JS_STATIC_ASSERT(sCapacityVals % sCommitVals == 0);
 
     /* Kept as a member of JSThreadData; cannot use constructor/destructor. */
     bool init();
     void finish();
 
 #ifdef DEBUG
@@ -689,17 +698,17 @@ class StackSpace
     void pushExecuteFrame(JSContext *cx, ExecuteFrameGuard &fg,
                           JSFrameRegs &regs, JSObject *initialVarObj);
 
     /*
      * Since RAII cannot be used for inline frames, callers must manually
      * call pushInlineFrame/popInlineFrame.
      */
     JS_REQUIRES_STACK
-    inline JSStackFrame *getInlineFrame(JSContext *cx, jsval *sp,
+    inline JSStackFrame *getInlineFrame(JSContext *cx, Value *sp,
                                         uintN nmissing, uintN nslots) const;
 
     JS_REQUIRES_STACK
     void pushInlineFrame(JSContext *cx, JSStackFrame *fp, jsbytecode *pc,
                          JSStackFrame *newfp);
 
     JS_REQUIRES_STACK
     inline void popInlineFrame(JSContext *cx, JSStackFrame *up, JSStackFrame *down);
@@ -732,27 +741,27 @@ class StackSpace
  * Usage:
  *   for (FrameRegsIter i(cx); !i.done(); ++i)
  *     ... i.fp() ... i.sp() ... i.pc()
  */
 class FrameRegsIter
 {
     CallStack         *curcs;
     JSStackFrame      *curfp;
-    jsval             *cursp;
+    Value             *cursp;
     jsbytecode        *curpc;
 
   public:
     JS_REQUIRES_STACK FrameRegsIter(JSContext *cx);
 
     bool done() const { return curfp == NULL; }
     FrameRegsIter &operator++();
 
     JSStackFrame *fp() const { return curfp; }
-    jsval *sp() const { return cursp; }
+    Value *sp() const { return cursp; }
     jsbytecode *pc() const { return curpc; }
 };
 
 /* Holds the number of recording attemps for an address. */
 typedef HashMap<jsbytecode*,
                 size_t,
                 DefaultHasher<jsbytecode*>,
                 SystemAllocPolicy> RecordAttemptMap;
@@ -918,17 +927,17 @@ struct JSFunctionMeter {
 
 struct JSLocalRootChunk;
 
 #define JSLRS_CHUNK_SHIFT       8
 #define JSLRS_CHUNK_SIZE        JS_BIT(JSLRS_CHUNK_SHIFT)
 #define JSLRS_CHUNK_MASK        JS_BITMASK(JSLRS_CHUNK_SHIFT)
 
 struct JSLocalRootChunk {
-    jsval               roots[JSLRS_CHUNK_SIZE];
+    void                *roots[JSLRS_CHUNK_SIZE];
     JSLocalRootChunk    *down;
 };
 
 struct JSLocalRootStack {
     uint32              scopeMark;
     uint32              rootCount;
     JSLocalRootChunk    *topChunk;
     JSLocalRootChunk    firstChunk;
@@ -1136,22 +1145,34 @@ struct JSClassProtoCache {
 
     GlobalAndProto  entries[JSProto_LIMIT - JSProto_Object];
 
 #ifdef __GNUC__
 # pragma GCC visibility push(default)
 #endif
     friend JSBool js_GetClassPrototype(JSContext *cx, JSObject *scope,
                                        JSProtoKey protoKey, JSObject **protop,
-                                       JSClass *clasp);
+                                       js::Class *clasp);
 #ifdef __GNUC__
 # pragma GCC visibility pop
 #endif
 };
 
+struct JSRootInfo {
+    JSRootInfo() {}
+    JSRootInfo(const char *name, JSGCRootType type) : name(name), type(type) {}
+    const char *name;
+    JSGCRootType type;
+};
+
+typedef js::HashMap<void *,
+                    JSRootInfo,
+                    js::DefaultHasher<void *>,
+                    js::SystemAllocPolicy> JSRootedValueMap;
+
 struct JSRuntime {
     /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */
     JSRuntimeState      state;
 
     /* Context create/destroy callback. */
     JSContextCallback   cxCallback;
 
     /*
@@ -1167,17 +1188,17 @@ struct JSRuntime {
      * trace-JITted code that reads it.
      */
     uint32              protoHazardShape;
 
     /* Garbage collector state, used by jsgc.c. */
     JSGCChunkInfo       *gcChunkList;
     JSGCArenaList       gcArenaList[FINALIZE_LIMIT];
     JSGCDoubleArenaList gcDoubleArenaList;
-    JSDHashTable        gcRootsHash;
+    JSRootedValueMap    gcRootsHash;
     JSDHashTable        gcLocksHash;
     jsrefcount          gcKeepAtoms;
     size_t              gcBytes;
     size_t              gcLastBytes;
     size_t              gcMaxBytes;
     size_t              gcMaxMallocBytes;
     uint32              gcEmptyArenaPoolLifespan;
     uint32              gcLevel;
@@ -1244,19 +1265,19 @@ struct JSRuntime {
      * Used to serialize cycle checks when setting __proto__ or __parent__ by
      * requesting the GC handle the required cycle detection. If the GC hasn't
      * been poked, it won't scan for garbage. This member is protected by
      * rt->gcLock.
      */
     JSSetSlotRequest    *setSlotRequests;
 
     /* Well-known numbers held for use by this runtime's contexts. */
-    jsval               NaNValue;
-    jsval               negativeInfinityValue;
-    jsval               positiveInfinityValue;
+    js::Value           NaNValue;
+    js::Value           negativeInfinityValue;
+    js::Value           positiveInfinityValue;
 
     js::DeflatedStringCache *deflatedStringCache;
 
     JSString            *emptyString;
 
     /*
      * Builtin functions, lazily created and held for use by the trace recorder.
      *
@@ -1635,33 +1656,33 @@ struct JSContext
     uint32              options;            /* see jsapi.h for JSOPTION_* */
 
     /* Locale specific callbacks for string conversion. */
     JSLocaleCallbacks   *localeCallbacks;
 
     /*
      * cx->resolvingTable is non-null and non-empty if we are initializing
      * standard classes lazily, or if we are otherwise recursing indirectly
-     * from js_LookupProperty through a JSClass.resolve hook.  It is used to
+     * from js_LookupProperty through a Class.resolve hook.  It is used to
      * limit runaway recursion (see jsapi.c and jsobj.c).
      */
     JSDHashTable        *resolvingTable;
 
     /*
      * True if generating an error, to prevent runaway recursion.
      * NB: generatingError packs with insideGCMarkCallback and throwing below.
      */
     JSPackedBool        generatingError;
 
     /* Flag to indicate that we run inside gcCallback(cx, JSGC_MARK_END). */
     JSPackedBool        insideGCMarkCallback;
 
     /* Exception state -- the exception member is a GC root by definition. */
     JSPackedBool        throwing;           /* is there a pending exception? */
-    jsval               exception;          /* most-recently-thrown exception */
+    js::Value           exception;          /* most-recently-thrown exception */
 
     /* Limit pointer for checking native stack consumption during recursion. */
     jsuword             stackLimit;
 
     /* Quota on the size of arenas used to compile and execute scripts. */
     size_t              scriptStackQuota;
 
     /* Data shared by threads in an address space. */
@@ -1675,17 +1696,17 @@ struct JSContext
      * Currently executing frame's regs, set by stack operations.
      * |fp != NULL| iff |regs != NULL| (although regs->pc can be NULL)
      */
     JS_REQUIRES_STACK
     JSFrameRegs         *regs;
 
   private:
     friend class js::StackSpace;
-    friend JSBool js_Interpret(JSContext *);
+    friend bool js::Interpret(JSContext *);
 
     /* 'fp' and 'regs' must only be changed by calling these functions. */
     void setCurrentFrame(JSStackFrame *fp) {
         this->fp = fp;
     }
 
     void setCurrentRegs(JSFrameRegs *regs) {
         this->regs = regs;
@@ -2029,18 +2050,18 @@ struct JSContext
     void purge();
 
     js::StackSpace &stack() const {
         return JS_THREAD_DATA(this)->stackSpace;
     }
 
 #ifdef DEBUG
     void assertValidStackDepth(uintN depth) {
-        JS_ASSERT(0 <= regs->sp - StackBase(fp));
-        JS_ASSERT(depth <= uintptr_t(regs->sp - StackBase(fp)));
+        JS_ASSERT(0 <= regs->sp - fp->base());
+        JS_ASSERT(depth <= uintptr_t(regs->sp - fp->base()));
     }
 #else
     void assertValidStackDepth(uintN /*depth*/) {}
 #endif
 
 private:
 
     /*
@@ -2119,17 +2140,17 @@ class AutoGCRooter {
 # pragma GCC visibility pop
 #endif
 
   protected:
     AutoGCRooter * const down;
 
     /*
      * Discriminates actual subclass of this being used.  If non-negative, the
-     * subclass roots an array of jsvals of the length stored in this field.
+     * subclass roots an array of values of the length stored in this field.
      * If negative, meaning is indicated by the corresponding value in the enum
      * below.  Any other negative value indicates some deeper problem such as
      * memory corruption.
      */
     ptrdiff_t tag;
 
     JSContext * const context;
 
@@ -2166,66 +2187,67 @@ class AutoSaveWeakRoots : private AutoGC
     JSWeakRoots savedRoots;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /* FIXME(bug 332648): Move this into a public header. */
 class AutoValueRooter : private AutoGCRooter
 {
   public:
-    explicit AutoValueRooter(JSContext *cx, jsval v = JSVAL_NULL
+    explicit AutoValueRooter(JSContext *cx
                              JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, JSVAL), val(v)
+      : AutoGCRooter(cx, JSVAL), val(js::NullValue())
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
+    AutoValueRooter(JSContext *cx, const Value &v
+                             JS_GUARD_OBJECT_NOTIFIER_PARAM)
+      : AutoGCRooter(cx, JSVAL)
+    {
+        val.copy(v);
+        JS_GUARD_OBJECT_NOTIFIER_INIT;
+    }
     AutoValueRooter(JSContext *cx, JSString *str
                     JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, JSVAL), val(STRING_TO_JSVAL(str))
+      : AutoGCRooter(cx, JSVAL), val(str)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
-    AutoValueRooter(JSContext *cx, JSObject *obj
-                    JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, JSVAL), val(OBJECT_TO_JSVAL(obj))
-    {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    void setObject(JSObject *obj) {
-        JS_ASSERT(tag == JSVAL);
-        val = OBJECT_TO_JSVAL(obj);
-    }
+    /*
+     * If you are looking for Object* overloads, use AutoObjectRooter instead;
+     * rooting Object*s as a js::Value requires discerning whether or not it is
+     * a function object. Also, AutoObjectRooter is smaller.
+     */
 
     void setString(JSString *str) {
         JS_ASSERT(tag == JSVAL);
         JS_ASSERT(str);
-        val = STRING_TO_JSVAL(str);
+        val.setString(str);
     }
 
-    void setDouble(jsdouble *dp) {
+    void setDouble(jsdouble d) {
         JS_ASSERT(tag == JSVAL);
-        JS_ASSERT(dp);
-        val = DOUBLE_TO_JSVAL(dp);
+        JS_ASSERT(d);
+        val.setDouble(d);
     }
 
-    jsval value() const {
+    const Value &value() const {
         JS_ASSERT(tag == JSVAL);
         return val;
     }
 
-    jsval *addr() {
+    Value *addr() {
         JS_ASSERT(tag == JSVAL);
         return &val;
     }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
   private:
-    jsval val;
+    Value val;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoObjectRooter : private AutoGCRooter {
   public:
     AutoObjectRooter(JSContext *cx, JSObject *obj = NULL
                      JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, OBJECT), obj(obj)
@@ -2249,35 +2271,35 @@ class AutoObjectRooter : private AutoGCR
 
   private:
     JSObject *obj;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoArrayRooter : private AutoGCRooter {
   public:
-    AutoArrayRooter(JSContext *cx, size_t len, jsval *vec
+    AutoArrayRooter(JSContext *cx, size_t len, Value *vec
                     JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, len), array(vec)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
         JS_ASSERT(tag >= 0);
     }
 
     void changeLength(size_t newLength) {
         tag = ptrdiff_t(newLength);
         JS_ASSERT(tag >= 0);
     }
 
-    void changeArray(jsval *newArray, size_t newLength) {
+    void changeArray(Value *newArray, size_t newLength) {
         changeLength(newLength);
         array = newArray;
     }
 
-    jsval *array;
+    Value *array;
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
   private:
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoScopePropertyRooter : private AutoGCRooter {
@@ -2380,47 +2402,47 @@ class AutoIdArray : private AutoGCRooter
 };
 
 /* The auto-root for enumeration object and its state. */
 class AutoEnumStateRooter : private AutoGCRooter
 {
   public:
     AutoEnumStateRooter(JSContext *cx, JSObject *obj
                         JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, ENUMERATOR), obj(obj), stateValue(JSVAL_NULL)
+      : AutoGCRooter(cx, ENUMERATOR), obj(obj), stateValue()
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
         JS_ASSERT(obj);
     }
 
     ~AutoEnumStateRooter() {
-        if (!JSVAL_IS_NULL(stateValue)) {
+        if (!stateValue.isNull()) {
 #ifdef DEBUG
             JSBool ok =
 #endif
             obj->enumerate(context, JSENUMERATE_DESTROY, &stateValue, 0);
             JS_ASSERT(ok);
         }
     }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
-    jsval state() const { return stateValue; }
-    jsval * addr() { return &stateValue; }
+    const Value &state() const { return stateValue; }
+    Value *addr() { return &stateValue; }
 
   protected:
     void trace(JSTracer *trc) {
         JS_CALL_OBJECT_TRACER(trc, obj, "js::AutoEnumStateRooter.obj");
         js_MarkEnumeratorState(trc, obj, stateValue);
     }
 
     JSObject * const obj;
 
   private:
-    jsval stateValue;
+    Value stateValue;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 #ifdef JS_HAS_XML_SUPPORT
 class AutoXMLRooter : private AutoGCRooter {
   public:
     AutoXMLRooter(JSContext *cx, JSXML *xml)
       : AutoGCRooter(cx, XML), xml(xml)
@@ -2619,48 +2641,43 @@ js_WaitForGC(JSRuntime *rt);
 
 #else /* !JS_THREADSAFE */
 
 # define js_WaitForGC(rt)    ((void) 0)
 
 #endif
 
 /*
- * JSClass.resolve and watchpoint recursion damping machinery.
+ * Class.resolve and watchpoint recursion damping machinery.
  */
 extern JSBool
 js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
                   JSResolvingEntry **entryp);
 
 extern void
 js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
                  JSResolvingEntry *entry, uint32 generation);
 
 /*
  * Local root set management.
- *
- * NB: the jsval parameters below may be properly tagged jsvals, or GC-thing
- * pointers cast to (jsval).  This relies on JSObject's tag being zero, but
- * on the up side it lets us push int-jsval-encoded scopeMark values on the
- * local root stack.
  */
 extern JSBool
 js_EnterLocalRootScope(JSContext *cx);
 
 #define js_LeaveLocalRootScope(cx) \
-    js_LeaveLocalRootScopeWithResult(cx, JSVAL_NULL)
+    js_LeaveLocalRootScopeWithResult(cx, sNullValue)
 
 extern void
-js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval);
+js_LeaveLocalRootScopeWithResult(JSContext *cx, const js::Value &rval);
 
 extern void
-js_ForgetLocalRoot(JSContext *cx, jsval v);
+js_ForgetLocalRoot(JSContext *cx, void *thing);
 
 extern int
-js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v);
+js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, void *thing);
 
 /*
  * Report an exception, which is currently realized as a printf-style format
  * string and its arguments.
  */
 typedef enum JSErrNum {
 #define MSG_DEF(name, number, count, exception, format) \
     name = number,
@@ -2722,30 +2739,30 @@ js_ReportErrorAgain(JSContext *cx, const
 
 extern void
 js_ReportIsNotDefined(JSContext *cx, const char *name);
 
 /*
  * Report an attempt to access the property of a null or undefined value (v).
  */
 extern JSBool
-js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v,
+js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, const js::Value &v,
                            JSString *fallback);
 
 extern void
-js_ReportMissingArg(JSContext *cx, jsval *vp, uintN arg);
+js_ReportMissingArg(JSContext *cx, const js::Value &v, uintN arg);
 
 /*
  * Report error using js_DecompileValueGenerator(cx, spindex, v, fallback) as
  * the first argument for the error message. If the error message has less
  * then 3 arguments, use null for arg1 or arg2.
  */
 extern JSBool
 js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
-                         intN spindex, jsval v, JSString *fallback,
+                         intN spindex, const js::Value &v, JSString *fallback,
                          const char *arg1, const char *arg2);
 
 #define js_ReportValueError(cx,errorNumber,spindex,v,fallback)                \
     ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,          \
                                     spindex, v, fallback, NULL, NULL))
 
 #define js_ReportValueError2(cx,errorNumber,spindex,v,fallback,arg1)          \
     ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,          \
@@ -2820,17 +2837,17 @@ LeaveTrace(JSContext *cx)
     if (JS_ON_TRACE(cx))
         DeepBail(cx);
 #endif
 }
 
 static JS_INLINE void
 LeaveTraceIfGlobalObject(JSContext *cx, JSObject *obj)
 {
-    if (!obj->fslots[JSSLOT_PARENT])
+    if (obj->fslots[JSSLOT_PARENT].isNull())
         LeaveTrace(cx);
 }
 
 static JS_INLINE JSBool
 CanLeaveTrace(JSContext *cx)
 {
     JS_ASSERT(JS_ON_TRACE(cx));
 #ifdef JS_TRACER
@@ -2911,43 +2928,43 @@ class AutoValueVector : private AutoGCRo
                              JS_GUARD_OBJECT_NOTIFIER_PARAM)
         : AutoGCRooter(cx, VECTOR), vector(cx)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     size_t length() const { return vector.length(); }
 
-    bool push(jsval v) { return vector.append(v); }
-    bool push(JSString *str) { return push(STRING_TO_JSVAL(str)); }
-    bool push(JSObject *obj) { return push(OBJECT_TO_JSVAL(obj)); }
-    bool push(jsdouble *dp) { return push(DOUBLE_TO_JSVAL(dp)); }
+    bool push(const js::Value &v) { return vector.append(v); }
+    bool push(JSString *str) { return push(str); }
+    bool push(JSObject *obj) { return push(obj); }
+    bool push(jsdouble d) { return push(d); }
 
     void pop() { vector.popBack(); }
 
     bool resize(size_t newLength) {
         if (!vector.resize(newLength))
             return false;
         return true;
     }
 
     bool reserve(size_t newLength) {
         return vector.reserve(newLength);
     }
 
-    jsval &operator[](size_t i) { return vector[i]; }
-    jsval operator[](size_t i) const { return vector[i]; }
-
-    const jsval *buffer() const { return vector.begin(); }
-    jsval *buffer() { return vector.begin(); }
+    const js::Value &operator[](size_t i) { return vector[i]; }
+    const js::Value &operator[](size_t i) const { return vector[i]; }
+
+    const js::Value *buffer() const { return vector.begin(); }
+    const js::Value *buffer() { return vector.begin(); }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
   private:
-    Vector<jsval, 8> vector;
+    Vector<js::Value, 8> vector;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 }
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #pragma warning(pop)
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -50,17 +50,17 @@ namespace js {
 
 JS_REQUIRES_STACK JS_ALWAYS_INLINE JSStackFrame *
 CallStack::getCurrentFrame() const
 {
     JS_ASSERT(inContext());
     return isSuspended() ? getSuspendedFrame() : cx->fp;
 }
 
-JS_REQUIRES_STACK inline jsval *
+JS_REQUIRES_STACK inline Value *
 StackSpace::firstUnused() const
 {
     CallStack *ccs = currentCallStack;
     if (!ccs)
         return base;
     if (JSContext *cx = ccs->maybeContext()) {
         if (!ccs->isSuspended())
             return cx->regs->sp;
@@ -77,17 +77,17 @@ StackSpace::isCurrent(JSContext *cx) con
     JS_ASSERT(cx == currentCallStack->maybeContext());
     JS_ASSERT(cx->getCurrentCallStack() == currentCallStack);
     JS_ASSERT(cx->callStackInSync());
     return true;
 }
 #endif
 
 JS_ALWAYS_INLINE bool
-StackSpace::ensureSpace(JSContext *maybecx, jsval *from, ptrdiff_t nvals) const
+StackSpace::ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
 {
     JS_ASSERT(from == firstUnused());
 #ifdef XP_WIN
     JS_ASSERT(from <= commitEnd);
     if (commitEnd - from >= nvals)
         return true;
     if (end - from < nvals) {
         if (maybecx)
@@ -115,24 +115,24 @@ StackSpace::ensureEnoughSpaceToEnterTrac
 {
 #ifdef XP_WIN
     return ensureSpace(NULL, firstUnused(), sMaxJSValsNeededForTrace);
 #endif
     return end - firstUnused() > sMaxJSValsNeededForTrace;
 }
 
 JS_ALWAYS_INLINE void
-StackSpace::popInvokeArgs(JSContext *cx, jsval *vp)
+StackSpace::popInvokeArgs(JSContext *cx, Value *vp)
 {
     JS_ASSERT(!currentCallStack->inContext());
     currentCallStack = currentCallStack->getPreviousInThread();
 }
 
 JS_REQUIRES_STACK JS_ALWAYS_INLINE JSStackFrame *
-StackSpace::getInlineFrame(JSContext *cx, jsval *sp,
+StackSpace::getInlineFrame(JSContext *cx, Value *sp,
                            uintN nmissing, uintN nslots) const
 {
     JS_ASSERT(isCurrent(cx) && cx->hasActiveCallStack());
     JS_ASSERT(cx->regs->sp == sp);
 
     ptrdiff_t nvals = nmissing + ValuesPerStackFrame + nslots;
     if (!ensureSpace(cx, sp, nvals))
         return NULL;
@@ -177,33 +177,33 @@ StackSpace::popInlineFrame(JSContext *cx
  * issues, force InvokeArgsGuard members inline:
  */
 JS_ALWAYS_INLINE
 InvokeArgsGuard::InvokeArgsGuard()
   : cx(NULL), cs(NULL), vp(NULL)
 {}
 
 JS_ALWAYS_INLINE
-InvokeArgsGuard::InvokeArgsGuard(jsval *vp, uintN argc)
+InvokeArgsGuard::InvokeArgsGuard(Value *vp, uintN argc)
   : cx(NULL), cs(NULL), vp(vp), argc(argc)
 {}
 
 JS_ALWAYS_INLINE
 InvokeArgsGuard::~InvokeArgsGuard()
 {
     if (!cs)
         return;
     JS_ASSERT(cs == cx->stack().getCurrentCallStack());
     cx->stack().popInvokeArgs(cx, vp);
 }
 
 void
 AutoIdArray::trace(JSTracer *trc) {
     JS_ASSERT(tag == IDARRAY);
-    js::TraceValues(trc, idArray->length, idArray->vector, "JSAutoIdArray.idArray");
+    TraceIds(trc, idArray->length, idArray->vector, "JSAutoIdArray.idArray");
 }
 
 class AutoNamespaces : protected AutoGCRooter {
   protected:
     AutoNamespaces(JSContext *cx) : AutoGCRooter(cx, NAMESPACES) {
     }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
@@ -213,17 +213,17 @@ class AutoNamespaces : protected AutoGCR
 };
 
 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);
+        CallGCMarkerIfGCThing(trc, static_cast<AutoValueRooter *>(this)->val);
         return;
 
       case SPROP:
         static_cast<AutoScopePropertyRooter *>(this)->sprop->trace(trc);
         return;
 
       case WEAKROOTS:
         static_cast<AutoSaveWeakRoots *>(this)->savedRoots.mark(trc);
@@ -239,29 +239,32 @@ 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");
+        TraceIds(trc, ida->length, ida->vector, "js::AutoIdArray.idArray");
         return;
       }
 
       case DESCRIPTORS: {
         PropertyDescriptorArray &descriptors =
             static_cast<AutoDescriptorArray *>(this)->descriptors;
         for (size_t i = 0, len = descriptors.length(); i < len; i++) {
             PropertyDescriptor &desc = descriptors[i];
 
-            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_SET_TRACING_NAME(trc, "PropertyDescriptor::value");
+            CallGCMarkerIfGCThing(trc, desc.value);
+            JS_SET_TRACING_NAME(trc, "PropertyDescriptor::get");
+            CallGCMarkerIfGCThing(trc, desc.get);
+            JS_SET_TRACING_NAME(trc, "PropertyDescriptor::set");
+            CallGCMarkerIfGCThing(trc, desc.set);
             js_TraceId(trc, desc.id);
         }
         return;
       }
 
       case NAMESPACES: {
         JSXMLArray &array = static_cast<AutoNamespaces *>(this)->array;
         TraceObjectVector(trc, reinterpret_cast<JSObject **>(array.vector), array.length);
@@ -271,31 +274,31 @@ AutoGCRooter::trace(JSTracer *trc)
 
       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);
+            CallGCMarker(trc, obj, JSTRACE_OBJECT);
         }
         return;
 
       case ID:
         JS_SET_TRACING_NAME(trc, "js::AutoIdRooter.val");
-        js_CallValueTracerIfGCThing(trc, static_cast<AutoIdRooter *>(this)->idval);
+        CallGCMarkerIfGCThing(trc, static_cast<AutoIdRooter *>(this)->idval);
         return;
 
       case VECTOR: {
-        js::Vector<jsval, 8> &vector = static_cast<js::AutoValueVector *>(this)->vector;
-        js::TraceValues(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector");
+        Vector<Value, 8> &vector = static_cast<AutoValueVector *>(this)->vector;
+        TraceValues(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector");
         return;
       }
     }
 
     JS_ASSERT(tag >= 0);
     TraceValues(trc, tag, static_cast<AutoArrayRooter *>(this)->array, "js::AutoArrayRooter.array");
 }
 
-}
+}  /* namespace js */
 
 #endif /* jscntxtinlines_h___ */
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -33,16 +33,19 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+
+#define __STDC_LIMIT_MACROS
+
 /*
  * JS date methods.
  */
 
 /*
  * "For example, OS/360 devotes 26 bytes of the permanently
  *  resident date-turnover routine to the proper handling of
  *  December 31 on leap years (when it is Day 366).  That
@@ -66,16 +69,18 @@
 #include "jsbuiltins.h"
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jsinterp.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsstr.h"
 
+#include "jsobjinlines.h"
+
 using namespace js;
 
 /*
  * The JS 'Date' object is patterned after the Java 'Date' object.
  * Here is an script:
  *
  *    today = new Date();
  *
@@ -494,22 +499,22 @@ msFromTime(jsdouble t)
  * We use the first reseved slot to store UTC time, and the second for caching
  * the local time. The initial value of the cache entry is NaN.
  */
 const uint32 JSSLOT_UTC_TIME    = JSSLOT_PRIVATE;
 const uint32 JSSLOT_LOCAL_TIME  = JSSLOT_PRIVATE + 1;
 
 const uint32 DATE_RESERVED_SLOTS = 2;
 
-JSClass js_DateClass = {
+Class js_DateClass = {
     js_Date_str,
     JSCLASS_HAS_RESERVED_SLOTS(DATE_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
-    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
-    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   NULL,
+    PropertyStub,  PropertyStub,  PropertyStub,  PropertyStub,
+    EnumerateStub, ResolveStub,   ConvertStub,   NULL,
     JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 /* for use by date_parse */
 
 static const char* wtb[] = {
     "am", "pm",
     "monday", "tuesday", "wednesday", "thursday", "friday",
@@ -578,26 +583,26 @@ date_msecFromDate(jsdouble year, jsdoubl
     result = MakeDate(day, msec_time);
     return result;
 }
 
 /* compute the time in msec (unclipped) from the given args */
 #define MAXARGS        7
 
 static JSBool
-date_msecFromArgs(JSContext *cx, uintN argc, jsval *argv, jsdouble *rval)
+date_msecFromArgs(JSContext *cx, uintN argc, Value *argv, jsdouble *rval)
 {
     uintN loop;
     jsdouble array[MAXARGS];
     jsdouble msec_time;
 
     for (loop = 0; loop < MAXARGS; loop++) {
         if (loop < argc) {
             jsdouble d;
-            if (!ValueToNumber(cx, argv[loop], &d))
+            if (!ValueToNumber(cx, &argv[loop], &d))
                 return JS_FALSE;
             /* return NaN if any arg is not finite */
             if (!JSDOUBLE_IS_FINITE(d)) {
                 *rval = js_NaN;
                 return JS_TRUE;
             }
             array[loop] = js_DoubleToInteger(d);
         } else {
@@ -618,26 +623,27 @@ date_msecFromArgs(JSContext *cx, uintN a
     *rval = msec_time;
     return JS_TRUE;
 }
 
 /*
  * See ECMA 15.9.4.[3-10];
  */
 static JSBool
-date_UTC(JSContext *cx, uintN argc, jsval *vp)
+date_UTC(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble msec_time;
 
     if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time))
         return JS_FALSE;
 
     msec_time = TIMECLIP(msec_time);
 
-    return js_NewNumberInRootedValue(cx, msec_time, vp);
+    vp->setDouble(msec_time);
+    return JS_TRUE;
 }
 
 /*
  * Read and convert decimal digits from s[*i] into *result
  * while *i < limit. 
  * 
  * Succeed if any digits are converted. Advance *i only
  * as digits are consumed.
@@ -1154,414 +1160,433 @@ date_parseString(JSString *str, jsdouble
 
 syntax:
     /* syntax error */
     *result = 0;
     return JS_FALSE;
 }
 
 static JSBool
-date_parse(JSContext *cx, uintN argc, jsval *vp)
+date_parse(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *str;
     jsdouble result;
 
     if (argc == 0) {
-        *vp = cx->runtime->NaNValue;
+        vp->setDouble(js_NaN);
         return true;
     }
     str = js_ValueToString(cx, vp[2]);
     if (!str)
         return JS_FALSE;
-    vp[2] = STRING_TO_JSVAL(str);
+    vp[2].setString(str);
     if (!date_parseString(str, &result)) {
-        *vp = cx->runtime->NaNValue;
+        vp->setDouble(js_NaN);
         return true;
     }
 
     result = TIMECLIP(result);
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return true;
 }
 
 static inline jsdouble
 NowAsMillis()
 {
     return (jsdouble) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
 }
 
 static JSBool
-date_now(JSContext *cx, uintN argc, jsval *vp)
+date_now(JSContext *cx, uintN argc, Value *vp)
 {
-    return js_NewDoubleInRootedValue(cx, NowAsMillis(), vp);
+    vp->setDouble(NowAsMillis());
+    return JS_TRUE;
 }
 
 #ifdef JS_TRACER
 static jsdouble FASTCALL
 date_now_tn(JSContext*)
 {
     return NowAsMillis();
 }
 #endif
 
 /*
  * Get UTC time from the date object. Returns false if the object is not
  * Date type.
  */
 static JSBool
-GetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
+GetUTCTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *dp)
 {
-    if (!JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
+    if (!InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
         return JS_FALSE;
-    *dp = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
+    *dp = obj->fslots[JSSLOT_UTC_TIME].asDouble();
     return JS_TRUE;
 }
 
 static void
-SetDateToNaN(JSContext *cx, JSObject *obj, jsval *vp = NULL)
+SetDateToNaN(JSContext *cx, JSObject *obj, Value *vp = NULL)
 {
     JS_ASSERT(obj->getClass() == &js_DateClass);
 
-    obj->fslots[JSSLOT_LOCAL_TIME] = cx->runtime->NaNValue;
-    obj->fslots[JSSLOT_UTC_TIME] = cx->runtime->NaNValue;
+    obj->fslots[JSSLOT_LOCAL_TIME].setDouble(js_NaN);
+    obj->fslots[JSSLOT_UTC_TIME].setDouble(js_NaN);
     if (vp)
-        *vp = cx->runtime->NaNValue;
+        vp->setDouble(js_NaN);
 }
 
 /*
  * Set UTC time to a given time and invalidate cached local time.
  */
 static JSBool
-SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, jsval *vp = NULL)
+SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, Value *vp = NULL)
 {
     JS_ASSERT(obj->getClass() == &js_DateClass);
 
-    obj->fslots[JSSLOT_LOCAL_TIME] = cx->runtime->NaNValue;
-    if (!js_NewDoubleInRootedValue(cx, t, &obj->fslots[JSSLOT_UTC_TIME]))
-        return false;
+    obj->fslots[JSSLOT_LOCAL_TIME].setDouble(js_NaN);
+    obj->fslots[JSSLOT_UTC_TIME].setDouble(t);
     if (vp)
-        *vp = obj->fslots[JSSLOT_UTC_TIME];
+        vp->setDouble(t);
     return true;
 }
 
 /*
  * Get the local time, cache it if necessary. If UTC time is not finite
  * (e.g., NaN), the local time slot is set to the UTC time without conversion.
  */
 static JSBool
-GetAndCacheLocalTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
+GetAndCacheLocalTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *dp)
 {
-    if (!obj || !JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
+    if (!obj || !InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
         return false;
 
-    jsval *slotp = &obj->fslots[JSSLOT_LOCAL_TIME];
-    jsdouble result = *JSVAL_TO_DOUBLE(*slotp);
+    Value &slot = obj->fslots[JSSLOT_LOCAL_TIME];
+    jsdouble result = slot.asDouble();
     if (JSDOUBLE_IS_NaN(result)) {
-        result = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
+        result = obj->fslots[JSSLOT_UTC_TIME].asDouble();
 
         /* if result is NaN, it couldn't be finite. */
         if (JSDOUBLE_IS_FINITE(result))
             result = LocalTime(result);
 
-        if (!js_NewDoubleInRootedValue(cx, result, slotp))
-            return false;
+        slot.setDouble(result);
     }
 
     *dp = result;
     return true;
 }
 
 /*
  * See ECMA 15.9.5.4 thru 15.9.5.23
  */
 static JSBool
-date_getTime(JSContext *cx, uintN argc, jsval *vp)
+date_getTime(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    return GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result) &&
-           js_NewNumberInRootedValue(cx, result, vp);
+    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
+        return JS_FALSE;
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-GetYear(JSContext *cx, JSBool fullyear, jsval *vp)
+GetYear(JSContext *cx, JSBool fullyear, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(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;
     }
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getYear(JSContext *cx, uintN argc, jsval *vp)
+date_getYear(JSContext *cx, uintN argc, Value *vp)
 {
     return GetYear(cx, JS_FALSE, vp);
 }
 
 static JSBool
-date_getFullYear(JSContext *cx, uintN argc, jsval *vp)
+date_getFullYear(JSContext *cx, uintN argc, Value *vp)
 {
     return GetYear(cx, JS_TRUE, vp);
 }
 
 static JSBool
-date_getUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
+date_getUTCFullYear(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = YearFromTime(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getMonth(JSContext *cx, uintN argc, jsval *vp)
+date_getMonth(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = MonthFromTime(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getUTCMonth(JSContext *cx, uintN argc, jsval *vp)
+date_getUTCMonth(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = MonthFromTime(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getDate(JSContext *cx, uintN argc, jsval *vp)
+date_getDate(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = DateFromTime(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getUTCDate(JSContext *cx, uintN argc, jsval *vp)
+date_getUTCDate(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = DateFromTime(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getDay(JSContext *cx, uintN argc, jsval *vp)
+date_getDay(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = WeekDay(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getUTCDay(JSContext *cx, uintN argc, jsval *vp)
+date_getUTCDay(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = WeekDay(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getHours(JSContext *cx, uintN argc, jsval *vp)
+date_getHours(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = HourFromTime(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getUTCHours(JSContext *cx, uintN argc, jsval *vp)
+date_getUTCHours(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = HourFromTime(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getMinutes(JSContext *cx, uintN argc, jsval *vp)
+date_getMinutes(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetAndCacheLocalTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = MinFromTime(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
+date_getUTCMinutes(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = MinFromTime(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 /* Date.getSeconds is mapped to getUTCSeconds */
 
 static JSBool
-date_getUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
+date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = SecFromTime(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
 
 static JSBool
-date_getUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
+date_getUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble result;
 
-    if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
+    if (!GetUTCTime(cx, ComputeThisObjectFromVp(cx, vp), vp, &result))
         return JS_FALSE;
 
     if (JSDOUBLE_IS_FINITE(result))
         result = msFromTime(result);
 
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_getTimezoneOffset(JSContext *cx, uintN argc, jsval *vp)
+date_getTimezoneOffset(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj;
     jsdouble utctime, localtime, result;
 
-    obj = JS_THIS_OBJECT(cx, vp);
+    obj = ComputeThisObjectFromVp(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
      * daylight savings time.
      */
     result = (utctime - localtime) / msPerMinute;
-    return js_NewNumberInRootedValue(cx, result, vp);
+    vp->setDouble(result);
+    return JS_TRUE;
 }
 
 static JSBool
-date_setTime(JSContext *cx, uintN argc, jsval *vp)
+date_setTime(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = JS_THIS_OBJECT(cx, vp);
-    if (!JS_InstanceOf(cx, obj, &js_DateClass, vp + 2))
+    JSObject *obj = ComputeThisObjectFromVp(cx, vp);
+    if (!InstanceOf(cx, obj, &js_DateClass, vp + 2))
         return false;
 
     if (argc == 0) {
         SetDateToNaN(cx, obj, vp);
         return true;
     }
 
     jsdouble result;
-    if (!ValueToNumber(cx, vp[2], &result))
+    if (!ValueToNumber(cx, &vp[2], &result))
         return false;
 
     return SetUTCTime(cx, obj, TIMECLIP(result), vp);
 }
 
 static JSBool
-date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
+date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, Value *vp)
 {
     JSObject *obj;
-    jsval *argv;
+    Value *argv;
     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 = JS_THIS_OBJECT(cx, vp);
+    obj = ComputeThisObjectFromVp(cx, vp);
     if (!GetUTCTime(cx, obj, vp, &result))
         return false;
 
     /* just return NaN if the date is already NaN */
-    if (!JSDOUBLE_IS_FINITE(result))
-        return js_NewNumberInRootedValue(cx, result, vp);
+    if (!JSDOUBLE_IS_FINITE(result)) {
+        vp->setDouble(result);
+        return true;
+    }
 
     /*
      * Satisfy the ECMA rule that if a function is called with
      * fewer arguments than the specified formal arguments, the
      * remaining arguments are set to undefined.  Seems like all
      * the Date.setWhatever functions in ECMA are only varargs
      * beyond the first argument; this should be set to undefined
      * if it's not given.  This means that "d = new Date();
@@ -1572,17 +1597,17 @@ date_makeTime(JSContext *cx, uintN maxar
         return true;
     }
     if (argc > maxargs)
         argc = maxargs;  /* clamp argc */
     JS_ASSERT(argc <= 4);
 
     argv = vp + 2;
     for (i = 0; i < argc; i++) {
-        if (!ValueToNumber(cx, argv[i], &args[i]))
+        if (!ValueToNumber(cx, &argv[i], &args[i]))
             return false;
         if (!JSDOUBLE_IS_FINITE(args[i])) {
             SetDateToNaN(cx, obj, vp);
             return true;
         }
         args[i] = js_DoubleToInteger(args[i]);
     }
 
@@ -1622,103 +1647,105 @@ date_makeTime(JSContext *cx, uintN maxar
         result = UTC(result);
 
 /*     fprintf(stderr, "%f\n", result); */
 
     return SetUTCTime(cx, obj, TIMECLIP(result), vp);
 }
 
 static JSBool
-date_setMilliseconds(JSContext *cx, uintN argc, jsval *vp)
+date_setMilliseconds(JSContext *cx, uintN argc, Value *vp)
 {
     return date_makeTime(cx, 1, JS_TRUE, argc, vp);
 }
 
 static JSBool
-date_setUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
+date_setUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
 {
     return date_makeTime(cx, 1, JS_FALSE, argc, vp);
 }
 
 static JSBool
-date_setSeconds(JSContext *cx, uintN argc, jsval *vp)
+date_s