Bug 463243 - Assert correct type in JSVAL_TO_* (and vice versa) macros to fail fast when type errors happen. r=brendan
☠☠ backed out by 3e3705f10f45 ☠ ☠
authorJeff Walden <jwalden@mit.edu>
Tue, 03 Feb 2009 13:56:25 -0800
changeset 24610 ca733f2cc2377e3ca8f81ba22fe42caf2385c0e7
parent 24609 bab1a858464d75dd2faf8b8fc6231d36e4d2469e
child 24611 3e3705f10f456f1fcb6313ac6fce8567e93f569c
push idunknown
push userunknown
push dateunknown
reviewersbrendan
bugs463243
milestone1.9.2a1pre
Bug 463243 - Assert correct type in JSVAL_TO_* (and vice versa) macros to fail fast when type errors happen. r=brendan
dom/src/base/nsJSEnvironment.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsatom.cpp
js/src/jsbool.h
js/src/jsbuiltins.cpp
js/src/jsbuiltins.h
js/src/jsdbgapi.cpp
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsscope.h
js/src/jstracer.cpp
js/src/jstypes.h
js/src/trace-test.js
js/src/xpconnect/src/xpcquickstubs.cpp
--- a/dom/src/base/nsJSEnvironment.cpp
+++ b/dom/src/base/nsJSEnvironment.cpp
@@ -1986,17 +1986,17 @@ nsJSContext::CallEventHandler(nsISupport
                                  reinterpret_cast<void **>(&argv), &mark);
     if (NS_FAILED(rv)) {
       stack->Pop(nsnull);
       return rv;
     }
 
     AutoFreeJSStack stackGuard(mContext, mark); // ensure always freed.
 
-    jsval funval = OBJECT_TO_JSVAL(aHandler);
+    jsval funval = OBJECT_TO_JSVAL(static_cast<JSObject *>(aHandler));
     JSAutoRequest ar(mContext);
     PRBool ok = ::JS_CallFunctionValue(mContext, target,
                                        funval, argc, argv, &rval);
 
     if (!ok) {
       // Tell XPConnect about any pending exceptions. This is needed
       // to avoid dropping JS exceptions in case we got here through
       // nested calls through XPConnect.
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -738,16 +738,31 @@ 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_PSEUDO_BOOLEAN(JSVAL_VOID) == 2);
+        JS_ASSERT(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_HOLE) == 3);
+        JS_ASSERT(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_ARETURN) == 4);
+
         js_NewRuntimeWasCalled = JS_TRUE;
     }
 #endif /* DEBUG */
 
     rt = (JSRuntime *) malloc(sizeof(JSRuntime));
     if (!rt)
         return NULL;
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -78,23 +78,64 @@ JS_BEGIN_EXTERN_C
                                  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_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)
