[mq]: Refactor value logic for better reuse

This commit is contained in:
Luke Wagner 2010-05-26 16:36:47 -07:00
parent a2c395a9dc
commit f7242c4d3f
5 changed files with 585 additions and 341 deletions

View File

@ -52,12 +52,12 @@
JS_BEGIN_EXTERN_C JS_BEGIN_EXTERN_C
/* Well-known JS values, initialized on startup. */ /* Well-known JS values, initialized on startup. */
#define JSVAL_NULL JSVAL_CONSTANT(JSVAL_MASK32_NULL, 0) #define JSVAL_NULL BUILD_JSVAL(JSVAL_MASK32_NULL, 0)
#define JSVAL_ZERO JSVAL_CONSTANT(JSVAL_MASK32_INT32, 0) #define JSVAL_ZERO BUILD_JSVAL(JSVAL_MASK32_INT32, 0)
#define JSVAL_ONE JSVAL_CONSTANT(JSVAL_MASK32_INT32, 1) #define JSVAL_ONE BUILD_JSVAL(JSVAL_MASK32_INT32, 1)
#define JSVAL_FALSE JSVAL_CONSTANT(JSVAL_MASK32_BOOLEAN, JS_FALSE) #define JSVAL_FALSE BUILD_JSVAL(JSVAL_MASK32_BOOLEAN, JS_FALSE)
#define JSVAL_TRUE JSVAL_CONSTANT(JSVAL_MASK32_BOOLEAN, JS_TRUE) #define JSVAL_TRUE BUILD_JSVAL(JSVAL_MASK32_BOOLEAN, JS_TRUE)
#define JSVAL_VOID JSVAL_CONSTANT(JSVAL_MASK32_UNDEFINED, 0) #define JSVAL_VOID BUILD_JSVAL(JSVAL_MASK32_UNDEFINED, 0)
/* Predicates for type testing. */ /* Predicates for type testing. */
@ -65,14 +65,14 @@ static JS_ALWAYS_INLINE JSBool
JSVAL_IS_NULL(jsval v) JSVAL_IS_NULL(jsval v)
{ {
jsval_layout l = { v }; jsval_layout l = { v };
return l.s.mask32 == JSVAL_MASK32_NULL; return JSVAL_IS_NULL_IMPL(l);
} }
static JS_ALWAYS_INLINE JSBool static JS_ALWAYS_INLINE JSBool
JSVAL_IS_VOID(jsval v) JSVAL_IS_VOID(jsval v)
{ {
jsval_layout l = { v }; jsval_layout l = { v };
return l.s.mask32 == JSVAL_MASK32_UNDEFINED; return JSVAL_IS_UNDEFINED_IMPL(l);
} }
static JS_ALWAYS_INLINE JSBool static JS_ALWAYS_INLINE JSBool
@ -103,17 +103,14 @@ INT_FITS_IN_JSVAL(jsint i)
static JS_ALWAYS_INLINE jsval static JS_ALWAYS_INLINE jsval
INT_TO_JSVAL(int32 i) INT_TO_JSVAL(int32 i)
{ {
jsval_layout l; return INT32_TO_JSVAL_IMPL(i).asBits;
l.s.mask32 = JSVAL_MASK32_INT32;
l.s.payload.i32 = i;
return l.asBits;
} }
static JS_ALWAYS_INLINE JSBool static JS_ALWAYS_INLINE JSBool
JSVAL_IS_DOUBLE(jsval v) JSVAL_IS_DOUBLE(jsval v)
{ {
jsval_layout l = { v }; jsval_layout l = { v };
return l.s.mask32 < JSVAL_MASK32_CLEAR; return JSVAL_IS_DOUBLE_IMPL(l);
} }
static JS_ALWAYS_INLINE jsdouble static JS_ALWAYS_INLINE jsdouble
@ -127,16 +124,14 @@ JSVAL_TO_DOUBLE(jsval v)
static JS_ALWAYS_INLINE jsval static JS_ALWAYS_INLINE jsval
DOUBLE_TO_JSVAL(jsdouble d) DOUBLE_TO_JSVAL(jsdouble d)
{ {
jsval_layout l; return DOUBLE_TO_JSVAL_IMPL(d).asBits;
l.asDouble = d;
JS_ASSERT(l.s.tag.nanBits != JSVAL_NANBOX_PATTERN);
return l.asBits;
} }
static JS_ALWAYS_INLINE JSBool static JS_ALWAYS_INLINE JSBool
JSVAL_IS_NUMBER(jsval v) 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 static JS_ALWAYS_INLINE JSBool
@ -151,30 +146,20 @@ JSVAL_TO_STRING(jsval v)
{ {
JS_ASSERT(JSVAL_IS_STRING(v)); JS_ASSERT(JSVAL_IS_STRING(v));
jsval_layout l = { v }; jsval_layout l = { v };
return l.s.payload.str; return JSVAL_TO_STRING_IMPL(l);
} }
static JS_ALWAYS_INLINE jsval static JS_ALWAYS_INLINE jsval
STRING_TO_JSVAL(JSString *str) STRING_TO_JSVAL(JSString *str)
{ {
jsval_layout l; return STRING_TO_JSVAL_IMPL(str).asBits;
l.s.mask32 = JSVAL_MASK32_STRING;
l.s.payload.str = str;
return l.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 static JS_ALWAYS_INLINE JSBool
JSVAL_IS_OBJECT(jsval v) JSVAL_IS_OBJECT(jsval v)
{ {
jsval_layout l = { 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 * static JS_ALWAYS_INLINE JSObject *
@ -182,9 +167,10 @@ JSVAL_TO_OBJECT(jsval v)
{ {
JS_ASSERT(JSVAL_IS_OBJECT(v)); JS_ASSERT(JSVAL_IS_OBJECT(v));
jsval_layout l = { 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 static JS_ALWAYS_INLINE jsval
OBJECT_TO_JSVAL(JSObject *obj) OBJECT_TO_JSVAL(JSObject *obj)
{ {
@ -193,12 +179,10 @@ OBJECT_TO_JSVAL(JSObject *obj)
if (!obj) if (!obj)
return JSVAL_NULL; return JSVAL_NULL;
uint32 mask = JS_ObjectIsFunction(NULL, obj) ? JSVAL_MASK32_FUNOBJ
: JSVAL_MASK32_NONFUNOBJ; JSValueMask32 mask = JS_ObjectIsFunction(NULL, obj) ? JSVAL_MASK32_FUNOBJ
jsval_layout l; : JSVAL_MASK32_NONFUNOBJ;
l.s.mask32 = mask; return OBJECT_TO_JSVAL_IMPL(mask, obj).asBits;
l.s.payload.obj = obj;
return l.asBits;
} }
static JS_ALWAYS_INLINE JSBool static JS_ALWAYS_INLINE JSBool
@ -219,24 +203,21 @@ JSVAL_TO_BOOLEAN(jsval v)
static JS_ALWAYS_INLINE jsval static JS_ALWAYS_INLINE jsval
BOOLEAN_TO_JSVAL(JSBool b) BOOLEAN_TO_JSVAL(JSBool b)
{ {
jsval_layout l; return BOOLEAN_TO_JSVAL_IMPL(b).asBits;
l.s.mask32 = JSVAL_MASK32_BOOLEAN;
l.s.payload.boo = b;
return l.asBits;
} }
static JS_ALWAYS_INLINE JSBool static JS_ALWAYS_INLINE JSBool
JSVAL_IS_PRIMITIVE(jsval v) JSVAL_IS_PRIMITIVE(jsval v)
{ {
jsval_layout l = { 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 static JS_ALWAYS_INLINE JSBool
JSVAL_IS_GCTHING(jsval v) JSVAL_IS_GCTHING(jsval v)
{ {
jsval_layout l = { 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 * static JS_ALWAYS_INLINE void *
@ -244,24 +225,23 @@ JSVAL_TO_GCTHING(jsval v)
{ {
JS_ASSERT(JSVAL_IS_GCTHING(v)); JS_ASSERT(JSVAL_IS_GCTHING(v));
jsval_layout l = { 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 static JS_ALWAYS_INLINE jsval
PRIVATE_TO_JSVAL(void *ptr) PRIVATE_TO_JSVAL(void *ptr)
{ {
jsval_layout l; return PRIVATE_TO_JSVAL_IMPL(ptr).asBits;
l.s.mask32 = JSVAL_MASK32_INT32;
l.s.payload.ptr = ptr;
return l.asBits;
} }
static JS_ALWAYS_INLINE void * static JS_ALWAYS_INLINE void *
JSVAL_TO_PRIVATE(jsval v) JSVAL_TO_PRIVATE(jsval v)
{ {
JS_ASSERT(JSVAL_IS_INT(v)); JS_ASSERT(JSVAL_IS_DOUBLE(v));
jsval_layout l = { 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. */ /* 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)); JS_ASSERT(JSVAL_IS_GCTHING(v));
jsval_layout l = { v }; jsval_layout l = { v };
return (uint32)(l.s.mask32 == JSVAL_MASK32_STRING); return JSVAL_TRACE_KIND_IMPL(l);
} }
struct JSTracer { struct JSTracer {
@ -3007,44 +2987,26 @@ class Value
* break this encapsulation should be listed as friends below. Also see * break this encapsulation should be listed as friends below. Also see
* uses of public jsval members in jsapi.h/jspubtd.h. * 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; friend class PrimitiveValue;
protected: protected:
/* Type masks */ /* Type masks */
template <int I> class T {};
void staticAssertions() { 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(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(JSWhyMagic) <= 4);
JS_STATIC_ASSERT(sizeof(((jsval_layout *)0)->s.payload) == 4);
JS_STATIC_ASSERT(sizeof(jsval) == 8);
} }
jsval_layout data; 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: public:
/* Constructors */ /* Constructors */
@ -3075,18 +3037,15 @@ class Value
/* Change to a Value of a single type */ /* Change to a Value of a single type */
void setNull() { void setNull() {
data.s.mask32 = JSVAL_MASK32_NULL; data.asBits = JSVAL_NULL;
data.s.payload.obj = NULL;
} }
void setUndefined() { void setUndefined() {
data.s.mask32 = JSVAL_MASK32_UNDEFINED; data.asBits = JSVAL_VOID;
data.s.payload.obj = NULL;
} }
void setInt32(int32 i) { void setInt32(int32 i) {
data.s.mask32 = JSVAL_MASK32_INT32; data = INT32_TO_JSVAL_IMPL(i);
data.s.payload.i32 = i;
} }
int32 &asInt32Ref() { int32 &asInt32Ref() {
@ -3096,8 +3055,7 @@ class Value
void setDouble(double d) { void setDouble(double d) {
ASSERT_DOUBLE_ALIGN(); ASSERT_DOUBLE_ALIGN();
data.asDouble = d; data = DOUBLE_TO_JSVAL_IMPL(d);
JS_ASSERT(data.s.tag.nanBits != JSVAL_NANBOX_PATTERN);
} }
double &asDoubleRef() { double &asDoubleRef() {
@ -3107,62 +3065,54 @@ class Value
} }
void setString(JSString *str) { void setString(JSString *str) {
data.s.mask32 = JSVAL_MASK32_STRING; data = STRING_TO_JSVAL_IMPL(str);
data.s.payload.str = str;
} }
void setFunObj(JSObject &arg) { void setFunObj(JSObject &arg) {
JS_ASSERT(JS_ObjectIsFunction(NULL, &arg)); JS_ASSERT(JS_ObjectIsFunction(NULL, &arg));
data.s.mask32 = JSVAL_MASK32_FUNOBJ; data = OBJECT_TO_JSVAL_IMPL(JSVAL_MASK32_FUNOBJ, &arg);
data.s.payload.obj = &arg;
} }
void setNonFunObj(JSObject &arg) { void setNonFunObj(JSObject &arg) {
JS_ASSERT(!JS_ObjectIsFunction(NULL, &arg)); JS_ASSERT(!JS_ObjectIsFunction(NULL, &arg));
data.s.mask32 = JSVAL_MASK32_NONFUNOBJ; data = OBJECT_TO_JSVAL_IMPL(JSVAL_MASK32_NONFUNOBJ, &arg);
data.s.payload.obj = &arg;
} }
void setBoolean(bool b) { void setBoolean(bool b) {
data.s.mask32 = JSVAL_MASK32_BOOLEAN; data = BOOLEAN_TO_JSVAL_IMPL(b);
data.s.payload.boo = b;
} }
void setMagic(JSWhyMagic why) { void setMagic(JSWhyMagic why) {
data.s.mask32 = JSVAL_MASK32_MAGIC; data = MAGIC_TO_JSVAL_IMPL(why);
data.s.payload.why = why;
} }
/* Change to a Value of a type dynamically chosen from a set of types */ /* Change to a Value of a type dynamically chosen from a set of types */
void setNumber(uint32 ui) { void setNumber(uint32 ui) {
if (ui > JSVAL_INT_MAX) { if (ui > JSVAL_INT_MAX)
data.asDouble = ui; data = DOUBLE_TO_JSVAL_IMPL(ui);
JS_ASSERT(data.s.tag.nanBits != JSVAL_NANBOX_PATTERN); else
} else { data = INT32_TO_JSVAL_IMPL((int32)ui);
data.s.mask32 = JSVAL_MASK32_INT32;
data.s.payload.i32 = (int32)ui;
}
} }
inline void setNumber(double d); inline void setNumber(double d);
void setFunObjOrNull(JSObject *arg) { void setFunObjOrNull(JSObject *arg) {
JS_ASSERT_IF(arg, JS_ObjectIsFunction(NULL, arg)); JS_ASSERT_IF(arg, JS_ObjectIsFunction(NULL, arg));
data.s.mask32 = arg ? JSVAL_MASK32_FUNOBJ : JSVAL_MASK32_NULL; JSValueMask32 mask = arg ? JSVAL_MASK32_FUNOBJ : JSVAL_MASK32_NULL;
data.s.payload.obj = arg; data = OBJECT_TO_JSVAL_IMPL(mask, arg);
} }
void setFunObjOrUndefined(JSObject *arg) { void setFunObjOrUndefined(JSObject *arg) {
JS_ASSERT_IF(arg, JS_ObjectIsFunction(NULL, arg)); JS_ASSERT_IF(arg, JS_ObjectIsFunction(NULL, arg));
data.s.mask32 = arg ? JSVAL_MASK32_FUNOBJ : JSVAL_MASK32_UNDEFINED; JSValueMask32 mask = arg ? JSVAL_MASK32_FUNOBJ : JSVAL_MASK32_UNDEFINED;
data.s.payload.obj = arg; data = OBJECT_TO_JSVAL_IMPL(mask, arg);
} }
void setNonFunObjOrNull(JSObject *arg) { void setNonFunObjOrNull(JSObject *arg) {
JS_ASSERT_IF(arg, !JS_ObjectIsFunction(NULL, arg)); JS_ASSERT_IF(arg, !JS_ObjectIsFunction(NULL, arg));
data.s.mask32 = arg ? JSVAL_MASK32_NONFUNOBJ : JSVAL_MASK32_NULL; JSValueMask32 mask = arg ? JSVAL_MASK32_NONFUNOBJ : JSVAL_MASK32_NULL;
data.s.payload.obj = arg; data = OBJECT_TO_JSVAL_IMPL(mask, arg);
} }
inline void setObject(JSObject &arg); inline void setObject(JSObject &arg);
@ -3171,15 +3121,15 @@ class Value
/* Query a Value's type */ /* Query a Value's type */
bool isUndefined() const { bool isUndefined() const {
return data.s.mask32 == JSVAL_MASK32_UNDEFINED; return JSVAL_IS_UNDEFINED_IMPL(data);
} }
bool isNull() const { bool isNull() const {
return data.s.mask32 == JSVAL_MASK32_NULL; return JSVAL_IS_NULL_IMPL(data);
} }
bool isNullOrUndefined() const { bool isNullOrUndefined() const {
return isNullOrUndefinedMask(data.s.mask32); return JSVAL_IS_SINGLETON_IMPL(data);
} }
bool isInt32() const { bool isInt32() const {
@ -3187,16 +3137,15 @@ class Value
} }
bool isInt32(int32 i32) const { bool isInt32(int32 i32) const {
return (data.s.mask32 == JSVAL_MASK32_INT32) & return JSVAL_IS_SPECIFIC_INT32_IMPL(data, i32);
(data.s.payload.i32 == i32);
} }
bool isDouble() const { bool isDouble() const {
return isDoubleMask(data.s.mask32); return JSVAL_IS_DOUBLE_IMPL(data);
} }
bool isNumber() const { bool isNumber() const {
return isNumberMask(data.s.mask32); return JSVAL_IS_NUMBER_IMPL(data);
} }
bool isString() const { bool isString() const {
@ -3212,19 +3161,19 @@ class Value
} }
bool isObject() const { bool isObject() const {
return isObjectMask(data.s.mask32); return JSVAL_IS_OBJECT_IMPL(data);
} }
bool isPrimitive() const { bool isPrimitive() const {
return !isObject(); return JSVAL_IS_PRIMITIVE_IMPL(data);
} }
bool isObjectOrNull() const { bool isObjectOrNull() const {
return isObjectOrNullMask(data.s.mask32); return JSVAL_IS_OBJECT_OR_NULL_IMPL(data);
} }
bool isGCThing() const { bool isGCThing() const {
return (data.s.mask32 & JSVAL_MASK32_GCTHING) > JSVAL_MASK32_CLEAR; return JSVAL_IS_GCTHING_IMPL(data);
} }
bool isBoolean() const { bool isBoolean() const {
@ -3232,13 +3181,11 @@ class Value
} }
bool isTrue() const { bool isTrue() const {
return data.s.mask32 == JSVAL_MASK32_BOOLEAN && return JSVAL_IS_SPECIFIC_BOOLEAN(data, true);
data.s.payload.boo == JS_TRUE;
} }
bool isFalse() const { bool isFalse() const {
return data.s.mask32 == JSVAL_MASK32_BOOLEAN && return JSVAL_IS_SPECIFIC_BOOLEAN(data, false);
data.s.payload.boo == JS_FALSE;
} }
bool isMagic() const { bool isMagic() const {
@ -3252,13 +3199,17 @@ class Value
int32 traceKind() const { int32 traceKind() const {
JS_ASSERT(isGCThing()); JS_ASSERT(isGCThing());
return (int32)(data.s.mask32 == JSVAL_MASK32_STRING); return JSVAL_TRACE_KIND_IMPL(data);
} }
#ifdef DEBUG
JSWhyMagic whyMagic() const { JSWhyMagic whyMagic() const {
JS_ASSERT(isMagic()); JS_ASSERT(isMagic());
return data.s.payload.why; return data.s.payload.why;
} }
#endif
/* Comparison */
bool operator==(const Value &rhs) const { bool operator==(const Value &rhs) const {
return data.asBits == rhs.data.asBits; return data.asBits == rhs.data.asBits;
@ -3268,6 +3219,18 @@ class Value
return data.asBits != rhs.data.asBits; 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 */ /* Extract a Value's payload */
int32 asInt32() const { int32 asInt32() const {
@ -3288,32 +3251,32 @@ class Value
JSString *asString() const { JSString *asString() const {
JS_ASSERT(isString()); JS_ASSERT(isString());
return data.s.payload.str; return JSVAL_TO_STRING_IMPL(data);
} }
JSObject &asNonFunObj() const { JSObject &asNonFunObj() const {
JS_ASSERT(isNonFunObj()); JS_ASSERT(isNonFunObj());
return *data.s.payload.obj; return *JSVAL_TO_OBJECT_IMPL(data);
} }
JSObject &asFunObj() const { JSObject &asFunObj() const {
JS_ASSERT(isFunObj()); JS_ASSERT(isFunObj());
return *data.s.payload.obj; return *JSVAL_TO_OBJECT_IMPL(data);
} }
JSObject &asObject() const { JSObject &asObject() const {
JS_ASSERT(isObject()); JS_ASSERT(isObject());
return *data.s.payload.obj; return *JSVAL_TO_OBJECT_IMPL(data);
} }
JSObject *asObjectOrNull() const { JSObject *asObjectOrNull() const {
JS_ASSERT(isObjectOrNull()); JS_ASSERT(isObjectOrNull());
return data.s.payload.obj; return JSVAL_TO_OBJECT_IMPL(data);
} }
void *asGCThing() const { void *asGCThing() const {
JS_ASSERT(isGCThing()); JS_ASSERT(isGCThing());
return data.s.payload.ptr; return JSVAL_TO_GCTHING_IMPL(data);
} }
bool asBoolean() const { bool asBoolean() const {
@ -3321,6 +3284,11 @@ class Value
return data.s.payload.boo; return data.s.payload.boo;
} }
uint32 asRawUint32() const {
JS_ASSERT(!isDouble());
return data.s.payload.u32;
}
/* Swap two Values */ /* Swap two Values */
void swap(Value &rhs) { void swap(Value &rhs) {

View File

@ -385,8 +385,7 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags)
namespace js { namespace js {
static const uint32 FAKE_NUMBER_MASK = JSVAL_MASK32_INT32 | static const uint32 FAKE_NUMBER_MASK = JSVAL_MASK32_INT32 | PrimitiveValue::DOUBLE_MASK;
PrimitiveValue::DOUBLE_MASK;
const uint32 PrimitiveValue::Masks[PrimitiveValue::THISP_ARRAY_SIZE] = { const uint32 PrimitiveValue::Masks[PrimitiveValue::THISP_ARRAY_SIZE] = {
0, /* 000 */ 0, /* 000 */
@ -952,36 +951,29 @@ EqualObjects(JSContext *cx, JSObject *lobj, JSObject *robj)
} }
bool 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; Value lval = lref, rval = rref;
uint32 rmask = rval.data.s.mask32; if (SamePrimitiveTypeOrBothObjects(lval, rval)) {
if (lmask == rmask) { if (lval.isString())
if (lmask == JSVAL_MASK32_STRING) return js_EqualStrings(lval.asString(), rval.asString());
return js_EqualStrings(lval.data.s.payload.str, rval.data.s.payload.str); if (lval.isDouble())
if (Value::isObjectMask(lmask)) return JSDOUBLE_COMPARE(lval.asDouble(), ==, rval.asDouble(), JS_FALSE);
return EqualObjects(cx, lval.data.s.payload.obj, rval.data.s.payload.obj); if (lval.isObject())
if (Value::isDoubleMask(lmask)) return EqualObjects(cx, &lval.asObject(), &rval.asObject());
return JSDOUBLE_COMPARE(lval.data.asDouble, ==, rval.data.asDouble, JS_FALSE); return lval.asRawUint32() == rval.asRawUint32();
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;
} }
if (Value::isNumberMask(lmask) && Value::isNumberMask(rmask)) { if (lval.isDouble() && rval.isInt32()) {
double ld = lmask == JSVAL_MASK32_INT32 ? lval.data.s.payload.i32 double ld = lval.asDouble();
: lval.data.asDouble; double rd = rval.asInt32();
double rd = rmask == JSVAL_MASK32_INT32 ? rval.data.s.payload.i32 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
: rval.data.asDouble; }
if (lval.isInt32() && rval.isDouble()) {
double ld = lval.asInt32();
double rd = rval.asDouble();
return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); 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; return false;
} }
@ -1787,6 +1779,15 @@ namespace reprmeter {
VALUE_TO_OBJECT(cx, vp_, obj); \ VALUE_TO_OBJECT(cx, vp_, obj); \
JS_END_MACRO 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, &regs.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] */ /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
static JS_ALWAYS_INLINE bool static JS_ALWAYS_INLINE bool
CanIncDecWithoutOverflow(int32_t i) CanIncDecWithoutOverflow(int32_t i)

View File

@ -283,7 +283,7 @@ class PrimitiveValue
static const uint32 Masks[THISP_ARRAY_SIZE]; static const uint32 Masks[THISP_ARRAY_SIZE];
public: public:
static const uint32 DOUBLE_MASK = 0x8000; static const uint32 DOUBLE_MASK = 0xFFFF8000;
static bool test(JSFunction *fun, const Value &v) { static bool test(JSFunction *fun, const Value &v) {
uint32 mask = Masks[(fun->flags >> THISP_SHIFT) & THISP_MASK]; uint32 mask = Masks[(fun->flags >> THISP_SHIFT) & THISP_MASK];

View File

@ -816,9 +816,9 @@ END_CASE(JSOP_BITAND)
*/ */
#if JS_HAS_XML_SUPPORT #if JS_HAS_XML_SUPPORT
#define XML_EQUALITY_OP(OP) \ #define XML_EQUALITY_OP(OP) \
if ((lmask == JSVAL_MASK32_NONFUNOBJ && lref.asObject().isXML()) || \ if ((lval.isNonFunObj() && lval.asObject().isXML()) || \
(rmask == JSVAL_MASK32_NONFUNOBJ && rref.asObject().isXML())) { \ (rval.isNonFunObj() && rval.asObject().isXML())) { \
if (!js_TestXMLEquality(cx, lref, rref, &cond)) \ if (!js_TestXMLEquality(cx, lval, rval, &cond)) \
goto error; \ goto error; \
cond = cond OP JS_TRUE; \ cond = cond OP JS_TRUE; \
} else } else
@ -826,7 +826,7 @@ END_CASE(JSOP_BITAND)
#define EXTENDED_EQUALITY_OP(OP) \ #define EXTENDED_EQUALITY_OP(OP) \
if (((clasp = l->getClass())->flags & JSCLASS_IS_EXTENDED) && \ if (((clasp = l->getClass())->flags & JSCLASS_IS_EXTENDED) && \
((ExtendedClass *)clasp)->equality) { \ ((ExtendedClass *)clasp)->equality) { \
if (!((ExtendedClass *)clasp)->equality(cx, l, &rref, &cond)) \ if (!((ExtendedClass *)clasp)->equality(cx, l, &lval, &cond)) \
goto error; \ goto error; \
cond = cond OP JS_TRUE; \ cond = cond OP JS_TRUE; \
} else } else
@ -837,57 +837,42 @@ END_CASE(JSOP_BITAND)
#define EQUALITY_OP(OP, IFNAN) \ #define EQUALITY_OP(OP, IFNAN) \
JS_BEGIN_MACRO \ JS_BEGIN_MACRO \
/* Depends on the value representation. */ \
Class *clasp; \ Class *clasp; \
JSBool cond; \ JSBool cond; \
Value &rref = regs.sp[-1]; \ Value rval = regs.sp[-1]; \
Value &lref = regs.sp[-2]; \ Value lval = regs.sp[-2]; \
uint32 rmask = rref.data.s.mask32; \
uint32 lmask = lref.data.s.mask32; \
XML_EQUALITY_OP(OP) \ XML_EQUALITY_OP(OP) \
if (lmask == rmask || \ if (SamePrimitiveTypeOrBothObjects(lval, rval)) { \
(Value::isObjectMask(lmask) && Value::isObjectMask(rmask))) { \ if (lval.isString()) { \
if (lmask == JSVAL_MASK32_STRING) { \ JSString *l = lval.asString(), *r = rval.asString(); \
JSString *l = lref.asString(), *r = rref.asString(); \
cond = js_EqualStrings(l, r) OP JS_TRUE; \ cond = js_EqualStrings(l, r) OP JS_TRUE; \
} else if (Value::isObjectMask(lmask)) { \ } else if (lval.isDouble()) { \
JSObject *l = &lref.asObject(), *r = &rref.asObject(); \ 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) \ EXTENDED_EQUALITY_OP(OP) \
cond = l OP r; \ 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 { \ } 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 { \ } else { \
if (Value::isNullOrUndefinedMask(lmask)) { \ if (lval.isNullOrUndefined()) { \
cond = Value::isNullOrUndefinedMask(rmask) OP true; \ cond = rval.isNullOrUndefined() OP true; \
} else if (Value::isNullOrUndefinedMask(rmask)) { \ } else if (rval.isNullOrUndefined()) { \
cond = true OP false; \ cond = true OP false; \
} else { \ } else { \
if (Value::isObjectMask(lmask)) { \ if (lval.isObject()) \
JSObject &obj = lref.asObject(); \ DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \
if (!obj.defaultValue(cx, JSTYPE_VOID, &lref)) \ if (rval.isObject()) \
goto error; \ DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \
lmask = lref.data.s.mask32; \ if (BothString(lval, rval)) { \
} \ JSString *l = lval.asString(), *r = rval.asString(); \
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(); \
cond = js_EqualStrings(l, r) OP JS_TRUE; \ cond = js_EqualStrings(l, r) OP JS_TRUE; \
} else { \ } else { \
double l, r; \ double l, r; \
if (!ValueToNumber(cx, lref, &l) || \ if (!ValueToNumber(cx, lval, &l) || \
!ValueToNumber(cx, rref, &r)) { \ !ValueToNumber(cx, rval, &r)) { \
goto error; \ goto error; \
} \ } \
cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \ cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \
@ -963,39 +948,24 @@ END_CASE(JSOP_CASEX)
#define RELATIONAL_OP(OP) \ #define RELATIONAL_OP(OP) \
JS_BEGIN_MACRO \ JS_BEGIN_MACRO \
/* Depends on the value representation */ \ Value rval = regs.sp[-1]; \
Value &rref = regs.sp[-1]; \ Value lval = regs.sp[-2]; \
Value &lref = regs.sp[-2]; \
uint32 rmask = rref.data.s.mask32; \
uint32 lmask = lref.data.s.mask32; \
uint32 maskand = lmask & rmask; \
bool cond; \ bool cond; \
/* Optimize for two int-tagged operands (typical loop control). */ \ /* Optimize for two int-tagged operands (typical loop control). */ \
if (maskand == JSVAL_MASK32_INT32) { \ if (BothInt32(lval, rval)) { \
cond = lref.asInt32() OP rref.asInt32(); \ cond = lval.asInt32() OP rval.asInt32(); \
} else { \ } else { \
if (Value::isObjectMask(lmask | rmask)) { \ if (lval.isObject()) \
if (Value::isObjectMask(lmask)) { \ DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
JSObject &obj = lref.asObject(); \ if (rval.isObject()) \
if (!obj.defaultValue(cx, JSTYPE_NUMBER, &lref)) \ DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
goto error; \ if (BothString(lval, rval)) { \
lmask = lref.data.s.mask32; \ JSString *l = lval.asString(), *r = rval.asString(); \
} \
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(); \
cond = js_CompareStrings(l, r) OP 0; \ cond = js_CompareStrings(l, r) OP 0; \
} else { \ } else { \
double l, r; \ double l, r; \
if (!ValueToNumber(cx, lref, &l) || \ if (!ValueToNumber(cx, lval, &l) || \
!ValueToNumber(cx, rref, &r)) { \ !ValueToNumber(cx, rval, &r)) { \
goto error; \ goto error; \
} \ } \
cond = JSDOUBLE_COMPARE(l, OP, r, false); \ cond = JSDOUBLE_COMPARE(l, OP, r, false); \
@ -1064,14 +1034,11 @@ END_CASE(JSOP_URSH)
BEGIN_CASE(JSOP_ADD) BEGIN_CASE(JSOP_ADD)
{ {
/* Depends on the value representation */ Value rval = regs.sp[-1];
Value &rref = regs.sp[-1]; Value lval = regs.sp[-2];
Value &lref = regs.sp[-2];
uint32 rmask = rref.data.s.mask32;
uint32 lmask = lref.data.s.mask32;
if ((lmask & rmask) == JSVAL_MASK32_INT32) { if (BothInt32(lval, rval)) {
int32_t l = lref.asInt32(), r = rref.asInt32(); int32_t l = lval.asInt32(), r = rval.asInt32();
int32_t sum = l + r; int32_t sum = l + r;
regs.sp--; regs.sp--;
if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000)))
@ -1080,50 +1047,47 @@ BEGIN_CASE(JSOP_ADD)
regs.sp[-1].setInt32(sum); regs.sp[-1].setInt32(sum);
} else } else
#if JS_HAS_XML_SUPPORT #if JS_HAS_XML_SUPPORT
if (lmask == JSVAL_MASK32_NONFUNOBJ && lref.asObject().isXML() && if (lval.isNonFunObj() && lval.asObject().isXML() &&
rmask == JSVAL_MASK32_NONFUNOBJ && rref.asObject().isXML()) { rval.isNonFunObj() && rval.asObject().isXML()) {
Value rval; Value rval;
if (!js_ConcatenateXML(cx, &lref.asObject(), &rref.asObject(), &rval)) if (!js_ConcatenateXML(cx, &lval.asObject(), &rval.asObject(), &rval))
goto error; goto error;
regs.sp--; regs.sp--;
regs.sp[-1] = rval; regs.sp[-1] = rval;
} else } else
#endif #endif
{ {
if (Value::isObjectMask(lmask)) { if (lval.isObject())
if (!lref.asObject().defaultValue(cx, JSTYPE_VOID, &lref)) DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);
goto error; if (rval.isObject())
lmask = lref.data.s.mask32; DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);
} bool lIsString, rIsString;
if (Value::isObjectMask(rmask)) { if ((lIsString = lval.isString()) | (rIsString = rval.isString())) {
if (!rref.asObject().defaultValue(cx, JSTYPE_VOID, &rref)) JSString *lstr, *rstr;
goto error; if (lIsString) {
rmask = rref.data.s.mask32; lstr = lval.asString();
}
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;
} else { } else {
str2 = rref.asString(); lstr = js_ValueToString(cx, lval);
str1 = js_ValueToString(cx, lref); if (!lstr)
if (!str1)
goto error; 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) if (!str)
goto error; goto error;
regs.sp--; regs.sp--;
regs.sp[-1].setString(str); regs.sp[-1].setString(str);
} else { } else {
double l, r; double l, r;
if (!ValueToNumber(cx, lref, &l) || !ValueToNumber(cx, rref, &r)) if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r))
goto error; goto error;
l += r; l += r;
regs.sp--; regs.sp--;
@ -2135,15 +2099,10 @@ END_CASE(JSOP_GETELEM)
BEGIN_CASE(JSOP_CALLELEM) BEGIN_CASE(JSOP_CALLELEM)
{ {
/* Depends on the value representation. */
/* Fetch the left part and resolve it to a non-null object. */ /* Fetch the left part and resolve it to a non-null object. */
JSObject *obj; JSObject *obj;
FETCH_OBJECT(cx, -2, 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. */ /* Fetch index and convert it to id suitable for use with obj. */
jsid id; jsid id;
FETCH_ELEMENT_ID(obj, -1, id); FETCH_ELEMENT_ID(obj, -1, id);
@ -2161,8 +2120,7 @@ BEGIN_CASE(JSOP_CALLELEM)
} else } else
#endif #endif
{ {
regs.sp[-1].data.s.mask32 = objmask; regs.sp[-1].setObject(*obj);
regs.sp[-1].data.s.payload.obj = obj;
} }
} }
END_CASE(JSOP_CALLELEM) END_CASE(JSOP_CALLELEM)

View File

@ -158,35 +158,12 @@ typedef struct JSONParser JSONParser;
*/ */
/* /*
* TODO: wrong, fix, explain more * TODO: explain boxing strategy
* 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().
*/ */
typedef enum JSValueMask16 typedef enum JSValueMask16
#if defined(_MSC_VER) && _MSC_VER >= 1400 #if defined(_MSC_VER)
: unsigned short : uint16
#endif #endif
{ {
JSVAL_MASK16_NULL = (uint16)0x0001, JSVAL_MASK16_NULL = (uint16)0x0001,
@ -203,6 +180,11 @@ typedef enum JSValueMask16
JSVAL_MASK16_OBJORNULL = JSVAL_MASK16_OBJECT | JSVAL_MASK16_NULL, JSVAL_MASK16_OBJORNULL = JSVAL_MASK16_OBJECT | JSVAL_MASK16_NULL,
JSVAL_MASK16_GCTHING = JSVAL_MASK16_OBJECT | JSVAL_MASK16_STRING, 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) JSVAL_NANBOX_PATTERN = ((uint16)0xFFFF)
} }
#if defined(__GNUC__) #if defined(__GNUC__)
@ -210,45 +192,36 @@ __attribute__((packed))
#endif #endif
JSValueMask16; JSValueMask16;
#define JSVAL_MASK32_CLEAR ((uint32)0xFFFF0000) typedef enum JSValueMask32
#if defined(_MSC_VER)
#define JSVAL_MASK32_NULL ((uint32)(JSVAL_MASK32_CLEAR | JSVAL_MASK16_NULL)) : uint32
#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;
#endif #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__ #ifdef __GNUC__
# define VALUE_ALIGNMENT __attribute__((aligned (8))) # define VALUE_ALIGNMENT __attribute__((aligned (8)))
@ -264,32 +237,376 @@ typedef union jsval_payload
# error "TODO: do something for compiler" # error "TODO: do something for compiler"
#endif #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) #if !defined(IS_LITTLE_ENDIAN)
# error "Need to fix up jsval_layout" # error "Unsupported configuration"
#endif #endif
// TODO: explain
typedef union jsval_layout typedef union jsval_layout
{ {
uint64 asBits; uint64 asBits;
struct { 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 { union {
struct { struct {
JSValueMask16 mask16; JSValueMask16 mask16;
uint16 nanBits; uint16 nanBits;
} tag; } tag;
uint32 mask32; JSValueMask32 mask32;
}; };
} s; } s;
double asDouble; double asDouble;
} jsval_layout; } jsval_layout;
typedef uint64 jsval; #if JS_BITS_PER_WORD == 32
/* These are engine-internal details, not part of the public API */ static JS_ALWAYS_INLINE JSBool
#define DOUBLE_AS_JSVAL(d) (*(jsval *)&(d)) JSVAL_IS_NULL_IMPL(jsval_layout l)
#define JSVAL_CONSTANT(mask32, payload) ((jsval)((((uint64)(mask32)) << 32) | (payload))) {
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) * Boxed word macros (private engine detail)