Igor's fix for bug 432881, r=me.
authorBrendan Eich <brendan@mozilla.org>
Fri, 23 May 2008 19:14:05 -0700
changeset 17167 b3d09506eaae1fcb5cbddf76c6e4cd95c2b0ee2a
parent 17166 6e4e82c5932f463b82bf5463b8e34de2a91b7e73
child 17168 ef81950a59e686a777e38f128901b352befcd381
push idunknown
push userunknown
push dateunknown
reviewersme
bugs432881
milestone1.9.1a1pre
Igor's fix for bug 432881, r=me.
js/src/js.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsatom.cpp
js/src/jsbool.cpp
js/src/jsbool.h
js/src/jsfun.cpp
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsobj.cpp
--- a/js/src/js.cpp
+++ b/js/src/js.cpp
@@ -303,17 +303,17 @@ Process(JSContext *cx, JSObject *obj, ch
 
         /* Clear any pending exception from previous failed compiles.  */
         JS_ClearPendingException(cx);
         script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein",
                                   startline);
         if (script) {
             if (!compileOnly) {
                 ok = JS_ExecuteScript(cx, obj, script, &result);
-                if (ok && result != JSVAL_VOID) {
+                if (ok && !JSVAL_IS_VOID(result)) {
                     str = JS_ValueToString(cx, result);
                     if (str)
                         fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
                     else
                         ok = JS_FALSE;
                 }
             }
             JS_DestroyScript(cx, script);
@@ -1073,17 +1073,17 @@ TrapHandler(JSContext *cx, JSScript *scr
     str = (JSString *) closure;
     caller = JS_GetScriptedCaller(cx, NULL);
     if (!JS_EvaluateScript(cx, caller->scopeChain,
                            JS_GetStringBytes(str), JS_GetStringLength(str),
                            caller->script->filename, caller->script->lineno,
                            rval)) {
         return JSTRAP_ERROR;
     }
-    if (*rval != JSVAL_VOID)
+    if (!JSVAL_IS_VOID(*rval))
         return JSTRAP_RETURN;
     return JSTRAP_CONTINUE;
 }
 
 static JSBool
 Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
     JSString *str;
@@ -2204,17 +2204,17 @@ split_enumerate(JSContext *cx, JSObject 
             *idp = JSVAL_ZERO;
         break;
 
       case JSENUMERATE_NEXT:
         iterator = (JSObject*)JSVAL_TO_OBJECT(*statep);
         if (!JS_NextProperty(cx, iterator, idp))
             return JS_FALSE;
 
-        if (*idp != JSVAL_VOID)
+        if (!JSVAL_IS_VOID(*idp))
             break;
         /* Fall through. */
 
       case JSENUMERATE_DESTROY:
         /* Let GC at our iterator object. */
         *statep = JSVAL_NULL;
         break;
     }
@@ -3198,17 +3198,17 @@ its_enumerate(JSContext *cx, JSObject *o
             JS_ReportError(cx, "its enumeration failed");
             return JS_FALSE;
         }
 
         iterator = (JSObject *) JSVAL_TO_OBJECT(*statep);
         if (!JS_NextProperty(cx, iterator, idp))
             return JS_FALSE;
 
-        if (*idp != JSVAL_VOID)
+        if (!JSVAL_IS_VOID(*idp))
             break;
         /* Fall through. */
 
       case JSENUMERATE_DESTROY:
         /* Allow our iterator object to be GC'd. */
         *statep = JSVAL_NULL;
         break;
     }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -65,26 +65,28 @@ JS_BEGIN_EXTERN_C
 #define JSVAL_TAG(v)            ((v) & JSVAL_TAGMASK)
 #define JSVAL_SETTAG(v,t)       ((v) | (t))
 #define JSVAL_CLRTAG(v)         ((v) & ~(jsval)JSVAL_TAGMASK)
 #define JSVAL_ALIGN             JS_BIT(JSVAL_TAGBITS)
 
 /* Predicates for type testing. */
 #define JSVAL_IS_OBJECT(v)      (JSVAL_TAG(v) == JSVAL_OBJECT)
 #define JSVAL_IS_NUMBER(v)      (JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v))
-#define JSVAL_IS_INT(v)         (((v) & JSVAL_INT) && (v) != JSVAL_VOID)
+#define JSVAL_IS_INT(v)         ((v) & JSVAL_INT)
 #define JSVAL_IS_DOUBLE(v)      (JSVAL_TAG(v) == JSVAL_DOUBLE)
 #define JSVAL_IS_STRING(v)      (JSVAL_TAG(v) == JSVAL_STRING)
-#define JSVAL_IS_BOOLEAN(v)     (JSVAL_TAG(v) == JSVAL_BOOLEAN)
+#define JSVAL_IS_BOOLEAN(v)     (((v) & ~((jsval)1 << JSVAL_TAGBITS)) ==      \
+                                 JSVAL_BOOLEAN)
 #define JSVAL_IS_NULL(v)        ((v) == JSVAL_NULL)
 #define JSVAL_IS_VOID(v)        ((v) == JSVAL_VOID)
 #define JSVAL_IS_PRIMITIVE(v)   (!JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v))
 
 /* Objects, strings, and doubles are GC'ed. */