+
+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);
+}
+
+static JS_ALWAYS_INLINE jsval
+OBJECT_TO_JSVAL(JSObject *obj)
+{
+    JS_ASSERT(((jsval) obj & JSVAL_TAGBITS) == JSVAL_OBJECT);
+    JS_STATIC_ASSERT(JSVAL_OBJECT == 0);
+    return (jsval) obj;
+}
+
+static JS_ALWAYS_INLINE jsval
+DOUBLE_TO_JSVAL(jsdouble *dp)
+{
+    return JSVAL_SETTAG((jsval) dp, JSVAL_DOUBLE);
+}
+
+static JS_ALWAYS_INLINE jsval
+STRING_TO_JSVAL(JSString *str)
+{
+    return JSVAL_SETTAG((jsval) str, JSVAL_STRING);
+}
 
 /* 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)
@@ -104,20 +145,55 @@ JS_BEGIN_EXTERN_C
 #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)
 #define INT_FITS_IN_JSVAL(i)    ((jsuint)(i) - (jsuint)JSVAL_INT_MIN <=      \
                                  (jsuint)(JSVAL_INT_MAX - JSVAL_INT_MIN))
 #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)
+/*
+ * A pseudo-boolean is a 29-bit (for 32-bit jsval) or 61-bit (for 64-bit jsval)
+ * value other than 0 or 1 encoded as a jsval whose tag is JSVAL_BOOLEAN.
+ *
+ * JSVAL_VOID happens to be defined as a jsval encoding a pseudo-boolean, but
+ * embedders MUST NOT rely on this. All other possible pseudo-boolean values
+ * are implementation-reserved and MUST NOT be constructed by any embedding of
+ * SpiderMonkey.
+ */
+#define JSVAL_TO_PSEUDO_BOOLEAN(v) ((JSBool) ((v) >> JSVAL_TAGBITS))
+#define PSEUDO_BOOLEAN_TO_JSVAL(b) \
+  JSVAL_SETTAG((jsval) (b) << JSVAL_TAGBITS, JSVAL_BOOLEAN)
+
+/*
+ * Well-known JS values.  The extern'd variables are initialized when the
+ * first JSContext is created by JS_NewContext (see below).
+ */
+#define JSVAL_NULL              ((jsval) 0)
+#define JSVAL_ZERO              INT_TO_JSVAL(0)
+#define JSVAL_ONE               INT_TO_JSVAL(1)
+#define JSVAL_FALSE             PSEUDO_BOOLEAN_TO_JSVAL(JS_FALSE)
+#define JSVAL_TRUE              PSEUDO_BOOLEAN_TO_JSVAL(JS_TRUE)
+#define JSVAL_VOID              PSEUDO_BOOLEAN_TO_JSVAL(2)
+
+
+/* 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_PSEUDO_BOOLEAN(v);
+}
+
+static JS_ALWAYS_INLINE jsval
+BOOLEAN_TO_JSVAL(JSBool b)
+{
+  JS_ASSERT(b == JS_TRUE || b == JS_FALSE);
+  return PSEUDO_BOOLEAN_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 */
@@ -175,27 +251,16 @@ JS_BEGIN_EXTERN_C
  * If you set this flag in a JSFunctionSpec struct's flags initializer, then
  * that struct must live at least as long as the native static method object
  * created due to this flag by JS_DefineFunctions or JS_InitClass.  Typically
  * 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              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
  * comment in jstypes.h regarding safe int64 usage.
  */
 extern JS_PUBLIC_API(int64)
 JS_Now(void);
 
 /* Don't want to export data, so provide accessors for non-inline jsvals. */
 extern JS_PUBLIC_API(jsval)
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -73,17 +73,16 @@ js_AtomToPrintableString(JSContext *cx, 
  *
  * The elements of the array after the first empty string define strings
  * corresponding to the two boolean literals, false and true, followed by the
  * JSType enumerators from jspubtd.h starting with "undefined" for JSTYPE_VOID
  * (which is pseudo-boolean 2) and continuing as initialized below. The static
  * asserts check these relations.
  */
 JS_STATIC_ASSERT(JSTYPE_LIMIT == 8);
-JS_STATIC_ASSERT(JSVAL_TO_BOOLEAN(JSVAL_VOID) == 2);
 JS_STATIC_ASSERT(JSTYPE_VOID == 0);
 
 const char *const js_common_atom_names[] = {
     "",                         /* emptyAtom                    */
     js_false_str,               /* booleanAtoms[0]              */
     js_true_str,                /* booleanAtoms[1]              */
     js_undefined_str,           /* typeAtoms[JSTYPE_VOID]       */
     js_object_str,              /* typeAtoms[JSTYPE_OBJECT]     */
--- a/js/src/jsbool.h
+++ b/js/src/jsbool.h
@@ -49,20 +49,20 @@ JS_BEGIN_EXTERN_C
  * 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).
+ * NB: PSEUDO_BOOLEAN_TO_JSVAL(2) is JSVAL_VOID (see jsapi.h).
  */
-#define JSVAL_HOLE      BOOLEAN_TO_JSVAL(3)
-#define JSVAL_ARETURN   BOOLEAN_TO_JSVAL(4)
+#define JSVAL_HOLE      PSEUDO_BOOLEAN_TO_JSVAL(3)
+#define JSVAL_ARETURN   PSEUDO_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/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -378,27 +378,29 @@ js_TypeOfObject(JSContext* cx, JSObject*
 {
     JSType type = JS_TypeOfValue(cx, OBJECT_TO_JSVAL(obj));
     return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]);
 }
 
 JSString* FASTCALL
 js_TypeOfBoolean(JSContext* cx, int32 unboxed)
 {
-    jsval boxed = BOOLEAN_TO_JSVAL(unboxed);
+    /* Watch out for pseudo-booleans. */
+    jsval boxed = PSEUDO_BOOLEAN_TO_JSVAL(unboxed);
     JS_ASSERT(JSVAL_IS_VOID(boxed) || JSVAL_IS_BOOLEAN(boxed));
     JSType type = JS_TypeOfValue(cx, boxed);
     return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]);
 }
 
 jsdouble FASTCALL
 js_BooleanOrUndefinedToNumber(JSContext* cx, int32 unboxed)
 {
-    if (unboxed == JSVAL_TO_BOOLEAN(JSVAL_VOID))
+    if (unboxed == JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID))
         return js_NaN;
