diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 201c9c93897..d268c6bd637 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -52,12 +52,12 @@ JS_BEGIN_EXTERN_C /* Well-known JS values, initialized on startup. */ -#define JSVAL_NULL JSVAL_CONSTANT(JSVAL_MASK32_NULL, 0) -#define JSVAL_ZERO JSVAL_CONSTANT(JSVAL_MASK32_INT32, 0) -#define JSVAL_ONE JSVAL_CONSTANT(JSVAL_MASK32_INT32, 1) -#define JSVAL_FALSE JSVAL_CONSTANT(JSVAL_MASK32_BOOLEAN, JS_FALSE) -#define JSVAL_TRUE JSVAL_CONSTANT(JSVAL_MASK32_BOOLEAN, JS_TRUE) -#define JSVAL_VOID JSVAL_CONSTANT(JSVAL_MASK32_UNDEFINED, 0) +#define JSVAL_NULL BUILD_JSVAL(JSVAL_MASK32_NULL, 0) +#define JSVAL_ZERO BUILD_JSVAL(JSVAL_MASK32_INT32, 0) +#define JSVAL_ONE BUILD_JSVAL(JSVAL_MASK32_INT32, 1) +#define JSVAL_FALSE BUILD_JSVAL(JSVAL_MASK32_BOOLEAN, JS_FALSE) +#define JSVAL_TRUE BUILD_JSVAL(JSVAL_MASK32_BOOLEAN, JS_TRUE) +#define JSVAL_VOID BUILD_JSVAL(JSVAL_MASK32_UNDEFINED, 0) /* Predicates for type testing. */ @@ -65,14 +65,14 @@ static JS_ALWAYS_INLINE JSBool JSVAL_IS_NULL(jsval v) { jsval_layout l = { v }; - return l.s.mask32 == JSVAL_MASK32_NULL; + return JSVAL_IS_NULL_IMPL(l); } static JS_ALWAYS_INLINE JSBool JSVAL_IS_VOID(jsval v) { jsval_layout l = { v }; - return l.s.mask32 == JSVAL_MASK32_UNDEFINED; + return JSVAL_IS_UNDEFINED_IMPL(l); } static JS_ALWAYS_INLINE JSBool @@ -103,17 +103,14 @@ INT_FITS_IN_JSVAL(jsint i) static JS_ALWAYS_INLINE jsval INT_TO_JSVAL(int32 i) { - jsval_layout l; - l.s.mask32 = JSVAL_MASK32_INT32; - l.s.payload.i32 = i; - return l.asBits; + return INT32_TO_JSVAL_IMPL(i).asBits; } static JS_ALWAYS_INLINE JSBool JSVAL_IS_DOUBLE(jsval v) { jsval_layout l = { v }; - return l.s.mask32 < JSVAL_MASK32_CLEAR; + return JSVAL_IS_DOUBLE_IMPL(l); } static JS_ALWAYS_INLINE jsdouble @@ -127,16 +124,14 @@ JSVAL_TO_DOUBLE(jsval v) static JS_ALWAYS_INLINE jsval DOUBLE_TO_JSVAL(jsdouble d) { - jsval_layout l; - l.asDouble = d; - JS_ASSERT(l.s.tag.nanBits != JSVAL_NANBOX_PATTERN); - return l.asBits; + return DOUBLE_TO_JSVAL_IMPL(d).asBits; } static JS_ALWAYS_INLINE JSBool JSVAL_IS_NUMBER(jsval v) { - return JSVAL_IS_INT(v) | JSVAL_IS_DOUBLE(v); + jsval_layout l = { v }; + return JSVAL_IS_NUMBER_IMPL(l); } static JS_ALWAYS_INLINE JSBool @@ -151,30 +146,20 @@ JSVAL_TO_STRING(jsval v) { JS_ASSERT(JSVAL_IS_STRING(v)); jsval_layout l = { v }; - return l.s.payload.str; + return JSVAL_TO_STRING_IMPL(l); } static JS_ALWAYS_INLINE jsval STRING_TO_JSVAL(JSString *str) { - jsval_layout l; - l.s.mask32 = JSVAL_MASK32_STRING; - l.s.payload.str = str; - return l.asBits; + return STRING_TO_JSVAL_IMPL(str).asBits; } -/* - * 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_OBJECT(jsval v) { jsval_layout l = { v }; - return (l.s.mask32 & JSVAL_MASK32_OBJORNULL) > JSVAL_MASK32_CLEAR; + return JSVAL_IS_OBJECT_OR_NULL_IMPL(l); } static JS_ALWAYS_INLINE JSObject * @@ -182,9 +167,10 @@ JSVAL_TO_OBJECT(jsval v) { JS_ASSERT(JSVAL_IS_OBJECT(v)); jsval_layout l = { v }; - return l.s.payload.obj; + return JSVAL_TO_OBJECT_IMPL(l); } +/* N.B. Not cheap; uses public API instead of obj->isFunction()! */ static JS_ALWAYS_INLINE jsval OBJECT_TO_JSVAL(JSObject *obj) { @@ -193,12 +179,10 @@ OBJECT_TO_JSVAL(JSObject *obj) if (!obj) return JSVAL_NULL; - uint32 mask = JS_ObjectIsFunction(NULL, obj) ? JSVAL_MASK32_FUNOBJ - : JSVAL_MASK32_NONFUNOBJ; - jsval_layout l; - l.s.mask32 = mask; - l.s.payload.obj = obj; - return l.asBits; + + JSValueMask32 mask = JS_ObjectIsFunction(NULL, obj) ? JSVAL_MASK32_FUNOBJ + : JSVAL_MASK32_NONFUNOBJ; + return OBJECT_TO_JSVAL_IMPL(mask, obj).asBits; } static JS_ALWAYS_INLINE JSBool @@ -219,24 +203,21 @@ JSVAL_TO_BOOLEAN(jsval v) static JS_ALWAYS_INLINE jsval BOOLEAN_TO_JSVAL(JSBool b) { - jsval_layout l; - l.s.mask32 = JSVAL_MASK32_BOOLEAN; - l.s.payload.boo = b; - return l.asBits; + return BOOLEAN_TO_JSVAL_IMPL(b).asBits; } static JS_ALWAYS_INLINE JSBool JSVAL_IS_PRIMITIVE(jsval v) { jsval_layout l = { v }; - return (l.s.mask32 & JSVAL_MASK32_OBJECT) <= JSVAL_MASK32_CLEAR; + return JSVAL_IS_PRIMITIVE_IMPL(l); } static JS_ALWAYS_INLINE JSBool JSVAL_IS_GCTHING(jsval v) { jsval_layout l = { v }; - return (l.s.mask32 & JSVAL_MASK32_GCTHING) > JSVAL_MASK32_CLEAR; + return JSVAL_IS_GCTHING_IMPL(l); } static JS_ALWAYS_INLINE void * @@ -244,24 +225,23 @@ JSVAL_TO_GCTHING(jsval v) { JS_ASSERT(JSVAL_IS_GCTHING(v)); jsval_layout l = { v }; - return l.s.payload.ptr; + return JSVAL_TO_GCTHING_IMPL(l); } +/* To be GC-safe, privates are tagged as doubles. */ + static JS_ALWAYS_INLINE jsval PRIVATE_TO_JSVAL(void *ptr) { - jsval_layout l; - l.s.mask32 = JSVAL_MASK32_INT32; - l.s.payload.ptr = ptr; - return l.asBits; + return PRIVATE_TO_JSVAL_IMPL(ptr).asBits; } static JS_ALWAYS_INLINE void * JSVAL_TO_PRIVATE(jsval v) { - JS_ASSERT(JSVAL_IS_INT(v)); + JS_ASSERT(JSVAL_IS_DOUBLE(v)); jsval_layout l = { v }; - return l.s.payload.ptr; + return JSVAL_TO_PRIVATE_IMPL(l); } /* Lock and unlock the GC thing held by a jsval. */ @@ -1187,7 +1167,7 @@ JSVAL_TRACE_KIND(jsval v) { JS_ASSERT(JSVAL_IS_GCTHING(v)); jsval_layout l = { v }; - return (uint32)(l.s.mask32 == JSVAL_MASK32_STRING); + return JSVAL_TRACE_KIND_IMPL(l); } struct JSTracer { @@ -3007,44 +2987,26 @@ class Value * 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 class PrimitiveValue; protected: /* Type masks */ + template class T {}; + void staticAssertions() { - JS_STATIC_ASSERT(sizeof(void *) == 4); - JS_STATIC_ASSERT(sizeof(jsval) == 8); - JS_STATIC_ASSERT(sizeof(JSBool) == 4); JS_STATIC_ASSERT(sizeof(JSValueMask16) == 2); - JS_STATIC_ASSERT(sizeof(jsval_payload) == 4); + JS_STATIC_ASSERT(JSVAL_NANBOX_PATTERN == 0xFFFF); + JS_STATIC_ASSERT(sizeof(JSValueMask32) == 4); + JS_STATIC_ASSERT(JSVAL_MASK32_CLEAR == 0xFFFF0000); + JS_STATIC_ASSERT(sizeof(JSBool) == 4); JS_STATIC_ASSERT(sizeof(JSWhyMagic) <= 4); + JS_STATIC_ASSERT(sizeof(((jsval_layout *)0)->s.payload) == 4); + JS_STATIC_ASSERT(sizeof(jsval) == 8); } jsval_layout data; - static bool isNullOrUndefinedMask(uint32 mask) { - return (mask & JSVAL_MASK32_SINGLETON) > JSVAL_MASK32_CLEAR; - } - - static bool isDoubleMask(uint32 mask) { - return mask < JSVAL_MASK32_CLEAR; - } - - static bool isNumberMask(uint32 mask) { - return (mask < JSVAL_MASK32_CLEAR) | (mask == JSVAL_MASK32_INT32); - } - - static bool isObjectMask(uint32 mask) { - return (mask & JSVAL_MASK32_OBJECT) > JSVAL_MASK32_CLEAR; - } - - static bool isObjectOrNullMask(uint32 mask) { - return (mask & JSVAL_MASK32_OBJORNULL) > JSVAL_MASK32_CLEAR; - } - public: /* Constructors */ @@ -3075,18 +3037,15 @@ class Value /* Change to a Value of a single type */ void setNull() { - data.s.mask32 = JSVAL_MASK32_NULL; - data.s.payload.obj = NULL; + data.asBits = JSVAL_NULL; } void setUndefined() { - data.s.mask32 = JSVAL_MASK32_UNDEFINED; - data.s.payload.obj = NULL; + data.asBits = JSVAL_VOID; } void setInt32(int32 i) { - data.s.mask32 = JSVAL_MASK32_INT32; - data.s.payload.i32 = i; + data = INT32_TO_JSVAL_IMPL(i); } int32 &asInt32Ref() { @@ -3096,8 +3055,7 @@ class Value void setDouble(double d) { ASSERT_DOUBLE_ALIGN(); - data.asDouble = d; - JS_ASSERT(data.s.tag.nanBits != JSVAL_NANBOX_PATTERN); + data = DOUBLE_TO_JSVAL_IMPL(d); } double &asDoubleRef() { @@ -3107,62 +3065,54 @@ class Value } void setString(JSString *str) { - data.s.mask32 = JSVAL_MASK32_STRING; - data.s.payload.str = str; + data = STRING_TO_JSVAL_IMPL(str); } void setFunObj(JSObject &arg) { JS_ASSERT(JS_ObjectIsFunction(NULL, &arg)); - data.s.mask32 = JSVAL_MASK32_FUNOBJ; - data.s.payload.obj = &arg; + data = OBJECT_TO_JSVAL_IMPL(JSVAL_MASK32_FUNOBJ, &arg); } void setNonFunObj(JSObject &arg) { JS_ASSERT(!JS_ObjectIsFunction(NULL, &arg)); - data.s.mask32 = JSVAL_MASK32_NONFUNOBJ; - data.s.payload.obj = &arg; + data = OBJECT_TO_JSVAL_IMPL(JSVAL_MASK32_NONFUNOBJ, &arg); } void setBoolean(bool b) { - data.s.mask32 = JSVAL_MASK32_BOOLEAN; - data.s.payload.boo = b; + data = BOOLEAN_TO_JSVAL_IMPL(b); } void setMagic(JSWhyMagic why) { - data.s.mask32 = JSVAL_MASK32_MAGIC; - data.s.payload.why = why; + data = MAGIC_TO_JSVAL_IMPL(why); } /* Change to a Value of a type dynamically chosen from a set of types */ void setNumber(uint32 ui) { - if (ui > JSVAL_INT_MAX) { - data.asDouble = ui; - JS_ASSERT(data.s.tag.nanBits != JSVAL_NANBOX_PATTERN); - } else { - data.s.mask32 = JSVAL_MASK32_INT32; - data.s.payload.i32 = (int32)ui; - } + if (ui > JSVAL_INT_MAX) + data = DOUBLE_TO_JSVAL_IMPL(ui); + else + data = INT32_TO_JSVAL_IMPL((int32)ui); } inline void setNumber(double d); void setFunObjOrNull(JSObject *arg) { JS_ASSERT_IF(arg, JS_ObjectIsFunction(NULL, arg)); - data.s.mask32 = arg ? JSVAL_MASK32_FUNOBJ : JSVAL_MASK32_NULL; - data.s.payload.obj = arg; + JSValueMask32 mask = arg ? JSVAL_MASK32_FUNOBJ : JSVAL_MASK32_NULL; + data = OBJECT_TO_JSVAL_IMPL(mask, arg); } void setFunObjOrUndefined(JSObject *arg) { JS_ASSERT_IF(arg, JS_ObjectIsFunction(NULL, arg)); - data.s.mask32 = arg ? JSVAL_MASK32_FUNOBJ : JSVAL_MASK32_UNDEFINED; - data.s.payload.obj = arg; + JSValueMask32 mask = arg ? JSVAL_MASK32_FUNOBJ : JSVAL_MASK32_UNDEFINED; + data = OBJECT_TO_JSVAL_IMPL(mask, arg); } void setNonFunObjOrNull(JSObject *arg) { JS_ASSERT_IF(arg, !JS_ObjectIsFunction(NULL, arg)); - data.s.mask32 = arg ? JSVAL_MASK32_NONFUNOBJ : JSVAL_MASK32_NULL; - data.s.payload.obj = arg; + JSValueMask32 mask = arg ? JSVAL_MASK32_NONFUNOBJ : JSVAL_MASK32_NULL; + data = OBJECT_TO_JSVAL_IMPL(mask, arg); } inline void setObject(JSObject &arg); @@ -3171,15 +3121,15 @@ class Value /* Query a Value's type */ bool isUndefined() const { - return data.s.mask32 == JSVAL_MASK32_UNDEFINED; + return JSVAL_IS_UNDEFINED_IMPL(data); } bool isNull() const { - return data.s.mask32 == JSVAL_MASK32_NULL; + return JSVAL_IS_NULL_IMPL(data); } bool isNullOrUndefined() const { - return isNullOrUndefinedMask(data.s.mask32); + return JSVAL_IS_SINGLETON_IMPL(data); } bool isInt32() const { @@ -3187,16 +3137,15 @@ class Value } bool isInt32(int32 i32) const { - return (data.s.mask32 == JSVAL_MASK32_INT32) & - (data.s.payload.i32 == i32); + return JSVAL_IS_SPECIFIC_INT32_IMPL(data, i32); } bool isDouble() const { - return isDoubleMask(data.s.mask32); + return JSVAL_IS_DOUBLE_IMPL(data); } bool isNumber() const { - return isNumberMask(data.s.mask32); + return JSVAL_IS_NUMBER_IMPL(data); } bool isString() const { @@ -3212,19 +3161,19 @@ class Value } bool isObject() const { - return isObjectMask(data.s.mask32); + return JSVAL_IS_OBJECT_IMPL(data); } bool isPrimitive() const { - return !isObject(); + return JSVAL_IS_PRIMITIVE_IMPL(data); } bool isObjectOrNull() const { - return isObjectOrNullMask(data.s.mask32); + return JSVAL_IS_OBJECT_OR_NULL_IMPL(data); } bool isGCThing() const { - return (data.s.mask32 & JSVAL_MASK32_GCTHING) > JSVAL_MASK32_CLEAR; + return JSVAL_IS_GCTHING_IMPL(data); } bool isBoolean() const { @@ -3232,13 +3181,11 @@ class Value } bool isTrue() const { - return data.s.mask32 == JSVAL_MASK32_BOOLEAN && - data.s.payload.boo == JS_TRUE; + return JSVAL_IS_SPECIFIC_BOOLEAN(data, true); } bool isFalse() const { - return data.s.mask32 == JSVAL_MASK32_BOOLEAN && - data.s.payload.boo == JS_FALSE; + return JSVAL_IS_SPECIFIC_BOOLEAN(data, false); } bool isMagic() const { @@ -3252,13 +3199,17 @@ class Value int32 traceKind() const { JS_ASSERT(isGCThing()); - return (int32)(data.s.mask32 == JSVAL_MASK32_STRING); + return JSVAL_TRACE_KIND_IMPL(data); } +#ifdef DEBUG JSWhyMagic whyMagic() const { JS_ASSERT(isMagic()); return data.s.payload.why; } +#endif + + /* Comparison */ bool operator==(const Value &rhs) const { return data.asBits == rhs.data.asBits; @@ -3268,6 +3219,18 @@ class Value return data.asBits != rhs.data.asBits; } + friend bool SamePrimitiveTypeOrBothObjects(const Value &lhs, const Value &rhs) { + return JSVAL_SAME_PRIMITIVE_TYPE_OR_BOTH_OBJECTS_IMPL(lhs.data, rhs.data); + } + + friend bool BothInt32(const Value &lhs, const Value &rhs) { + return JSVAL_BOTH_INT32_IMPL(lhs.data, rhs.data); + } + + friend bool BothString(const Value &lhs, const Value &rhs) { + return JSVAL_BOTH_STRING_IMPL(lhs.data, rhs.data); + } + /* Extract a Value's payload */ int32 asInt32() const { @@ -3288,32 +3251,32 @@ class Value JSString *asString() const { JS_ASSERT(isString()); - return data.s.payload.str; + return JSVAL_TO_STRING_IMPL(data); } JSObject &asNonFunObj() const { JS_ASSERT(isNonFunObj()); - return *data.s.payload.obj; + return *JSVAL_TO_OBJECT_IMPL(data); } JSObject &asFunObj() const { JS_ASSERT(isFunObj()); - return *data.s.payload.obj; + return *JSVAL_TO_OBJECT_IMPL(data); } JSObject &asObject() const { JS_ASSERT(isObject()); - return *data.s.payload.obj; + return *JSVAL_TO_OBJECT_IMPL(data); } JSObject *asObjectOrNull() const { JS_ASSERT(isObjectOrNull()); - return data.s.payload.obj; + return JSVAL_TO_OBJECT_IMPL(data); } void *asGCThing() const { JS_ASSERT(isGCThing()); - return data.s.payload.ptr; + return JSVAL_TO_GCTHING_IMPL(data); } bool asBoolean() const { @@ -3321,6 +3284,11 @@ class Value return data.s.payload.boo; } + uint32 asRawUint32() const { + JS_ASSERT(!isDouble()); + return data.s.payload.u32; + } + /* Swap two Values */ void swap(Value &rhs) { diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 37a26544a63..dcad52ce4f1 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -385,8 +385,7 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags) namespace js { -static const uint32 FAKE_NUMBER_MASK = JSVAL_MASK32_INT32 | - PrimitiveValue::DOUBLE_MASK; +static const uint32 FAKE_NUMBER_MASK = JSVAL_MASK32_INT32 | PrimitiveValue::DOUBLE_MASK; const uint32 PrimitiveValue::Masks[PrimitiveValue::THISP_ARRAY_SIZE] = { 0, /* 000 */ @@ -952,36 +951,29 @@ EqualObjects(JSContext *cx, JSObject *lobj, JSObject *robj) } bool -StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval) +StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref) { - uint32 lmask = lval.data.s.mask32; - uint32 rmask = rval.data.s.mask32; - if (lmask == rmask) { - if (lmask == JSVAL_MASK32_STRING) - return js_EqualStrings(lval.data.s.payload.str, rval.data.s.payload.str); - if (Value::isObjectMask(lmask)) - return EqualObjects(cx, lval.data.s.payload.obj, rval.data.s.payload.obj); - if (Value::isDoubleMask(lmask)) - return JSDOUBLE_COMPARE(lval.data.asDouble, ==, rval.data.asDouble, JS_FALSE); - JS_ASSERT(lmask == JSVAL_MASK32_NULL || - lmask == JSVAL_MASK32_UNDEFINED || - lmask == JSVAL_MASK32_INT32 || - lmask == JSVAL_MASK32_FUNOBJ || - lmask == JSVAL_MASK32_NONFUNOBJ || - lmask == JSVAL_MASK32_BOOLEAN); - return lval.data.s.payload.u32 == rval.data.s.payload.u32; + Value lval = lref, rval = rref; + if (SamePrimitiveTypeOrBothObjects(lval, rval)) { + if (lval.isString()) + return js_EqualStrings(lval.asString(), rval.asString()); + if (lval.isDouble()) + return JSDOUBLE_COMPARE(lval.asDouble(), ==, rval.asDouble(), JS_FALSE); + if (lval.isObject()) + return EqualObjects(cx, &lval.asObject(), &rval.asObject()); + return lval.asRawUint32() == rval.asRawUint32(); } - if (Value::isNumberMask(lmask) && Value::isNumberMask(rmask)) { - double ld = lmask == JSVAL_MASK32_INT32 ? lval.data.s.payload.i32 - : lval.data.asDouble; - double rd = rmask == JSVAL_MASK32_INT32 ? rval.data.s.payload.i32 - : rval.data.asDouble; + if (lval.isDouble() && rval.isInt32()) { + double ld = lval.asDouble(); + double rd = rval.asInt32(); + return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + } + if (lval.isInt32() && rval.isDouble()) { + double ld = lval.asInt32(); + double rd = rval.asDouble(); return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); } - - if (Value::isObjectMask(lmask) && Value::isObjectMask(rmask)) - return EqualObjects(cx, lval.data.s.payload.obj, rval.data.s.payload.obj); return false; } @@ -1787,6 +1779,15 @@ namespace reprmeter { VALUE_TO_OBJECT(cx, vp_, obj); \ JS_END_MACRO +#define DEFAULT_VALUE(cx, n, hint, v) \ + JS_BEGIN_MACRO \ + JS_ASSERT(v.isObject()); \ + JS_ASSERT(v == regs.sp[n]); \ + if (!v.asObject().defaultValue(cx, hint, ®s.sp[n])) \ + goto error; \ + v = regs.sp[n]; \ + JS_END_MACRO + /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */ static JS_ALWAYS_INLINE bool CanIncDecWithoutOverflow(int32_t i) diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 0b63adfb97b..2d6af15b766 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -283,7 +283,7 @@ class PrimitiveValue static const uint32 Masks[THISP_ARRAY_SIZE]; public: - static const uint32 DOUBLE_MASK = 0x8000; + static const uint32 DOUBLE_MASK = 0xFFFF8000; static bool test(JSFunction *fun, const Value &v) { uint32 mask = Masks[(fun->flags >> THISP_SHIFT) & THISP_MASK]; diff --git a/js/src/jsops.cpp b/js/src/jsops.cpp index 3258d5f9ee3..e7cf54f79cd 100644 --- a/js/src/jsops.cpp +++ b/js/src/jsops.cpp @@ -816,9 +816,9 @@ END_CASE(JSOP_BITAND) */ #if JS_HAS_XML_SUPPORT #define XML_EQUALITY_OP(OP) \ - if ((lmask == JSVAL_MASK32_NONFUNOBJ && lref.asObject().isXML()) || \ - (rmask == JSVAL_MASK32_NONFUNOBJ && rref.asObject().isXML())) { \ - if (!js_TestXMLEquality(cx, lref, rref, &cond)) \ + if ((lval.isNonFunObj() && lval.asObject().isXML()) || \ + (rval.isNonFunObj() && rval.asObject().isXML())) { \ + if (!js_TestXMLEquality(cx, lval, rval, &cond)) \ goto error; \ cond = cond OP JS_TRUE; \ } else @@ -826,7 +826,7 @@ END_CASE(JSOP_BITAND) #define EXTENDED_EQUALITY_OP(OP) \ if (((clasp = l->getClass())->flags & JSCLASS_IS_EXTENDED) && \ ((ExtendedClass *)clasp)->equality) { \ - if (!((ExtendedClass *)clasp)->equality(cx, l, &rref, &cond)) \ + if (!((ExtendedClass *)clasp)->equality(cx, l, &lval, &cond)) \ goto error; \ cond = cond OP JS_TRUE; \ } else @@ -837,57 +837,42 @@ END_CASE(JSOP_BITAND) #define EQUALITY_OP(OP, IFNAN) \ JS_BEGIN_MACRO \ - /* Depends on the value representation. */ \ Class *clasp; \ JSBool cond; \ - Value &rref = regs.sp[-1]; \ - Value &lref = regs.sp[-2]; \ - uint32 rmask = rref.data.s.mask32; \ - uint32 lmask = lref.data.s.mask32; \ + Value rval = regs.sp[-1]; \ + Value lval = regs.sp[-2]; \ XML_EQUALITY_OP(OP) \ - if (lmask == rmask || \ - (Value::isObjectMask(lmask) && Value::isObjectMask(rmask))) { \ - if (lmask == JSVAL_MASK32_STRING) { \ - JSString *l = lref.asString(), *r = rref.asString(); \ + if (SamePrimitiveTypeOrBothObjects(lval, rval)) { \ + if (lval.isString()) { \ + JSString *l = lval.asString(), *r = rval.asString(); \ cond = js_EqualStrings(l, r) OP JS_TRUE; \ - } else if (Value::isObjectMask(lmask)) { \ - JSObject *l = &lref.asObject(), *r = &rref.asObject(); \ + } else if (lval.isDouble()) { \ + double l = lval.asDouble(), r = rval.asDouble(); \ + cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \ + } else if (lval.isObject()) { \ + JSObject *l = &lval.asObject(), *r = &rval.asObject(); \ EXTENDED_EQUALITY_OP(OP) \ cond = l OP r; \ - } else if (JS_UNLIKELY(Value::isDoubleMask(lmask))) { \ - double l = lref.asDouble(), r = rref.asDouble(); \ - cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \ } else { \ - cond = lref.data.s.payload.u32 OP rref.data.s.payload.u32; \ + cond = lval.asRawUint32() OP rval.asRawUint32(); \ } \ - } else if (Value::isDoubleMask(lmask) && Value::isDoubleMask(rmask)) { \ - double l = lref.asDouble(), r = rref.asDouble(); \ - cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \ } else { \ - if (Value::isNullOrUndefinedMask(lmask)) { \ - cond = Value::isNullOrUndefinedMask(rmask) OP true; \ - } else if (Value::isNullOrUndefinedMask(rmask)) { \ + if (lval.isNullOrUndefined()) { \ + cond = rval.isNullOrUndefined() OP true; \ + } else if (rval.isNullOrUndefined()) { \ cond = true OP false; \ } else { \ - if (Value::isObjectMask(lmask)) { \ - JSObject &obj = lref.asObject(); \ - if (!obj.defaultValue(cx, JSTYPE_VOID, &lref)) \ - goto error; \ - lmask = lref.data.s.mask32; \ - } \ - if (Value::isObjectMask(rmask)) { \ - JSObject &obj = rref.asObject(); \ - if (!obj.defaultValue(cx, JSTYPE_VOID, &rref)) \ - goto error; \ - rmask = rref.data.s.mask32; \ - } \ - if (lmask == JSVAL_MASK32_STRING && rmask == JSVAL_MASK32_STRING) { \ - JSString *l = lref.asString(), *r = rref.asString(); \ + if (lval.isObject()) \ + DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \ + if (rval.isObject()) \ + DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \ + if (BothString(lval, rval)) { \ + JSString *l = lval.asString(), *r = rval.asString(); \ cond = js_EqualStrings(l, r) OP JS_TRUE; \ } else { \ double l, r; \ - if (!ValueToNumber(cx, lref, &l) || \ - !ValueToNumber(cx, rref, &r)) { \ + if (!ValueToNumber(cx, lval, &l) || \ + !ValueToNumber(cx, rval, &r)) { \ goto error; \ } \ cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \ @@ -963,39 +948,24 @@ END_CASE(JSOP_CASEX) #define RELATIONAL_OP(OP) \ JS_BEGIN_MACRO \ - /* Depends on the value representation */ \ - Value &rref = regs.sp[-1]; \ - Value &lref = regs.sp[-2]; \ - uint32 rmask = rref.data.s.mask32; \ - uint32 lmask = lref.data.s.mask32; \ - uint32 maskand = lmask & rmask; \ + Value rval = regs.sp[-1]; \ + Value lval = regs.sp[-2]; \ bool cond; \ /* Optimize for two int-tagged operands (typical loop control). */ \ - if (maskand == JSVAL_MASK32_INT32) { \ - cond = lref.asInt32() OP rref.asInt32(); \ + if (BothInt32(lval, rval)) { \ + cond = lval.asInt32() OP rval.asInt32(); \ } else { \ - if (Value::isObjectMask(lmask | rmask)) { \ - if (Value::isObjectMask(lmask)) { \ - JSObject &obj = lref.asObject(); \ - if (!obj.defaultValue(cx, JSTYPE_NUMBER, &lref)) \ - goto error; \ - lmask = lref.data.s.mask32; \ - } \ - if (Value::isObjectMask(rmask)) { \ - JSObject &obj = rref.asObject(); \ - if (!obj.defaultValue(cx, JSTYPE_NUMBER, &rref)) \ - goto error; \ - rmask = rref.data.s.mask32; \ - } \ - maskand = lmask & rmask; \ - } \ - if (maskand == JSVAL_MASK32_STRING) { \ - JSString *l = lref.asString(), *r = rref.asString(); \ + if (lval.isObject()) \ + DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \ + if (rval.isObject()) \ + DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \ + if (BothString(lval, rval)) { \ + JSString *l = lval.asString(), *r = rval.asString(); \ cond = js_CompareStrings(l, r) OP 0; \ } else { \ double l, r; \ - if (!ValueToNumber(cx, lref, &l) || \ - !ValueToNumber(cx, rref, &r)) { \ + if (!ValueToNumber(cx, lval, &l) || \ + !ValueToNumber(cx, rval, &r)) { \ goto error; \ } \ cond = JSDOUBLE_COMPARE(l, OP, r, false); \ @@ -1064,14 +1034,11 @@ END_CASE(JSOP_URSH) BEGIN_CASE(JSOP_ADD) { - /* Depends on the value representation */ - Value &rref = regs.sp[-1]; - Value &lref = regs.sp[-2]; - uint32 rmask = rref.data.s.mask32; - uint32 lmask = lref.data.s.mask32; + Value rval = regs.sp[-1]; + Value lval = regs.sp[-2]; - if ((lmask & rmask) == JSVAL_MASK32_INT32) { - int32_t l = lref.asInt32(), r = rref.asInt32(); + if (BothInt32(lval, rval)) { + int32_t l = lval.asInt32(), r = rval.asInt32(); int32_t sum = l + r; regs.sp--; if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) @@ -1080,50 +1047,47 @@ BEGIN_CASE(JSOP_ADD) regs.sp[-1].setInt32(sum); } else #if JS_HAS_XML_SUPPORT - if (lmask == JSVAL_MASK32_NONFUNOBJ && lref.asObject().isXML() && - rmask == JSVAL_MASK32_NONFUNOBJ && rref.asObject().isXML()) { + if (lval.isNonFunObj() && lval.asObject().isXML() && + rval.isNonFunObj() && rval.asObject().isXML()) { Value rval; - if (!js_ConcatenateXML(cx, &lref.asObject(), &rref.asObject(), &rval)) + if (!js_ConcatenateXML(cx, &lval.asObject(), &rval.asObject(), &rval)) goto error; regs.sp--; regs.sp[-1] = rval; } else #endif { - if (Value::isObjectMask(lmask)) { - if (!lref.asObject().defaultValue(cx, JSTYPE_VOID, &lref)) - goto error; - lmask = lref.data.s.mask32; - } - if (Value::isObjectMask(rmask)) { - if (!rref.asObject().defaultValue(cx, JSTYPE_VOID, &rref)) - goto error; - rmask = rref.data.s.mask32; - } - if (lmask == JSVAL_MASK32_STRING || rmask == JSVAL_MASK32_STRING) { - JSString *str1, *str2; - if (lmask == rmask) { - str1 = lref.asString(); - str2 = rref.asString(); - } else if (lmask == JSVAL_MASK32_STRING) { - str1 = lref.asString(); - str2 = js_ValueToString(cx, rref); - if (!str2) - goto error; + if (lval.isObject()) + DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); + if (rval.isObject()) + DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); + bool lIsString, rIsString; + if ((lIsString = lval.isString()) | (rIsString = rval.isString())) { + JSString *lstr, *rstr; + if (lIsString) { + lstr = lval.asString(); } else { - str2 = rref.asString(); - str1 = js_ValueToString(cx, lref); - if (!str1) + lstr = js_ValueToString(cx, lval); + if (!lstr) goto error; + regs.sp[-2].setString(lstr); } - JSString *str = js_ConcatStrings(cx, str1, str2); + if (rIsString) { + rstr = rval.asString(); + } else { + rstr = js_ValueToString(cx, rval); + if (!rstr) + goto error; + regs.sp[-1].setString(rstr); + } + JSString *str = js_ConcatStrings(cx, lstr, rstr); if (!str) goto error; regs.sp--; regs.sp[-1].setString(str); } else { double l, r; - if (!ValueToNumber(cx, lref, &l) || !ValueToNumber(cx, rref, &r)) + if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r)) goto error; l += r; regs.sp--; @@ -2135,15 +2099,10 @@ END_CASE(JSOP_GETELEM) BEGIN_CASE(JSOP_CALLELEM) { - /* Depends on the value representation. */ - /* Fetch the left part and resolve it to a non-null object. */ JSObject *obj; FETCH_OBJECT(cx, -2, obj); - /* Save the mask so that we don't need to query it later. */ - uint32 objmask = regs.sp[-2].data.s.mask32; - /* Fetch index and convert it to id suitable for use with obj. */ jsid id; FETCH_ELEMENT_ID(obj, -1, id); @@ -2161,8 +2120,7 @@ BEGIN_CASE(JSOP_CALLELEM) } else #endif { - regs.sp[-1].data.s.mask32 = objmask; - regs.sp[-1].data.s.payload.obj = obj; + regs.sp[-1].setObject(*obj); } } END_CASE(JSOP_CALLELEM) diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 24acd32d6d4..32700a174c4 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -158,35 +158,12 @@ typedef struct JSONParser JSONParser; */ /* - * TODO: wrong, fix, explain more - * Engine-internal value details: - * - * A jsval has an abstract type which is represented by a mask which assigns a - * bit to each type. This allows fast set-membership queries. However, we give - * one type (null) a mask of 0 for two reasons: - * - * 1. memset'ing values to 0 produces a valid value. This was true of the old, - * boxed jsvals (and now jsboxedwords) and eases the transition. - * - * 2. Testing for null can often be compiled to slightly shorter/faster code. - * - * The down-side is that set-membership queries need to be done more carefully. - * E.g., to test whether a value v is undefined or null, the correct test is: - * - * (v.mask & ~UndefinedMask) == 0 - * - * instead of the intuitive (but incorrect) test: - * - * (v.mask & (NullMask | UndefinedMask)) != 0 - * - * Since the value representation is kept a private detail of js::Value and - * only exposed to a few functions through friendship, this type of error - * should be hidden behind simple inline methods like v.isNullOrUndefined(). + * TODO: explain boxing strategy */ typedef enum JSValueMask16 -#if defined(_MSC_VER) && _MSC_VER >= 1400 -: unsigned short +#if defined(_MSC_VER) + : uint16 #endif { JSVAL_MASK16_NULL = (uint16)0x0001, @@ -203,6 +180,11 @@ typedef enum JSValueMask16 JSVAL_MASK16_OBJORNULL = JSVAL_MASK16_OBJECT | JSVAL_MASK16_NULL, JSVAL_MASK16_GCTHING = JSVAL_MASK16_OBJECT | JSVAL_MASK16_STRING, + /* + * This enumerator value plus __attribute__((packed)) plus the static + * assert that sizeof(JSValueMask16) == 2 should guarantee that enumerators + * are uint16 in GCC. + */ JSVAL_NANBOX_PATTERN = ((uint16)0xFFFF) } #if defined(__GNUC__) @@ -210,45 +192,36 @@ __attribute__((packed)) #endif JSValueMask16; -#define JSVAL_MASK32_CLEAR ((uint32)0xFFFF0000) - -#define JSVAL_MASK32_NULL ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_NULL)) -#define JSVAL_MASK32_UNDEFINED ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_UNDEFINED)) -#define JSVAL_MASK32_INT32 ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_INT32)) -#define JSVAL_MASK32_STRING ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_STRING)) -#define JSVAL_MASK32_NONFUNOBJ ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_NONFUNOBJ)) -#define JSVAL_MASK32_FUNOBJ ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_FUNOBJ)) -#define JSVAL_MASK32_BOOLEAN ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_BOOLEAN)) -#define JSVAL_MASK32_MAGIC ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_MAGIC)) - -#define JSVAL_MASK32_SINGLETON ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_SINGLETON)) -#define JSVAL_MASK32_OBJECT ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_OBJECT)) -#define JSVAL_MASK32_OBJORNULL ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_OBJORNULL)) -#define JSVAL_MASK32_GCTHING ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_GCTHING)) - -typedef enum JSWhyMagic -{ - JS_ARRAY_HOLE, /* a hole in a dense array */ - JS_ARGS_HOLE, /* a hole in the args object's array */ - JS_NATIVE_ENUMERATE, /* indicates that a custom enumerate hook forwarded - * to js_Enumerate, which really means the object can be - * enumerated like a native object. */ - JS_NO_ITER_VALUE, /* there is not a pending iterator value */ - JS_GENERATOR_CLOSING /* exception value thrown when closing a generator */ -} JSWhyMagic; - -typedef union jsval_payload -{ - int32 i32; - uint32 u32; - JSBool boo; -#if JS_BITS_PER_WORD == 32 - JSString *str; - JSObject *obj; - void *ptr; +typedef enum JSValueMask32 +#if defined(_MSC_VER) + : uint32 #endif - JSWhyMagic why; -} jsval_data; +{ + /* + * This enumerator value plus __attribute__((packed)) plus the static + * assert that sizeof(JSValueMask32) == 4 should guarantee that enumerators + * are uint32 in GCC. + */ + JSVAL_MASK32_CLEAR = ((uint32)0xFFFF0000), + + JSVAL_MASK32_NULL = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_NULL)), + JSVAL_MASK32_UNDEFINED = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_UNDEFINED)), + JSVAL_MASK32_INT32 = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_INT32)), + JSVAL_MASK32_STRING = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_STRING)), + JSVAL_MASK32_NONFUNOBJ = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_NONFUNOBJ)), + JSVAL_MASK32_FUNOBJ = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_FUNOBJ)), + JSVAL_MASK32_BOOLEAN = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_BOOLEAN)), + JSVAL_MASK32_MAGIC = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_MAGIC)), + + JSVAL_MASK32_SINGLETON = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_SINGLETON)), + JSVAL_MASK32_OBJECT = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_OBJECT)), + JSVAL_MASK32_OBJORNULL = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_OBJORNULL)), + JSVAL_MASK32_GCTHING = ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_GCTHING)) +} +#if defined(__GNUC__) +__attribute__((packed)) +#endif +JSValueMask32; #ifdef __GNUC__ # define VALUE_ALIGNMENT __attribute__((aligned (8))) @@ -264,32 +237,376 @@ typedef union jsval_payload # error "TODO: do something for compiler" #endif +typedef VALUE_ALIGNMENT uint64 jsval; + +#define BUILD_JSVAL(mask32, payload) ((jsval)((((uint64)(uint32)(mask32)) << 32) | (uint32)(payload))) + +typedef enum JSWhyMagic +{ + JS_ARRAY_HOLE, /* a hole in a dense array */ + JS_ARGS_HOLE, /* a hole in the args object's array */ + JS_NATIVE_ENUMERATE, /* indicates that a custom enumerate hook forwarded + * to js_Enumerate, which really means the object can be + * enumerated like a native object. */ + JS_NO_ITER_VALUE, /* there is not a pending iterator value */ + JS_GENERATOR_CLOSING /* exception value thrown when closing a generator */ +} JSWhyMagic; + #if !defined(IS_LITTLE_ENDIAN) -# error "Need to fix up jsval_layout" +# error "Unsupported configuration" #endif -// TODO: explain typedef union jsval_layout { uint64 asBits; struct { - jsval_payload payload; + union { + int32 i32; + uint32 u32; + JSBool boo; +#if JS_BITS_PER_WORD == 32 + JSString *str; + JSObject *obj; + void *ptr; +#elif JS_BITS_PER_WORD == 64 + uint32 ptr; +#else +# error "Unsupported configuration" +#endif + JSWhyMagic why; + } payload; union { struct { JSValueMask16 mask16; uint16 nanBits; } tag; - uint32 mask32; + JSValueMask32 mask32; }; } s; double asDouble; } jsval_layout; -typedef uint64 jsval; +#if JS_BITS_PER_WORD == 32 -/* These are engine-internal details, not part of the public API */ -#define DOUBLE_AS_JSVAL(d) (*(jsval *)&(d)) -#define JSVAL_CONSTANT(mask32, payload) ((jsval)((((uint64)(mask32)) << 32) | (payload))) +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_NULL_IMPL(jsval_layout l) +{ + return l.s.mask32 == JSVAL_MASK32_NULL; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_UNDEFINED_IMPL(jsval_layout l) +{ + return l.s.mask32 == JSVAL_MASK32_UNDEFINED; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32 i32) +{ + return l.s.mask32 == JSVAL_MASK32_INT32 && l.s.payload.i32 == i32; +} + +static JS_ALWAYS_INLINE jsval_layout +INT32_TO_JSVAL_IMPL(int32 i) +{ + jsval_layout l; + l.s.mask32 = JSVAL_MASK32_INT32; + l.s.payload.i32 = i; + return l; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_DOUBLE_IMPL(jsval_layout l) +{ + return l.s.mask32 < JSVAL_MASK32_CLEAR; +} + +static JS_ALWAYS_INLINE jsval_layout +DOUBLE_TO_JSVAL_IMPL(jsdouble d) +{ + jsval_layout l; + l.asDouble = d; + JS_ASSERT(l.s.tag.nanBits != JSVAL_NANBOX_PATTERN); + return l; +} + +static JS_ALWAYS_INLINE jsval_layout +STRING_TO_JSVAL_IMPL(JSString *str) +{ + jsval_layout l; + l.s.mask32 = JSVAL_MASK32_STRING; + l.s.payload.str = str; + return l; +} + +static JS_ALWAYS_INLINE JSString * +JSVAL_TO_STRING_IMPL(jsval_layout l) +{ + return l.s.payload.str; +} + +static JS_ALWAYS_INLINE JSObject * +JSVAL_TO_OBJECT_IMPL(jsval_layout l) +{ + return l.s.payload.obj; +} + +static JS_ALWAYS_INLINE jsval_layout +OBJECT_TO_JSVAL_IMPL(JSValueMask32 mask, JSObject *obj) +{ + jsval_layout l; + l.s.mask32 = mask; + l.s.payload.obj = obj; + return l; +} + +static JS_ALWAYS_INLINE jsval_layout +BOOLEAN_TO_JSVAL_IMPL(JSBool b) +{ + jsval_layout l; + l.s.mask32 = JSVAL_MASK32_BOOLEAN; + l.s.payload.boo = b; + return l; +} + +static JS_ALWAYS_INLINE void * +JSVAL_TO_GCTHING_IMPL(jsval_layout l) +{ + return l.s.payload.ptr; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_SPECIFIC_BOOLEAN(jsval_layout l, JSBool b) +{ + return (l.s.mask32 == JSVAL_MASK32_BOOLEAN) && (l.s.payload.boo == b); +} + +static JS_ALWAYS_INLINE jsval_layout +PRIVATE_TO_JSVAL_IMPL(void *ptr) +{ + JS_ASSERT(((uint32)ptr | 1) == 0); + jsval_layout l; + l.s.tag.nanBits = 0; + l.s.payload.ptr = ptr; + return l; +} + +static JS_ALWAYS_INLINE void * +JSVAL_TO_PRIVATE_IMPL(jsval_layout l) +{ + return l.s.payload.ptr; +} + +static JS_ALWAYS_INLINE jsval_layout +MAGIC_TO_JSVAL_IMPL(JSWhyMagic why) +{ + jsval_layout l; + l.s.mask32 = JSVAL_MASK32_MAGIC; + l.s.payload.why = why; + return l; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_SAME_PRIMITIVE_TYPE_OR_BOTH_OBJECTS_IMPL(jsval_layout lhs, jsval_layout rhs) +{ + return ((lhs.s.mask32 ^ rhs.s.mask32) & ~(uint32)JSVAL_MASK16_OBJECT) == 0 || + (lhs.s.mask32 < JSVAL_MASK32_CLEAR && rhs.s.mask32 < JSVAL_MASK32_CLEAR); +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_BOTH_STRING_IMPL(jsval_layout lhs, jsval_layout rhs) +{ + return (lhs.s.mask32 & rhs.s.mask32) == JSVAL_MASK32_STRING; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_BOTH_INT32_IMPL(jsval_layout lhs, jsval_layout rhs) +{ + return (lhs.s.mask32 & rhs.s.mask32) == JSVAL_MASK32_INT32; +} + +#elif JS_BITS_PER_WORD == 64 + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_NULL_IMPL(jsval_layout l) +{ + return l.asBits == BUILD_JSVAL(JSVAL_MASK32_NULL, 0); +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_UNDEFINED_IMPL(jsval_layout l) +{ + return l.asBits == BUILD_JSVAL(JSVAL_MASK32_UNDEFINED, 0); +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32 i32) +{ + return l.asBits == BUILD_JSVAL(JSVAL_MASK32_INT32, i32); +} + +static JS_ALWAYS_INLINE jsval_layout +INT32_TO_JSVAL_IMPL(int32 i) +{ + jsval_layout l; + l.asBits = BUILD_JSVAL(JSVAL_MASK32_INT32, i); + return l; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_DOUBLE_IMPL(jsval_layout l) +{ + return l.asBits < BUILD_JSVAL(JSVAL_MASK32_CLEAR, 0); +} + +static JS_ALWAYS_INLINE jsval_layout +DOUBLE_TO_JSVAL_IMPL(jsdouble d) +{ + jsval_layout l; + l.asDouble = d; + JS_ASSERT(l.s.tag.nanBits != JSVAL_NANBOX_PATTERN); + return l; +} + +static JS_ALWAYS_INLINE jsval_layout +STRING_TO_JSVAL_IMPL(JSString *str) +{ + JS_ASSERT((size_t)str < (size_t)0xFFFFFFFF); + jsval_layout l; + l.asBits = BUILD_JSVAL(JSVAL_MASK32_STRING, (uint32)str); + return l; +} + +static JS_ALWAYS_INLINE JSString * +JSVAL_TO_STRING_IMPL(jsval_layout v) +{ + return (JSString *)(uint64)l.s.payload.ptr; +} + +static JS_ALWAYS_INLINE JSObject * +JSVAL_TO_OBJECT_IMPL(jsval_layout l) +{ + return (JSObject *)(uint64)l.s.payload.ptr; +} + +static JS_ALWAYS_INLINE jsval_layout +OBJECT_TO_JSVAL_IMPL(JSValueMask32 mask32, JSObject *obj) +{ + JS_ASSERT((size_t)obj < (size_t)0xFFFFFFFF); + jsval_layout l; + l.asBits = BUILD_JSVAL(mask32, (uint32)obj); + return l; +} + +static JS_ALWAYS_INLINE jsval_layout +BOOLEAN_TO_JSVAL_IMPL(JSBool b) +{ + jsval_layout l; + l.asBits = BUILD_JSVAL(JSVAL_MASK32_BOOLEAN, b); + return l; +} + +static JS_ALWAYS_INLINE void * +JSVAL_TO_GCTHING_IMPL(jsval_layout l) +{ + return (void *)(uint64)l.s.payload.ptr; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_SPECIFIC_BOOLEAN(jsval_layout l, JSBool b) +{ + return l.asBits == BUILD_JSVAL(JSVAL_MASK32_BOOLEAN, b); +} + +static JS_ALWAYS_INLINE jsval_layout +PRIVATE_TO_JSVAL_IMPL(void *ptr) +{ + JS_ASSERT(((uint32)ptr | 1) == 0); + jsval_layout l; + l.asBits = 0x8000000000000000LL | (ptr >> 1); + return l; +} + +static JS_ALWAYS_INLINE void * +JSVAL_TO_PRIVATE_IMPL(jsval_layout l) +{ + return (void *)(l.s.payload.asBits << 1); +} + +static JS_ALWAYS_INLINE jsval_layout +MAGIC_TO_JSVAL_IMPL(JSWhyMagic why) +{ + jsval_layout l; + l.asBits = BUILD_JSVAL(JSVAL_MASK32_MAGIC, why); + return l; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_SAME_PRIMITIVE_TYPE_OR_BOTH_OBJECTS_IMPL(jsval_layout lhs, jsval_layout rhs) +{ + uint32 xor32 = (uint32)((lhs.asBits ^ rhs.asBits) >> 32); + return (xor32 & ~(uint32)JSVAL_MASK16_OBJECT) == 0 || + (JSVAL_IS_DOUBLE_IMPL(lhs) && JSVAL_IS_DOUBLE_IMPL(rhs)); +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_BOTH_STRING_IMPL(jsval_layout lhs, jsval_layout rhs) +{ + return (uint32)((lhs.asBits & rhs.asBits) >> 32) == JSVAL_MASK32_STRING; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_BOTH_INT32_IMPL(jsval_layout lhs, jsval_layout rhs) +{ + return (uint32)((lhs.asBits & rhs.asBits) >> 32) == JSVAL_MASK32_INT32; +} + +#else +# error "Unsupported configuration" +#endif + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_SINGLETON_IMPL(jsval_layout l) +{ + return (l.s.mask32 & JSVAL_MASK32_SINGLETON) > JSVAL_MASK32_CLEAR; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_NUMBER_IMPL(jsval_layout l) +{ + JSValueMask32 mask = l.s.mask32; + return mask < JSVAL_MASK32_CLEAR || mask == JSVAL_MASK32_INT32; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_OBJECT_IMPL(jsval_layout l) +{ + return (l.s.mask32 & JSVAL_MASK32_OBJECT) > JSVAL_MASK32_CLEAR; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_OBJECT_OR_NULL_IMPL(jsval_layout l) +{ + return (l.s.mask32 & JSVAL_MASK32_OBJORNULL) > JSVAL_MASK32_CLEAR; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_PRIMITIVE_IMPL(jsval_layout l) +{ + return (l.s.mask32 & JSVAL_MASK32_OBJECT) <= JSVAL_MASK32_CLEAR; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_GCTHING_IMPL(jsval_layout l) +{ + return (l.s.mask32 & JSVAL_MASK32_GCTHING) > JSVAL_MASK32_CLEAR; +} + +static JS_ALWAYS_INLINE uint32 +JSVAL_TRACE_KIND_IMPL(jsval_layout l) +{ + return (uint32)(l.s.mask32 == JSVAL_MASK32_STRING); +} /* * Boxed word macros (private engine detail)