-#define JSVAL_IS_GCTHING(v)     (!((v) & JSVAL_INT) && !JSVAL_IS_BOOLEAN(v))
+#define JSVAL_IS_GCTHING(v)     (!((v) & JSVAL_INT) &&                        \
+                                 JSVAL_TAG(v) != JSVAL_BOOLEAN)
 #define JSVAL_TO_GCTHING(v)     ((void *)JSVAL_CLRTAG(v))
 #define JSVAL_TO_OBJECT(v)      ((JSObject *)JSVAL_TO_GCTHING(v))
 #define JSVAL_TO_DOUBLE(v)      ((jsdouble *)JSVAL_TO_GCTHING(v))
 #define JSVAL_TO_STRING(v)      ((JSString *)JSVAL_TO_GCTHING(v))
 #define OBJECT_TO_JSVAL(obj)    ((jsval)(obj))
 #define DOUBLE_TO_JSVAL(dp)     JSVAL_SETTAG((jsval)(dp), JSVAL_DOUBLE)
 #define STRING_TO_JSVAL(str)    JSVAL_SETTAG((jsval)(str), JSVAL_STRING)
 
@@ -94,19 +96,20 @@ JS_BEGIN_EXTERN_C
                                  : 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)1 - JSVAL_INT_POW2(30))
+#define JSVAL_INT_MIN           (-JSVAL_INT_POW2(30))
 #define JSVAL_INT_MAX           (JSVAL_INT_POW2(30) - 1)
-#define INT_FITS_IN_JSVAL(i)    ((jsuint)((i)+JSVAL_INT_MAX) <= 2*JSVAL_INT_MAX)
+#define INT_FITS_IN_JSVAL(i)    ((jsuint)(i) + JSVAL_INT_MAX + 1 <=           \
+                                 2 * JSVAL_INT_MAX + 1)
 #define JSVAL_TO_INT(v)         ((jsint)(v) >> 1)
 #define INT_TO_JSVAL(i)         (((jsval)(i) << 1) | JSVAL_INT)
 
 /* Convert between boolean and jsval. */
 #define JSVAL_TO_BOOLEAN(v)     ((JSBool)((v) >> JSVAL_TAGBITS))
 #define BOOLEAN_TO_JSVAL(b)     JSVAL_SETTAG((jsval)(b) << JSVAL_TAGBITS,     \
                                              JSVAL_BOOLEAN)
 
@@ -205,17 +208,17 @@ JS_BEGIN_EXTERN_C
  * JSFunctionSpec structs are allocated in static arrays.
  */
 #define JSFUN_GENERIC_NATIVE    JSFUN_LAMBDA
 
 /*
  * Well-known JS values.  The extern'd variables are initialized when the
  * first JSContext is created by JS_NewContext (see below).
  */