+    JS_ASSERT(unboxed == JS_TRUE || unboxed == JS_FALSE);
     return unboxed;
 }
 
 JSString* FASTCALL
 js_BooleanOrUndefinedToString(JSContext *cx, int32 unboxed)
 {
     JS_ASSERT(uint32(unboxed) <= 2);
     return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[unboxed]);
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -89,17 +89,17 @@ struct JSTraceableNative {
 };
 
 /*
  * We use a magic boxed pointer value to represent error conditions that
  * trigger a side exit. The address is so low that it should never be actually
  * in use. If it is, a performance regression occurs, not an actual runtime
  * error.
  */
-#define JSVAL_ERROR_COOKIE OBJECT_TO_JSVAL((void*)0x10)
+#define JSVAL_ERROR_COOKIE OBJECT_TO_JSVAL((JSObject*)0x10)
 
 /* Macros used by JS_DEFINE_CALLINFOn. */
 #ifdef DEBUG
 #define _JS_CI_NAME(op) ,#op
 #else
 #define _JS_CI_NAME(op)
 #endif
 
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -646,17 +646,18 @@ js_watch_set(JSContext *cx, JSObject *ob
                     cx->fp = &frame;
                 }
 #ifdef __GNUC__
                 else
                     argv = NULL;    /* suppress bogus gcc warnings */
 #endif
                 ok = !wp->setter ||
                      ((sprop->attrs & JSPROP_SETTER)
-                      ? js_InternalCall(cx, obj, OBJECT_TO_JSVAL(wp->setter),
+                      ? js_InternalCall(cx, obj,
+                                        OBJECT_TO_JSVAL((JSObject *)wp->setter),
                                         1, vp, vp)
                       : wp->setter(cx, OBJ_THIS_OBJECT(cx, obj), userid, vp));
                 if (injectFrame) {
                     /* Evil code can cause us to have an arguments object. */
                     if (frame.callobj)
                         ok &= js_PutCallObject(cx, &frame);
                     if (frame.argsobj)
                         ok &= js_PutArgsObject(cx, &frame);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3584,17 +3584,17 @@ js_Interpret(JSContext *cx)
          (obj2 = JSVAL_TO_OBJECT(lval)) &&                                    \
          OBJECT_IS_XML(cx, obj2)) ||                                          \
         (rtmp == JSVAL_OBJECT &&                                              \
          (obj2 = JSVAL_TO_OBJECT(rval)) &&                                    \
          OBJECT_IS_XML(cx, obj2))) {                                          \
         JSXMLObjectOps *ops;                                                  \
                                                                               \
         ops = (JSXMLObjectOps *) obj2->map->ops;                              \
-        if (obj2 == JSVAL_TO_OBJECT(rval))                                    \
+        if (JSVAL_IS_OBJECT(rval) && obj2 == JSVAL_TO_OBJECT(rval))           \
             rval = lval;                                                      \
         if (!ops->equality(cx, obj2, rval, &cond))                            \
             goto error;                                                       \
         cond = cond OP JS_TRUE;                                               \
     } else
 
 #define EXTENDED_EQUALITY_OP(OP)                                              \
     if (ltmp == JSVAL_OBJECT &&                                               \
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1858,17 +1858,17 @@ obj_lookupGetter(JSContext *cx, uintN ar
     obj = JS_THIS_OBJECT(cx, vp);
     if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
         return JS_FALSE;
     *vp = JSVAL_VOID;
     if (prop) {
         if (OBJ_IS_NATIVE(pobj)) {
             sprop = (JSScopeProperty *) prop;
             if (sprop->attrs & JSPROP_GETTER)
-                *vp = OBJECT_TO_JSVAL(sprop->getter);
+                *vp = OBJECT_TO_JSVAL((JSObject *) sprop->getter);
         }
         OBJ_DROP_PROPERTY(cx, pobj, prop);
     }
     return JS_TRUE;
 }
 
 static JSBool
 obj_lookupSetter(JSContext *cx, uintN argc, jsval *vp)
@@ -1883,17 +1883,17 @@ obj_lookupSetter(JSContext *cx, uintN ar
     obj = JS_THIS_OBJECT(cx, vp);
     if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
         return JS_FALSE;
     *vp = JSVAL_VOID;
     if (prop) {
         if (OBJ_IS_NATIVE(pobj)) {
             sprop = (JSScopeProperty *) prop;
             if (sprop->attrs & JSPROP_SETTER)
-                *vp = OBJECT_TO_JSVAL(sprop->setter);
+                *vp = OBJECT_TO_JSVAL((JSObject *) sprop->setter);
         }
         OBJ_DROP_PROPERTY(cx, pobj, prop);
     }
     return JS_TRUE;
 }
 #endif /* JS_HAS_GETTER_SETTER */
 
 JSBool
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -329,29 +329,29 @@ struct JSScopeProperty {
 #define SPROP_HAS_STUB_SETTER(sprop)    (!(sprop)->setter)
 
 /*
  * NB: SPROP_GET must not be called if SPROP_HAS_STUB_GETTER(sprop).
  */
 #define SPROP_GET(cx,sprop,obj,obj2,vp)                                       \
     (((sprop)->attrs & JSPROP_GETTER)                                         \
      ? js_InternalGetOrSet(cx, obj, (sprop)->id,                              \
-                           OBJECT_TO_JSVAL((sprop)->getter), JSACC_READ,      \
-                           0, 0, vp)                                          \
+                           OBJECT_TO_JSVAL((JSObject *) (sprop)->getter),     \
+                           JSACC_READ, 0, 0, vp)                              \
      : (sprop)->getter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp))
 
 /*
  * NB: SPROP_SET must not be called if (SPROP_HAS_STUB_SETTER(sprop) &&
  * !(sprop->attrs & JSPROP_GETTER)).
  */
 #define SPROP_SET(cx,sprop,obj,obj2,vp)                                       \
     (((sprop)->attrs & JSPROP_SETTER)                                         \
      ? js_InternalGetOrSet(cx, obj, (sprop)->id,                              \
-                           OBJECT_TO_JSVAL((sprop)->setter), JSACC_WRITE,     \
-                           1, vp, vp)                                         \
+                           OBJECT_TO_JSVAL((JSObject *) (sprop)->setter),     \
+                           JSACC_WRITE, 1, vp, vp)                            \
      : ((sprop)->attrs & JSPROP_GETTER)                                       \
      ? (JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,                    \
                              JSMSG_GETTER_ONLY, NULL), JS_FALSE)              \
      : (sprop)->setter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp))
 
 /* Macro for common expression to test for shared permanent attributes. */
 #define SPROP_IS_SHARED_PERMANENT(sprop)                                      \
     ((~(sprop)->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -1451,18 +1451,19 @@ ValueToNative(JSContext* cx, jsval v, ui
             d = JSVAL_TO_INT(v);
         else
             d = *JSVAL_TO_DOUBLE(v);
         JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v));
         *(jsdouble*)slot = d;
         debug_only_v(printf("double<%g> ", d);)
         return;
       case JSVAL_BOOLEAN:
+        /* Watch out for pseudo-booleans. */
         JS_ASSERT(tag == JSVAL_BOOLEAN);
-        *(JSBool*)slot = JSVAL_TO_BOOLEAN(v);
+        *(JSBool*)slot = JSVAL_TO_PSEUDO_BOOLEAN(v);
         debug_only_v(printf("boolean<%d> ", *(JSBool*)slot);)
         return;
       case JSVAL_STRING:
         JS_ASSERT(tag == JSVAL_STRING);
         *(JSString**)slot = JSVAL_TO_STRING(v);
         debug_only_v(printf("string<%p> ", *(JSString**)slot);)
         return;
       case JSVAL_TNULL:
@@ -1535,17 +1536,18 @@ oom:
    heap-allocated doubles. */
 static void
 NativeToValue(JSContext* cx, jsval& v, uint8 type, double* slot)
 {
     jsint i;
     jsdouble d;
     switch (type) {
       case JSVAL_BOOLEAN:
-        v = BOOLEAN_TO_JSVAL(*(JSBool*)slot);
+        /* Watch out for pseudo-booleans. */
+        v = PSEUDO_BOOLEAN_TO_JSVAL(*(JSBool*)slot);
         debug_only_v(printf("boolean<%d> ", *(JSBool*)slot);)
         break;
       case JSVAL_INT:
         i = *(jsint*)slot;
         debug_only_v(printf("int<%d> ", i);)
       store_int:
         if (INT_FITS_IN_JSVAL(i)) {
             v = INT_TO_JSVAL(i);
@@ -4831,17 +4833,17 @@ TraceRecorder::ifop()
         return true;
 
     /* No need to guard if type strictly determines true or false value. */
     if (JSVAL_TAG(v) == JSVAL_OBJECT)
         return true;
 
     if (JSVAL_TAG(v) == JSVAL_BOOLEAN) {
         /* Test for boolean is true, negate later if we are testing for false. */
-        cond = JSVAL_TO_BOOLEAN(v) == 1;
+        cond = JSVAL_TO_PSEUDO_BOOLEAN(v) == JS_TRUE;
         x = lir->ins2i(LIR_eq, v_ins, 1);
     } else if (isNumber(v)) {
         jsdouble d = asNumber(v);
         cond = !JSDOUBLE_IS_NaN(d) && d;
         jsdpun u;
         u.d = 0;
         x = lir->ins2(LIR_and, 
                       lir->ins2(LIR_feq, v_ins, v_ins),
@@ -5100,20 +5102,20 @@ TraceRecorder::equalityHelper(jsval l, j
             r_ins = lir->insImm(1);
             cond = js_EqualStrings(JSVAL_TO_STRING(l), JSVAL_TO_STRING(r));
         } else {
             JS_ASSERT(isNumber(l) && isNumber(r));
             cond = (asNumber(l) == asNumber(r));
             fp = true;
         }
     } else if (JSVAL_IS_NULL(l) && JSVAL_TAG(r) == JSVAL_BOOLEAN) {
-        l_ins = lir->insImm(JSVAL_TO_BOOLEAN(JSVAL_VOID));
+        l_ins = lir->insImm(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
         cond = (r == JSVAL_VOID);
     } else if (JSVAL_TAG(l) == JSVAL_BOOLEAN && JSVAL_IS_NULL(r)) {
-        r_ins = lir->insImm(JSVAL_TO_BOOLEAN(JSVAL_VOID));
+        r_ins = lir->insImm(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
         cond = (l == JSVAL_VOID);
     } else if (isNumber(l) && JSVAL_IS_STRING(r)) {
         args[0] = r_ins, args[1] = cx_ins;
         r_ins = lir->insCall(&js_StringToNumber_ci, args);
         cond = (asNumber(l) == js_StringToNumber(cx, JSVAL_TO_STRING(r)));
         fp = true;
     } else if (JSVAL_IS_STRING(l) && isNumber(r)) {
         args[0] = l_ins, args[1] = cx_ins;
@@ -5354,23 +5356,23 @@ TraceRecorder::binary(LOpcode op)
             b = lir->insCall(&js_StringToNumber_ci, args);
             rnum = js_StringToNumber(cx, JSVAL_TO_STRING(r));
             rightIsNumber = true;
         }
     }
     if (JSVAL_TAG(l) == JSVAL_BOOLEAN) {
         LIns* args[] = { a, cx_ins };
         a = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args);
-        lnum = js_BooleanOrUndefinedToNumber(cx, JSVAL_TO_BOOLEAN(l));
+        lnum = js_BooleanOrUndefinedToNumber(cx, JSVAL_TO_PSEUDO_BOOLEAN(l));
         leftIsNumber = true;
     }
     if (JSVAL_TAG(r) == JSVAL_BOOLEAN) {
         LIns* args[] = { b, cx_ins };
         b = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args);
-        rnum = js_BooleanOrUndefinedToNumber(cx, JSVAL_TO_BOOLEAN(r));
+        rnum = js_BooleanOrUndefinedToNumber(cx, JSVAL_TO_PSEUDO_BOOLEAN(r));
         rightIsNumber = true;
     }
     if (leftIsNumber && rightIsNumber) {
         if (intop) {
             LIns *args[] = { a };
             a = lir->insCall(op == LIR_ush ? &js_DoubleToUint32_ci : &js_DoubleToInt32_ci, args);
             b = f2i(b);
         }
@@ -5657,22 +5659,19 @@ TraceRecorder::native_get(LIns* obj_ins,
         LIns*& dslots_ins, LIns*& v_ins)
 {
     if (!SPROP_HAS_STUB_GETTER(sprop))
         return false;
 
     if (sprop->slot != SPROP_INVALID_SLOT)
         v_ins = stobj_get_slot(pobj_ins, sprop->slot, dslots_ins);
     else
-        v_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
-    return true;
-}
-
-// So box_jsval can emit no LIR_or at all to tag an object jsval.
-JS_STATIC_ASSERT(JSVAL_OBJECT == 0);
+        v_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
+    return true;
+}
 
 JS_REQUIRES_STACK void
 TraceRecorder::box_jsval(jsval v, LIns*& v_ins)
 {
     if (isNumber(v)) {
         LIns* args[] = { v_ins, cx_ins };
         v_ins = lir->insCall(&js_BoxDouble_ci, args);
         guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)),
@@ -5922,17 +5921,17 @@ TraceRecorder::record_EnterFrame()
         ABORT_TRACE("recursive call");
     
     debug_only_v(printf("EnterFrame %s, callDepth=%d\n",
                         js_AtomToPrintableString(cx, cx->fp->fun->atom),
                         callDepth);)
     debug_only_v(
         js_Disassemble(cx, cx->fp->script, JS_TRUE, stdout);
         printf("----\n");)
-    LIns* void_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
+    LIns* void_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
 
     jsval* vp = &fp->argv[fp->argc];
     jsval* vpstop = vp + ptrdiff_t(fp->fun->nargs) - ptrdiff_t(fp->argc);
     while (vp < vpstop) {
         if (vp >= fp->down->regs->sp)
             nativeFrameTracker.set(vp, (LIns*)0);
         set(vp++, void_ins, true);
     }
@@ -5961,17 +5960,17 @@ TraceRecorder::record_LeaveFrame()
     atoms = FrameAtomBase(cx, cx->fp);
     set(&stackval(-1), rval_ins, true);
     return true;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_PUSH()
 {
-    stack(0, INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID)));
+    stack(0, INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID)));
     return true;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_POPV()
 {
     jsval& rval = stackval(-1);
     LIns *rval_ins = get(&rval);
@@ -6610,17 +6609,17 @@ success:
       {
         res_ins = lir->ins1(LIR_i2f, res_ins);
         jsdpun u;
         u.d = 0.0;
         guard(false, lir->ins2(LIR_flt, res_ins, lir->insImmq(u.u64)), OOM_EXIT);
         break;
       }
       case FAIL_VOID:
-        guard(false, lir->ins2i(LIR_eq, res_ins, JSVAL_TO_BOOLEAN(JSVAL_VOID)), OOM_EXIT);
+        guard(false, lir->ins2i(LIR_eq, res_ins, JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID)), OOM_EXIT);
         break;
       default:;
     }
     set(&fval, res_ins);
 
     if (!constructing) {
         /*
          * The return value will be processed by FastNativeCallComplete since
@@ -6666,31 +6665,31 @@ TraceRecorder::record_JSOP_TYPEOF()
         type = INS_CONSTPTR(ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_STRING]));
     } else if (isNumber(r)) {
         type = INS_CONSTPTR(ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]));
     } else {
         LIns* args[] = { get(&r), cx_ins };
         if (JSVAL_TAG(r) == JSVAL_BOOLEAN) {
             // We specialize identically for boolean and undefined. We must not have a hole here.
             // Pass the unboxed type here, since TypeOfBoolean knows how to handle it.
-            JS_ASSERT(JSVAL_TO_BOOLEAN(r) <= 2);
+            JS_ASSERT(r == JSVAL_TRUE || r == JSVAL_FALSE || r == JSVAL_VOID);
             type = lir->insCall(&js_TypeOfBoolean_ci, args);
         } else {
             JS_ASSERT(JSVAL_TAG(r) == JSVAL_OBJECT);
             type = lir->insCall(&js_TypeOfObject_ci, args);
         }
     }
     set(&r, type);
     return true;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_VOID()
 {
-    stack(-1, INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID)));
+    stack(-1, INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID)));
     return true;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_INCNAME()
 {
     return incName(1);
 }
@@ -7451,17 +7450,17 @@ TraceRecorder::prop(JSObject* obj, LIns*
     JSObject* obj2;
     jsuword pcval;
     if (!test_property_cache(obj, obj_ins, obj2, pcval))
         return false;
 
     /* Check for non-existent property reference, which results in undefined. */
     const JSCodeSpec& cs = js_CodeSpec[*cx->fp->regs->pc];
     if (PCVAL_IS_NULL(pcval)) {
-        v_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
+        v_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
         JS_ASSERT(cs.ndefs == 1);
         stack(-cs.nuses, v_ins);
         slot = SPROP_INVALID_SLOT;
         return true;
     }
 
     /* Insist if setting on obj being the directly addressed object. */
     uint32 setflags = (cs.format & (JOF_SET | JOF_INCDEC | JOF_FOR));
@@ -7542,17 +7541,17 @@ TraceRecorder::elem(jsval& oval, jsval& 
     LIns* dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots));
     if (!guardDenseArrayIndex(obj, i, obj_ins, dslots_ins, idx_ins, BRANCH_EXIT)) {
         LIns* rt_ins = lir->insLoad(LIR_ldp, cx_ins, offsetof(JSContext, runtime));
         guard(true, 
               lir->ins_eq0(lir->insLoad(LIR_ldp, rt_ins,
                                         offsetof(JSRuntime, anyArrayProtoHasElement))),
               MISMATCH_EXIT);
         // Return undefined and indicate that we didn't actually read this (addr_ins).
-        v_ins = lir->insImm(JSVAL_TO_BOOLEAN(JSVAL_VOID));
+        v_ins = lir->insImm(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
         addr_ins = NULL; 
         return true;
     }
 
     // We can't "see through" a hole to a possible Array.prototype property, so
     // we abort here and guard below (after unboxing).
     vp = &obj->dslots[i];
     if (*vp == JSVAL_HOLE)
@@ -7563,17 +7562,17 @@ TraceRecorder::elem(jsval& oval, jsval& 
 
     /* Load the value and guard on its type to unbox it. */
     v_ins = lir->insLoad(LIR_ldp, addr_ins, 0);
     unbox_jsval(*vp, v_ins);
 
     if (JSVAL_TAG(*vp) == JSVAL_BOOLEAN) {
         // Optimize to guard for a hole only after untagging, so we know that
         // we have a boolean, to avoid an extra guard for non-boolean values.
-        guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_HOLE))),
+        guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_HOLE))),
               MISMATCH_EXIT);
     }
     return true;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::getProp(JSObject* obj, LIns* obj_ins)
 {
@@ -8041,17 +8040,17 @@ TraceRecorder::record_JSOP_IN()
         if (!js_ValueToStringId(cx, lval, &id))
             ABORT_TRACE("left operand of JSOP_IN didn't convert to a string-id");
         LIns* args[] = { get(&lval), obj_ins, cx_ins };
         x = lir->insCall(&js_HasNamedProperty_ci, args);
     } else {
         ABORT_TRACE("string or integer expected");
     }        
 