-#define JSVAL_VOID              INT_TO_JSVAL(0 - JSVAL_INT_POW2(30))
+#define JSVAL_VOID              BOOLEAN_TO_JSVAL(2)
 #define JSVAL_NULL              OBJECT_TO_JSVAL(0)
 #define JSVAL_ZERO              INT_TO_JSVAL(0)
 #define JSVAL_ONE               INT_TO_JSVAL(1)
 #define JSVAL_FALSE             BOOLEAN_TO_JSVAL(JS_FALSE)
 #define JSVAL_TRUE              BOOLEAN_TO_JSVAL(JS_TRUE)
 
 /*
  * Microseconds since the epoch, midnight, January 1, 1970 UTC.  See the
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -381,17 +381,17 @@ GetArrayElement(JSContext *cx, JSObject 
         return JS_TRUE;
     }
 
     if (index <= JSVAL_INT_MAX) {
         id = INT_TO_JSID(index);
     } else {
         if (!BigIndexToId(cx, obj, index, JS_FALSE, &id))
             return JS_FALSE;
-        if (id == JSVAL_VOID) {
+        if (JSVAL_IS_VOID(id)) {
             *hole = JS_TRUE;
             *vp = JSVAL_VOID;
             return JS_TRUE;
         }
     }
 
     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
         return JS_FALSE;
@@ -432,17 +432,17 @@ SetArrayElement(JSContext *cx, JSObject 
         }
     }
 
     if (index <= JSVAL_INT_MAX) {
         id = INT_TO_JSID(index);
     } else {
         if (!BigIndexToId(cx, obj, index, JS_TRUE, &id))
             return JS_FALSE;
-        JS_ASSERT(id != JSVAL_VOID);
+        JS_ASSERT(!JSVAL_IS_VOID(id));
     }
     return OBJ_SET_PROPERTY(cx, obj, id, &v);
 }
 
 static JSBool
 DeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index)
 {
     jsid id;
@@ -457,32 +457,32 @@ DeleteArrayElement(JSContext *cx, JSObje
         return JS_TRUE;
     }
 
     if (index <= JSVAL_INT_MAX) {
         id = INT_TO_JSID(index);
     } else {
         if (!BigIndexToId(cx, obj, index, JS_FALSE, &id))
             return JS_FALSE;
-        if (id == JSVAL_VOID)
+        if (JSVAL_IS_VOID(id))
             return JS_TRUE;
     }
     return OBJ_DELETE_PROPERTY(cx, obj, 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, jsuint index,
                         JSBool hole, jsval v)
 {
     if (hole) {
-        JS_ASSERT(v == JSVAL_VOID);
+        JS_ASSERT(JSVAL_IS_VOID(v));
         return DeleteArrayElement(cx, obj, index);
     }
     return SetArrayElement(cx, obj, index, v);
 }
 
 JSBool
 js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length)
 {
@@ -611,17 +611,17 @@ array_length_setter(JSContext *cx, JSObj
         /* Protect iter against GC in OBJ_DELETE_PROPERTY. */
         JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr);
         gap = oldlen - newlen;
         for (;;) {
             ok = (JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
                   JS_NextProperty(cx, iter, &id));
             if (!ok)
                 break;
-            if (id == JSVAL_VOID)
+            if (JSVAL_IS_VOID(id))
                 break;
             if (js_IdIsIndex(id, &index) && index - newlen < gap) {
                 ok = OBJ_DELETE_PROPERTY(cx, obj, id, &junk);
                 if (!ok)
                     break;
             }
         }
         JS_POP_TEMP_ROOT(cx, &tvr);
@@ -661,30 +661,30 @@ array_lookupProperty(JSContext *cx, JSOb
         }
 
         return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
     }
 
     /* FIXME 417501: threadsafety: could race with a lookup on another thread.
      * If we can only have a single lookup active per context, we could
      * pigeonhole this on the context instead. */
-    JS_ASSERT(STOBJ_GET_SLOT(obj, JSSLOT_ARRAY_LOOKUP_HOLDER) == JSVAL_VOID);
-    STOBJ_SET_SLOT(obj, JSSLOT_ARRAY_LOOKUP_HOLDER, (jsval)id);
+    JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]));
+    obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER] = (jsval) id;
     *propp = (JSProperty *)&(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]);
     *objp = obj;
     return JS_TRUE;
 }
 
 static void
 array_dropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
 {
-    JS_ASSERT(!OBJ_IS_DENSE_ARRAY(cx, obj) ||
-              STOBJ_GET_SLOT(obj, JSSLOT_ARRAY_LOOKUP_HOLDER) != JSVAL_VOID);
+    JS_ASSERT_IF(OBJ_IS_DENSE_ARRAY(cx, obj),
+                 !JSVAL_IS_VOID(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]));
 #ifdef DEBUG
-    STOBJ_SET_SLOT(obj, JSSLOT_ARRAY_LOOKUP_HOLDER, JSVAL_VOID);
+    obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER] = JSVAL_VOID;
 #endif
 }
 
 static JSBool
 array_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     uint32 i;
 
@@ -1692,18 +1692,18 @@ sort_compare(void *arg, const void *a, c
     JSContext *cx = ca->context;
     jsval *invokevp, *sp;
     jsdouble cmp;
 
     /**
      * array_sort deals with holes and undefs on its own and they should not
      * come here.
      */
-    JS_ASSERT(av != JSVAL_VOID);
-    JS_ASSERT(bv != JSVAL_VOID);
+    JS_ASSERT(!JSVAL_IS_VOID(av));
+    JS_ASSERT(!JSVAL_IS_VOID(bv));
 
     if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP))
         return JS_FALSE;
 
     invokevp = ca->elemroot;
     sp = invokevp;
     *sp++ = ca->fval;
     *sp++ = JSVAL_NULL;
@@ -1840,17 +1840,17 @@ array_sort(JSContext *cx, uintN argc, js
         tvr.count = newlen + 1;
         ok = GetArrayElement(cx, obj, i, &hole, &vec[newlen]);
         if (!ok)
             goto out;
 
         if (hole)
             continue;
 
-        if (vec[newlen] == JSVAL_VOID) {
+        if (JSVAL_IS_VOID(vec[newlen])) {
             ++undefs;
             continue;
         }
 
         /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */
         all_strings &= JSVAL_IS_STRING(vec[newlen]);
 
         ++newlen;
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -785,18 +785,18 @@ js_AtomizePrimitiveValue(JSContext *cx, 
         atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0);
         if (!atom)
             return JS_FALSE;
     } else if (JSVAL_IS_DOUBLE(v)) {
         atom = js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(v));
         if (!atom)
             return JS_FALSE;
     } else {
-        JS_ASSERT(JSVAL_IS_INT(v) || v == JSVAL_TRUE || v == JSVAL_FALSE ||
-                  v == JSVAL_NULL || v == JSVAL_VOID);
+        JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_BOOLEAN(v) ||
+                  JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v));
         atom = (JSAtom *)v;
     }
     *atomp = atom;
     return JS_TRUE;
 }
 
 JSBool
 js_ValueToStringId(JSContext *cx, jsval v, jsid *idp)
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -49,16 +49,21 @@
 #include "jscntxt.h"
 #include "jsconfig.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsstr.h"
 
+/* Check pseudo-booleans values. */
+JS_STATIC_ASSERT(JSVAL_VOID == JSVAL_TRUE + JSVAL_ALIGN);
+JS_STATIC_ASSERT(JSVAL_HOLE == JSVAL_VOID + JSVAL_ALIGN);
+JS_STATIC_ASSERT(JSVAL_ARETURN == JSVAL_HOLE + JSVAL_ALIGN);
+
 JSClass js_BooleanClass = {
     "Boolean",
     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean),
     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
--- a/js/src/jsbool.h
+++ b/js/src/jsbool.h
@@ -41,26 +41,28 @@
 #define jsbool_h___
 /*
  * JS boolean interface.
  */
 
 JS_BEGIN_EXTERN_C
 
 /*
- * Crypto-booleans, not visible to script but used internally by the engine.
+ * Pseudo-booleans, 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".
  *
  * JSVAL_ARETURN is used to throw asynchronous return for generator.close().
+ *
+ * NB: BOOLEAN_TO_JSVAL(2) is JSVAL_VOID (see jsapi.h).
  */