-    guard(false, lir->ins2i(LIR_eq, x, JSVAL_TO_BOOLEAN(JSVAL_VOID)), OOM_EXIT);
+    guard(false, lir->ins2i(LIR_eq, x, JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID)), OOM_EXIT);
     x = lir->ins2i(LIR_eq, x, 1);
 
     JSObject* obj2;
     JSProperty* prop;
     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
         ABORT_TRACE("OBJ_LOOKUP_PROPERTY failed in JSOP_IN");
     bool cond = prop != NULL;
     if (prop)
@@ -8651,17 +8650,17 @@ TraceRecorder::record_JSOP_CALLPROP()
             i = JSProto_String;
             debug_only(protoname = "String.prototype";)
         } else if (JSVAL_IS_NUMBER(l)) {
             i = JSProto_Number;
             debug_only(protoname = "Number.prototype";)
         } else if (JSVAL_TAG(l) == JSVAL_BOOLEAN) {
             if (l == JSVAL_VOID)
                 ABORT_TRACE("callprop on void");
-            guard(false, lir->ins2i(LIR_eq, get(&l), JSVAL_TO_BOOLEAN(JSVAL_VOID)), MISMATCH_EXIT);
+            guard(false, lir->ins2i(LIR_eq, get(&l), JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID)), MISMATCH_EXIT);
             i = JSProto_Boolean;
             debug_only(protoname = "Boolean.prototype";)
         } else {
             JS_ASSERT(JSVAL_IS_NULL(l) || JSVAL_IS_VOID(l));
             ABORT_TRACE("callprop on null or void");
         }
 
         if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj))