-#define JSVAL_HOLE      BOOLEAN_TO_JSVAL(2)
-#define JSVAL_ARETURN   BOOLEAN_TO_JSVAL(3)
+#define JSVAL_HOLE      BOOLEAN_TO_JSVAL(3)
+#define JSVAL_ARETURN   BOOLEAN_TO_JSVAL(4)
 
 extern JSClass js_BooleanClass;
 
 extern JSObject *
 js_InitBooleanClass(JSContext *cx, JSObject *obj);
 
 extern JSString *
 js_BooleanToString(JSContext *cx, JSBool b);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2026,17 +2026,17 @@ js_NewFunction(JSContext *cx, JSObject *
     if (funobj) {
         JS_ASSERT(HAS_FUNCTION_CLASS(funobj));
         OBJ_SET_PARENT(cx, funobj, parent);
     } else {
         funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent, 0);
         if (!funobj)
             return NULL;
     }
-    JS_ASSERT(funobj->fslots[JSSLOT_PRIVATE] == JSVAL_VOID);
+    JS_ASSERT(JSVAL_IS_VOID(funobj->fslots[JSSLOT_PRIVATE]));
     fun = (JSFunction *) funobj;
 
     /* Initialize all function members. */
     fun->nargs = nargs;
     fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_INTERPRETED);
     if (flags & JSFUN_INTERPRETED) {
         JS_ASSERT(!native);
         JS_ASSERT(nargs == 0);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2353,18 +2353,16 @@ js_DumpOpMeters()
  * Quickly test if v is an int from the [-2**29, 2**29) range, that is, when
  * the lowest bit of v is 1 and the bits 30 and 31 are both either 0 or 1. For
  * such v we can do increment or decrement via adding or subtracting two
  * without checking that the result overflows JSVAL_INT_MIN or JSVAL_INT_MAX.
  */
 #define CAN_DO_FAST_INC_DEC(v)     (((((v) << 1) ^ v) & 0x80000001) == 1)
 
 JS_STATIC_ASSERT(JSVAL_INT == 1);
-JS_STATIC_ASSERT(JSVAL_INT & JSVAL_VOID);
-JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(JSVAL_VOID));
 JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MIN)));
 JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MAX)));
 
 /*
  * Conditional assert to detect failure to clear a pending exception that is
  * suppressed (or unintentional suppression of a wanted exception).
  */
 #if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_shaver
@@ -2439,20 +2437,16 @@ JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == 
  * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
  * remain distinct for the decompiler.
  */
 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
 
 /* Ensure we can share deffun and closure code. */
 JS_STATIC_ASSERT(JSOP_DEFFUN_LENGTH == JSOP_CLOSURE_LENGTH);
 
-
-/* See comments in FETCH_SHIFT macro. */
-JS_STATIC_ASSERT((JSVAL_TO_INT(JSVAL_VOID) & 31) == 0);
-
 JSBool
 js_Interpret(JSContext *cx)
 {
     JSRuntime *rt;
     JSStackFrame *fp;
     JSScript *script;
     uintN inlineCallCount;
     JSAtom **atoms;
@@ -3403,25 +3397,17 @@ interrupt:
           END_CASE(JSOP_BITAND)
 
 #define RELATIONAL_OP(OP)                                                     \
     JS_BEGIN_MACRO                                                            \
         rval = FETCH_OPND(-1);                                                \
         lval = FETCH_OPND(-2);                                                \
         /* Optimize for two int-tagged operands (typical loop control). */    \
         if ((lval & rval) & JSVAL_INT) {                                      \
-            ltmp = lval ^ JSVAL_VOID;                                         \
-            rtmp = rval ^ JSVAL_VOID;                                         \
-            if (ltmp && rtmp) {                                               \
-                cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval);              \
-            } else {                                                          \
-                d  = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN;                  \
-                d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN;                  \
-                cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE);                 \
-            }                                                                 \
+            cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval);                  \
         } else {                                                              \
             if (!JSVAL_IS_PRIMITIVE(lval))                                    \
                 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval);                   \
             if (!JSVAL_IS_PRIMITIVE(rval))                                    \
                 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval);                   \
             if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) {             \
                 str  = JSVAL_TO_STRING(lval);                                 \
                 str2 = JSVAL_TO_STRING(rval);                                 \
@@ -3585,41 +3571,21 @@ interrupt:
 
           BEGIN_CASE(JSOP_GE)
             RELATIONAL_OP(>=);
           END_CASE(JSOP_GE)
 
 #undef EQUALITY_OP
 #undef RELATIONAL_OP
 
-/*
- * We do not check for JSVAL_VOID here since ToInt32(undefined) == 0
- * and (JSVAL_TO_INT(JSVAL_VOID) & 31) == 0. The static assert before
- * js_Interpret ensures this.
- */
-#define FETCH_SHIFT(shift)                                                    \
-    JS_BEGIN_MACRO                                                            \
-        jsval v_;                                                             \
-                                                                              \
-        v_ = FETCH_OPND(-1);                                                  \
-        if (v_ & JSVAL_INT) {                                                 \
-            shift = JSVAL_TO_INT(v_);                                         \
-        } else {                                                              \
-            shift = js_ValueToECMAInt32(cx, &regs.sp[-1]);                    \
-            if (JSVAL_IS_NULL(regs.sp[-1]))                                   \
-                goto error;                                                   \
-        }                                                                     \
-        shift &= 31;                                                          \
-    JS_END_MACRO
-
 #define SIGNED_SHIFT_OP(OP)                                                   \
     JS_BEGIN_MACRO                                                            \
         FETCH_INT(cx, -2, i);                                                 \
-        FETCH_SHIFT(j);                                                       \
-        i = i OP j;                                                           \
+        FETCH_INT(cx, -1, j);                                                 \
+        i = i OP (j & 31);                                                    \
         regs.sp--;                                                            \
         STORE_INT(cx, -1, i);                                                 \
     JS_END_MACRO
 
           BEGIN_CASE(JSOP_LSH)
             SIGNED_SHIFT_OP(<<);
           END_CASE(JSOP_LSH)
 
@@ -3627,18 +3593,18 @@ interrupt:
             SIGNED_SHIFT_OP(>>);
           END_CASE(JSOP_RSH)
 
           BEGIN_CASE(JSOP_URSH)
           {
             uint32 u;
 
             FETCH_UINT(cx, -2, u);
-            FETCH_SHIFT(j);
-            u >>= j;
+            FETCH_INT(cx, -1, j);
+            u >>= (j & 31);
             regs.sp--;
             STORE_UINT(cx, -1, u);
           }
           END_CASE(JSOP_URSH)
 
 #undef BITWISE_OP
 #undef SIGNED_SHIFT_OP
 
@@ -3757,22 +3723,25 @@ interrupt:
           BEGIN_CASE(JSOP_BITNOT)
             FETCH_INT(cx, -1, i);
             i = ~i;
             STORE_INT(cx, -1, i);
           END_CASE(JSOP_BITNOT)
 
           BEGIN_CASE(JSOP_NEG)
             /*
-             * Optimize the case of an int-tagged operand by noting that
-             * INT_FITS_IN_JSVAL(i) => INT_FITS_IN_JSVAL(-i) unless i is 0
-             * when -i is the negative zero which is jsdouble.
+             * When the operand is int jsval, INT_FITS_IN_JSVAL(i) implies
+             * INT_FITS_IN_JSVAL(-i) unless i is 0 or JSVAL_INT_MIN when the
+             * results, -0.0 or JSVAL_INT_MAX + 1, are jsdouble values.
              */
             rval = FETCH_OPND(-1);
-            if (JSVAL_IS_INT(rval) && (i = JSVAL_TO_INT(rval)) != 0) {
+            if (JSVAL_IS_INT(rval) &&
+                rval != INT_TO_JSVAL(JSVAL_INT_MIN) &&
+                (i = JSVAL_TO_INT(rval)) != 0) {
+                JS_STATIC_ASSERT(!INT_FITS_IN_JSVAL(-JSVAL_INT_MIN));
                 i = -i;
                 JS_ASSERT(INT_FITS_IN_JSVAL(i));
                 regs.sp[-1] = INT_TO_JSVAL(i);
             } else {
                 if (JSVAL_IS_DOUBLE(rval)) {
                     d = *JSVAL_TO_DOUBLE(rval);
                 } else {
                     d = js_ValueToNumber(cx, &regs.sp[-1]);
@@ -4307,17 +4276,17 @@ interrupt:
                     (obj = JSVAL_TO_OBJECT(rval),
                      fun = GET_FUNCTION_PRIVATE(cx, obj),
                      !PRIMITIVE_THIS_TEST(fun, lval))) {
                     if (!js_PrimitiveToObject(cx, &regs.sp[-1]))
                         goto error;
                 }
             }
 #if JS_HAS_NO_SUCH_METHOD
-            if (JS_UNLIKELY(rval == JSVAL_VOID)) {
+            if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
                 LOAD_ATOM(0);
                 regs.sp[-2] = ATOM_KEY(atom);
                 if (!js_OnUnknownMethod(cx, regs.sp - 2))
                     goto error;
             }
 #endif
           }
           END_CASE(JSOP_CALLPROP)
@@ -4573,17 +4542,17 @@ interrupt:
 
           BEGIN_CASE(JSOP_CALLELEM)
             /*
              * FIXME: JSOP_CALLELEM should call getMethod on XML objects as
              * CALLPROP does. See bug 362910.
              */
             ELEMENT_OP(-1, OBJ_GET_PROPERTY(cx, obj, id, &rval));
 #if JS_HAS_NO_SUCH_METHOD
-            if (JS_UNLIKELY(rval == JSVAL_VOID)) {
+            if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
                 regs.sp[-2] = regs.sp[-1];
                 regs.sp[-1] = OBJECT_TO_JSVAL(obj);
                 if (!js_OnUnknownMethod(cx, regs.sp - 2))
                     goto error;
             } else
 #endif
             {
                 STORE_OPND(-2, rval);
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -390,17 +390,17 @@ js_ComputeThis(JSContext *cx, JSBool laz
  * The alert should display "true".
  */
 JSObject *
 js_ComputeGlobalThis(JSContext *cx, JSBool lazy, jsval *argv);
 
 extern const uint16 js_PrimitiveTestFlags[];
 
 #define PRIMITIVE_THIS_TEST(fun,thisv)                                        \
-    (JS_ASSERT(thisv != JSVAL_VOID),                                          \
+    (JS_ASSERT(!JSVAL_IS_VOID(thisv)),                                        \
      JSFUN_THISP_TEST(JSFUN_THISP_FLAGS((fun)->flags),                        \
                       js_PrimitiveTestFlags[JSVAL_TAG(thisv) - 1]))
 
 /*
  * NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp
  * is non-null), and that vp points to the callee, |this| parameter, and
  * actual arguments of the call. [vp .. vp + 2 + argc) must belong to the last
  * JS stack segment that js_AllocStack allocated. The function may use the
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2887,17 +2887,17 @@ js_AllocSlot(JSContext *cx, JSObject *ob
     }
 
     if (map->freeslot >= STOBJ_NSLOTS(obj) &&
         !js_ReallocSlots(cx, obj, map->freeslot + 1, JS_FALSE)) {
         return JS_FALSE;
     }
 
     /* js_ReallocSlots or js_FreeSlot should set the free slots to void. */
-    JS_ASSERT(STOBJ_GET_SLOT(obj, map->freeslot) == JSVAL_VOID);
+    JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, map->freeslot)));
     *slotp = map->freeslot++;
     return JS_TRUE;
 }
 
 void
 js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
 {
     JSObjectMap *map;
@@ -4684,17 +4684,17 @@ js_PrimitiveToObject(JSContext *cx, jsva
         &js_NumberClass,    /* INT     */
         &js_StringClass,    /* STRING  */
         &js_NumberClass,    /* INT     */
         &js_BooleanClass,   /* BOOLEAN */
         &js_NumberClass     /* INT     */
     };
 
     JS_ASSERT(!JSVAL_IS_OBJECT(*vp));
-    JS_ASSERT(*vp != JSVAL_VOID);
+    JS_ASSERT(!JSVAL_IS_VOID(*vp));
     clasp = PrimitiveClasses[JSVAL_TAG(*vp) - 1];
     obj = js_NewObject(cx, clasp, NULL, NULL, 0);
     if (!obj)
         return JS_FALSE;
     STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, *vp);
     *vp = OBJECT_TO_JSVAL(obj);
     return JS_TRUE;
 }