@@ -8754,17 +8753,17 @@ TraceRecorder::record_JSOP_STOP()
      *
      * NB: we do not support script rval (eval, API users who want the result
      * of the last expression-statement, debugger API calls).
      */
     if (fp->flags & JSFRAME_CONSTRUCTING) {
         JS_ASSERT(OBJECT_TO_JSVAL(fp->thisp) == fp->argv[-1]);
         rval_ins = get(&fp->argv[-1]);
     } else {
-        rval_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
+        rval_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
     }
     clearFrameSlotsFromCache();
     return true;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_GETXPROP()
 {
@@ -8798,17 +8797,17 @@ TraceRecorder::record_JSOP_TYPEOFEXPR()
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_ENTERBLOCK()
 {
     JSScript* script = cx->fp->script;
     JSFrameRegs& regs = *cx->fp->regs;
     JSObject* obj;
     JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(0), obj);
 
-    LIns* void_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
+    LIns* void_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
     for (int i = 0, n = OBJ_BLOCK_COUNT(cx, obj); i < n; i++)
         stack(i, void_ins);
     return true;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_LEAVEBLOCK()
 {
@@ -9143,17 +9142,17 @@ TraceRecorder::record_JSOP_NEWARRAY()
 
     stack(-len, v_ins);
     return true;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_HOLE()
 {
-    stack(0, INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_HOLE)));
+    stack(0, INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_HOLE)));
     return true;
 }
 
 #ifdef JS_JIT_SPEW
 /* Prints information about entry typemaps and unstable exits for all peers at a PC */
 void
 js_DumpPeerStability(JSTraceMonitor* tm, const void* ip, uint32 globalShape)
 {
--- a/js/src/jstypes.h
+++ b/js/src/jstypes.h
@@ -178,17 +178,17 @@
 #endif
 
 #ifndef JS_ALWAYS_INLINE
 # if defined DEBUG
 #  define JS_ALWAYS_INLINE   JS_INLINE
 # elif defined _MSC_VER
 #  define JS_ALWAYS_INLINE   __forceinline
 # elif defined __GNUC__
-#  define JS_ALWAYS_INLINE   __attribute__((always_inline))
+#  define JS_ALWAYS_INLINE   __attribute__((always_inline)) JS_INLINE
 # else
 #  define JS_ALWAYS_INLINE   JS_INLINE
 # endif
 #endif
 
 #ifdef NS_STATIC_CHECKING
 /*
  * Attributes for static analysis. Functions declared with JS_REQUIRES_STACK
--- a/js/src/trace-test.js
+++ b/js/src/trace-test.js
@@ -4143,16 +4143,31 @@ function testInterpreterReentry7() {
     for (var p in arr)
         arr[p] = "bleen";
     return tmp + " " + arr._4;
 }
 testInterpreterReentry7.expected = "grue bleen";
 test(testInterpreterReentry7);
 */
 
+function testHolePushing() {
+    var a = ["foobar", "baz"];
+    for (var i = 0; i < 5; i++)
+        a = [, "overwritten", "new"];
+    var s = "[";
+    for (i = 0; i < a.length; i++) {
+        s += (i in a) ? a[i] : "<hole>";
+        if (i != a.length - 1)
+            s += ",";
+    }
+    return s + "], " + (0 in a);
+}
+testHolePushing.expected = "[<hole>,overwritten,new], false";
+test(testHolePushing);
+
 /*****************************************************************************
  *                                                                           *
  *  _____ _   _  _____ ______ _____ _______                                  *
  * |_   _| \ | |/ ____|  ____|  __ \__   __|                                 *
  *   | | |  \| | (___ | |__  | |__) | | |                                    *
  *   | | | . ` |\___ \|  __| |  _  /  | |                                    *
  *  _| |_| |\  |____) | |____| | \ \  | |                                    *
  * |_____|_| \_|_____/|______|_|  \_\ |_|                                    *
--- a/js/src/xpconnect/src/xpcquickstubs.cpp
+++ b/js/src/xpconnect/src/xpcquickstubs.cpp
@@ -250,25 +250,27 @@ LookupGetterOrSetter(JSContext *cx, JSBo
         return JS_TRUE;
     }
 
     // Inline obj_lookup[GS]etter here.
     if(wantGetter)
     {
         if(attrs & JSPROP_GETTER)
         {
-            JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(getter));
+            JS_SET_RVAL(cx, vp,
+                        OBJECT_TO_JSVAL(reinterpret_cast<JSObject *>(getter)));
             return JS_TRUE;
         }
     }
     else
     {
         if(attrs & JSPROP_SETTER)
         {
-            JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(setter));
+            JS_SET_RVAL(cx, vp,
+                        OBJECT_TO_JSVAL(reinterpret_cast<JSObject *>(setter)));
             return JS_TRUE;
         }
     }
 
     // Since XPConnect doesn't use JSPropertyOps in any other contexts,
     // ensuring that we have an XPConnect prototype object ensures that
     // we are only going to expose quickstubbed properties to script.
     // Also be careful not to overwrite existing properties!