This commit is contained in:
rogerl%netscape.com 2002-09-16 00:09:58 +00:00
Родитель 2861980340
Коммит 3e265b5e0a
9 изменённых файлов: 289 добавлений и 123 удалений

Просмотреть файл

@ -42,6 +42,7 @@
#include <algorithm>
#include <assert.h>
#include <list>
#include "world.h"
#include "utilities.h"

Просмотреть файл

@ -37,6 +37,7 @@
#include <map>
#include <algorithm>
#include <list>
#include "reader.h"
#include "parser.h"

Просмотреть файл

@ -43,6 +43,7 @@
#include <algorithm>
#include <assert.h>
#include <list>
#include "world.h"
#include "utilities.h"
@ -363,6 +364,7 @@ namespace MetaData {
ASSERT(activationStackTop < (activationStack + MAX_ACTIVATION_STACK));
activationStackTop->bCon = bCon;
activationStackTop->pc = pc;
activationStackTop->topFrame = meta->env.getTopFrame();
activationStackTop++;
bCon = new_bCon;
@ -377,6 +379,9 @@ namespace MetaData {
bCon = activationStackTop->bCon;
pc = activationStackTop->pc;
while (meta->env.getTopFrame() != activationStackTop->topFrame)
meta->env.removeTopFrame();
}
void JS2Engine::mark()

Просмотреть файл

@ -103,6 +103,7 @@ enum JS2Op {
int getStackEffect(JS2Op op);
class Frame;
class JS2Object;
class JS2Metadata;
class BytecodeContainer;
@ -168,6 +169,7 @@ public:
struct ActivationFrame {
uint8 *pc;
BytecodeContainer *bCon;
Frame *topFrame;
};
void jsr(BytecodeContainer *bCon);
bool activationStackEmpty() { return (activationStackTop == activationStack); }

Просмотреть файл

@ -49,6 +49,7 @@
#include <map>
#include <algorithm>
#include <list>
#include "reader.h"
#include "parser.h"
@ -89,7 +90,11 @@ namespace MetaData {
/*
* Validate an individual statement 'p', including it's children
*/
void JS2Metadata::ValidateStmt(Context *cxt, Environment *env, StmtNode *p) {
void JS2Metadata::ValidateStmt(Context *cxt, Environment *env, StmtNode *p)
{
CompoundAttribute *a = NULL;
JS2Object::RootIterator ri = JS2Object::addRoot(&a);
switch (p->getKind()) {
case StmtNode::block:
case StmtNode::group:
@ -274,7 +279,7 @@ namespace MetaData {
ValidateAttributeExpression(cxt, env, f->attributes);
attr = EvalAttributeExpression(env, CompilePhase, f->attributes);
}
CompoundAttribute *a = Attribute::toCompoundAttribute(attr);
a = Attribute::toCompoundAttribute(attr);
if (a->dynamic)
reportError(Exception::definitionError, "Illegal attribute", p->pos);
VariableBinding *vb = f->function.parameters;
@ -323,17 +328,25 @@ namespace MetaData {
defineHoistedVar(env, *f->function.name, p);
}
else {
FixedInstance *fInst = new FixedInstance(functionClass);
fInst->fWrap = new FunctionWrapper(unchecked, compileFrame);
f->fWrap = fInst->fWrap;
switch (memberMod) {
case Attribute::NoModifier:
case Attribute::Static:
{
FixedInstance *fInst = new FixedInstance(functionClass);
fInst->fWrap = new FunctionWrapper(unchecked, compileFrame);
f->fWrap = fInst->fWrap;
Variable *v = new Variable(functionClass, OBJECT_TO_JS2VAL(fInst), true);
defineStaticMember(env, *f->function.name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, v, p->pos);
}
break;
case Attribute::Virtual:
case Attribute::Final:
{
JS2Class *c = checked_cast<JS2Class *>(env->getTopFrame());
InstanceMember *m = new InstanceMethod(fInst);
defineInstanceMember(c, cxt, *f->function.name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, m, p->pos);
}
break;
}
}
}
@ -366,7 +379,7 @@ namespace MetaData {
defineHoistedVar(env, *name, p);
}
else {
CompoundAttribute *a = Attribute::toCompoundAttribute(attr);
a = Attribute::toCompoundAttribute(attr);
if (a->dynamic || a->prototype)
reportError(Exception::definitionError, "Illegal attribute", p->pos);
Attribute::MemberModifier memberMod = a->memberMod;
@ -418,7 +431,7 @@ namespace MetaData {
ValidateAttributeExpression(cxt, env, ns->attributes);
attr = EvalAttributeExpression(env, CompilePhase, ns->attributes);
}
CompoundAttribute *a = Attribute::toCompoundAttribute(attr);
a = Attribute::toCompoundAttribute(attr);
if (a->dynamic || a->prototype)
reportError(Exception::definitionError, "Illegal attribute", p->pos);
if ( ! ((a->memberMod == Attribute::NoModifier) || ((a->memberMod == Attribute::Static) && (env->getTopFrame()->kind == ClassKind))) )
@ -462,7 +475,7 @@ namespace MetaData {
ValidateAttributeExpression(cxt, env, classStmt->attributes);
attr = EvalAttributeExpression(env, CompilePhase, classStmt->attributes);
}
CompoundAttribute *a = Attribute::toCompoundAttribute(attr);
a = Attribute::toCompoundAttribute(attr);
if (!superClass->complete || superClass->final)
reportError(Exception::definitionError, "Illegal inheritance", p->pos);
JS2Object *proto = NULL;
@ -496,6 +509,8 @@ namespace MetaData {
c->complete = true;
}
} // switch (p->getKind())
JS2Object::removeRoot(ri);
}
@ -678,7 +693,8 @@ namespace MetaData {
{
ExprStmtNode *e = checked_cast<ExprStmtNode *>(p);
if (e->expr) {
EvalExprNode(env, phase, e->expr);
Reference *r = EvalExprNode(env, phase, e->expr);
if (r) r->emitReadBytecode(bCon, p->pos);
bCon->emitOp(eReturn, p->pos);
}
}
@ -1974,6 +1990,9 @@ doBinary:
case PackageKind:
return packageClass;
case MethodClosureKind:
return functionClass;
case SystemKind:
case ParameterKind:
case BlockKind:
@ -2146,6 +2165,7 @@ readClassProperty:
case AttributeObjectKind:
case MultinameKind:
case FixedInstanceKind:
case MethodClosureKind:
goto readClassProperty;
case DynamicInstanceKind:
isDynamicInstance = true;
@ -2156,35 +2176,8 @@ readClassProperty:
case PackageKind:
case ParameterKind:
case BlockKind:
return readProperty(checked_cast<Frame *>(container), multiname, lookupKind, phase, rval);
case ClassKind:
{
// this:
// JS2VAL_UNINITIALIZED --> generic
// JS2VAL_NULL --> none
// JS2VAL_VOID --> inaccessible
//
js2val thisObject;
if (lookupKind->isPropertyLookup())
thisObject = JS2VAL_UNINITIALIZED;
else
thisObject = lookupKind->thisObject;
MemberDescriptor m2;
if (findStaticMember(checked_cast<JS2Class *>(container), multiname, ReadAccess, phase, &m2) && m2.staticMember)
return readStaticMember(m2.staticMember, phase, rval);
else {
if (JS2VAL_IS_NULL(thisObject))
reportError(Exception::propertyAccessError, "Null 'this' object", engine->errorPos());
if (JS2VAL_IS_VOID(thisObject))
reportError(Exception::compileExpressionError, "Undefined 'this' object", engine->errorPos());
if (JS2VAL_IS_UNINITIALIZED(thisObject)) {
// 'this' is {generic}
// XXX is ??? in spec.
}
return readInstanceMember(thisObject, objectType(thisObject), m2.qname, phase, rval);
}
}
return readProperty(checked_cast<Frame *>(container), multiname, lookupKind, phase, rval);
case PrototypeInstanceKind:
return readDynamicProperty(container, multiname, lookupKind, phase, rval);
@ -2228,6 +2221,12 @@ readClassProperty:
break;
case InstanceMember::InstanceMethodKind:
{
*rval = OBJECT_TO_JS2VAL(new MethodClosure(containerVal, checked_cast<InstanceMethod *>(m)));
return true;
}
break;
case InstanceMember::InstanceAccessorKind:
break;
}
@ -2239,11 +2238,36 @@ readClassProperty:
// the property or not. If it does, return it's value
bool JS2Metadata::readProperty(Frame *container, Multiname *multiname, LookupKind *lookupKind, Phase phase, js2val *rval)
{
StaticMember *m = findFlatMember(container, multiname, ReadAccess, phase);
if (!m && (container->kind == GlobalObjectKind))
return readDynamicProperty(container, multiname, lookupKind, phase, rval);
else
return readStaticMember(m, phase, rval);
if (container->kind != ClassKind) {
// Must be System, Global, Package, Parameter or Block
StaticMember *m = findFlatMember(container, multiname, ReadAccess, phase);
if (!m && (container->kind == GlobalObjectKind))
return readDynamicProperty(container, multiname, lookupKind, phase, rval);
else
return readStaticMember(m, phase, rval);
}
else {
// XXX using JS2VAL_UNINITIALIZED to signal generic 'this'
js2val thisObject;
if (lookupKind->isPropertyLookup())
thisObject = JS2VAL_UNINITIALIZED;
else
thisObject = lookupKind->thisObject;
MemberDescriptor m2;
if (findStaticMember(checked_cast<JS2Class *>(container), multiname, ReadAccess, phase, &m2) && m2.staticMember)
return readStaticMember(m2.staticMember, phase, rval);
else {
if (JS2VAL_IS_NULL(thisObject))
reportError(Exception::propertyAccessError, "Null 'this' object", engine->errorPos());
if (JS2VAL_IS_INACCESSIBLE(thisObject))
reportError(Exception::compileExpressionError, "Inaccesible 'this' object", engine->errorPos());
if (JS2VAL_IS_UNINITIALIZED(thisObject)) {
// 'this' is {generic}
// XXX is ??? in spec.
}
return readInstanceMember(thisObject, objectType(thisObject), m2.qname, phase, rval);
}
}
}
// Write the value of a property in the container. Return true/false if that container has
@ -2257,6 +2281,7 @@ readClassProperty:
switch (container->kind) {
case AttributeObjectKind:
case MultinameKind:
case MethodClosureKind:
return false;
case FixedInstanceKind:
@ -2279,36 +2304,8 @@ readClassProperty:
case PackageKind:
case ParameterKind:
case BlockKind:
return writeProperty(checked_cast<Frame *>(container), multiname, lookupKind, createIfMissing, newValue, phase);
case ClassKind:
{
// this:
// JS2VAL_UNINITIALIZED --> generic
// JS2VAL_NULL --> none
// JS2VAL_VOID --> inaccessible
//
js2val thisObject;
if (lookupKind->isPropertyLookup())
thisObject = JS2VAL_UNINITIALIZED;
else
thisObject = lookupKind->thisObject;
MemberDescriptor m2;
if (findStaticMember(checked_cast<JS2Class *>(container), multiname, WriteAccess, phase, &m2) && m2.staticMember)
return writeStaticMember(m2.staticMember, newValue, phase);
else {
if (JS2VAL_IS_NULL(thisObject))
reportError(Exception::propertyAccessError, "Null 'this' object", engine->errorPos());
if (JS2VAL_IS_VOID(thisObject))
reportError(Exception::compileExpressionError, "Undefined 'this' object", engine->errorPos());
if (JS2VAL_IS_UNINITIALIZED(thisObject)) {
// 'this' is {generic}
// XXX is ??? in spec.
}
return writeInstanceMember(thisObject, objectType(thisObject), m2.qname, newValue, phase);
}
}
break;
return writeProperty(checked_cast<Frame *>(container), multiname, lookupKind, createIfMissing, newValue, phase);
case PrototypeInstanceKind:
return writeDynamicProperty(container, multiname, createIfMissing, newValue, phase);
@ -2351,11 +2348,36 @@ readClassProperty:
// the property or not.
bool JS2Metadata::writeProperty(Frame *container, Multiname *multiname, LookupKind *lookupKind, bool createIfMissing, js2val newValue, Phase phase)
{
StaticMember *m = findFlatMember(container, multiname, WriteAccess, phase);
if (!m && (container->kind == GlobalObjectKind))
return writeDynamicProperty(container, multiname, createIfMissing, newValue, phase);
else
return writeStaticMember(m, newValue, phase);
if (container->kind != ClassKind) {
// Must be System, Global, Package, Parameter or Block
StaticMember *m = findFlatMember(container, multiname, WriteAccess, phase);
if (!m && (container->kind == GlobalObjectKind))
return writeDynamicProperty(container, multiname, createIfMissing, newValue, phase);
else
return writeStaticMember(m, newValue, phase);
}
else {
// XXX using JS2VAL_UNINITIALIZED to signal generic 'this'
js2val thisObject;
if (lookupKind->isPropertyLookup())
thisObject = JS2VAL_UNINITIALIZED;
else
thisObject = lookupKind->thisObject;
MemberDescriptor m2;
if (findStaticMember(checked_cast<JS2Class *>(container), multiname, WriteAccess, phase, &m2) && m2.staticMember)
return writeStaticMember(m2.staticMember, newValue, phase);
else {
if (JS2VAL_IS_NULL(thisObject))
reportError(Exception::propertyAccessError, "Null 'this' object", engine->errorPos());
if (JS2VAL_IS_VOID(thisObject))
reportError(Exception::compileExpressionError, "Undefined 'this' object", engine->errorPos());
if (JS2VAL_IS_UNINITIALIZED(thisObject)) {
// 'this' is {generic}
// XXX is ??? in spec.
}
return writeInstanceMember(thisObject, objectType(thisObject), m2.qname, newValue, phase);
}
}
}
// Find a binding in the frame that matches the multiname and access
@ -2441,7 +2463,7 @@ readClassProperty:
else
iFound = ib->second;
}
b++;
ib++;
}
if (iFound) {
result->staticMember = NULL;
@ -2620,7 +2642,7 @@ readClassProperty:
p->owner = (Pond *)freeHeader;
uint8 *t = (uint8 *)(p + 1);
#ifdef DEBUG
memset(t, 0xB7, p->getSize() - sizeof(PondScum));
memset(t, 0xB3, p->getSize() - sizeof(PondScum));
#endif
freeHeader = p;
}
@ -2683,6 +2705,13 @@ readClassProperty:
GCMARKOBJECT(super)
GCMARKOBJECT(prototype)
GCMARKOBJECT(privateNamespace)
InstanceBindingIterator ib, iend;
for (ib = instanceReadBindings.begin(), iend = instanceReadBindings.end(); (ib != iend); ib++) {
GCMARKOBJECT(ib->second->content)
}
for (ib = instanceWriteBindings.begin(), iend = instanceWriteBindings.end(); (ib != iend); ib++) {
GCMARKOBJECT(ib->second->content)
}
}
/************************************************************************************
@ -2738,6 +2767,7 @@ readClassProperty:
FixedInstance::FixedInstance(JS2Class *type)
: JS2Object(FixedInstanceKind),
type(type),
fWrap(NULL),
call(NULL),
construct(NULL),
env(NULL),
@ -2752,6 +2782,10 @@ readClassProperty:
void FixedInstance::markChildren()
{
GCMARKOBJECT(type)
if (fWrap) {
GCMARKOBJECT(fWrap->compileFrame);
fWrap->bCon->mark();
}
if (slots) {
ASSERT(type);
for (uint32 i = 0; (i < type->slotCount); i++) {
@ -2782,6 +2816,22 @@ readClassProperty:
}
}
/************************************************************************************
*
* Frame
*
************************************************************************************/
void MethodClosure::markChildren()
{
if (JS2VAL_IS_OBJECT(thisObject)) {
JS2Object *obj = JS2VAL_TO_OBJECT(thisObject);
GCMARKOBJECT(obj)
}
GCMARKOBJECT(method->fInst)
}
/************************************************************************************
*
* Frame
@ -2855,6 +2905,77 @@ readClassProperty:
}
/************************************************************************************
*
* InstanceMember
*
************************************************************************************/
bool InstanceMember::isMarked()
{
return type->isMarked();
}
void InstanceMember::mark()
{
type->mark();
}
void InstanceMember::markChildren()
{
type->markChildren();
}
/************************************************************************************
*
* InstanceVariable
*
************************************************************************************/
bool InstanceVariable::isMarked()
{
if (type != FUTURE_TYPE)
return type->isMarked();
else
return false;
}
void InstanceVariable::mark()
{
if (type != FUTURE_TYPE)
type->mark();
}
void InstanceVariable::markChildren()
{
if (type != FUTURE_TYPE)
type->markChildren();
}
/************************************************************************************
*
* InstanceMethod
*
************************************************************************************/
bool InstanceMethod::isMarked()
{
return fInst->isMarked();
}
void InstanceMethod::mark()
{
fInst->mark();
}
void InstanceMethod::markChildren()
{
fInst->markChildren();
}
/************************************************************************************
*
* JS2Object
@ -2862,27 +2983,33 @@ readClassProperty:
************************************************************************************/
Pond JS2Object::pond(POND_SIZE, NULL);
std::vector<PondScum **> JS2Object::rootList;
std::list<PondScum **> JS2Object::rootList;
void JS2Object::addRoot(void *t)
JS2Object::RootIterator JS2Object::addRoot(void *t)
{
PondScum **p = (PondScum **)t;
ASSERT(p);
rootList.push_back(p);
return rootList.insert(rootList.end(), p);
}
void JS2Object::removeRoot(RootIterator ri)
{
rootList.erase(ri);
}
void JS2Object::gc(JS2Metadata *meta)
{
pond.resetMarks();
for (std::vector<PondScum **>::iterator i = rootList.begin(), end = rootList.end(); (i != end); i++) {
PondScum *p = **i;
if (p) {
for (std::list<PondScum **>::iterator i = rootList.begin(), end = rootList.end(); (i != end); i++) {
if (**i) {
PondScum *p = (**i) - 1;
ASSERT(p->owner && (p->getSize() >= sizeof(PondScum)) && (p->owner->sanity == POND_SANITY));
p->mark();
if (p->isJS2Object()) {
JS2Object *obj = (JS2Object *)(p + 1);
GCMARKOBJECT(obj)
}
else
p->mark();
}
}
meta->mark();

Просмотреть файл

@ -50,6 +50,7 @@ class Context;
class CompoundAttribute;
class BytecodeContainer;
class Pond;
class FixedInstance;
typedef void (Invokable)();
@ -67,7 +68,8 @@ enum ObjectKind {
PrototypeInstanceKind,
FixedInstanceKind,
DynamicInstanceKind,
MultinameKind
MultinameKind,
MethodClosureKind
};
class PondScum {
@ -126,9 +128,12 @@ public:
ObjectKind kind;
static Pond pond;
static std::vector<PondScum **> rootList;
static std::list<PondScum **> rootList;
typedef std::list<PondScum **>::iterator RootIterator;
static void gc(JS2Metadata *meta);
static void addRoot(void *t); // pass the address of any JS2Object pointer
static RootIterator addRoot(void *t); // pass the address of any JS2Object pointer
static void removeRoot(RootIterator ri);
static void *alloc(size_t s, bool isJS2Object = false);
static void unalloc(void *p);
@ -243,9 +248,9 @@ public:
MemberKind kind;
#ifdef DEBUG
virtual void uselessVirtual() { } // want the checked_cast stuff to work, so need a virtual function
#endif
virtual bool isMarked() { return true; }
virtual void mark() { }
virtual void markChildren() { }
};
// A static member is either forbidden, a variable, a hoisted variable, a constructor method, or an accessor:
@ -258,9 +263,6 @@ public:
// bindings that refer to this member.)
virtual StaticMember *clone() { ASSERT(false); return NULL; }
virtual bool isMarked() { return true; }
virtual void mark() { }
virtual void markChildren() { }
};
#define FUTURE_TYPE ((JS2Class *)(-1))
@ -347,9 +349,9 @@ public:
JS2Class *type; // Type of values that may be stored in this variable
bool final; // true if this member may not be overridden in subclasses
#ifdef DEBUG
virtual void uselessVirtual() { } // want the checked_cast stuff to work, so need a virtual function
#endif
virtual bool isMarked();
virtual void mark();
virtual void markChildren();
};
class InstanceVariable : public InstanceMember {
@ -358,13 +360,22 @@ public:
Invokable *evalInitialValue; // A function that computes this variable's initial value
bool immutable; // true if this variable's value may not be changed once set
uint32 slotIndex; // The index into an instance's slot array in which this variable is stored
virtual bool isMarked();
virtual void mark();
virtual void markChildren();
};
class InstanceMethod : public InstanceMember {
public:
InstanceMethod() : InstanceMember(InstanceMethodKind, NULL, false) { }
InstanceMethod(FixedInstance *fInst) : InstanceMember(InstanceMethodKind, NULL, false), fInst(fInst) { }
Signature type; // This method's signature
Invokable *code; // This method itself (a callable object); null if this method is abstract
FixedInstance *fInst;
virtual bool isMarked();
virtual void mark();
virtual void markChildren();
};
class InstanceAccessor : public InstanceMember {
@ -523,7 +534,15 @@ public:
virtual void markChildren();
};
// A METHODCLOSURE tuple describes an instance method with a bound this value.
class MethodClosure : public JS2Object {
public:
MethodClosure(js2val thisObject, InstanceMethod *method) : JS2Object(MethodClosureKind), thisObject(thisObject), method(method) { }
js2val thisObject; // The bound this value
InstanceMethod *method; // The bound method
virtual void markChildren();
};
// Base class for all references (lvalues)
@ -641,7 +660,7 @@ public:
class ParameterFrame : public Frame {
public:
ParameterFrame(js2val thisObject, bool prototype) : Frame(ParameterKind), thisObject(thisObject), prototype(prototype) { }
ParameterFrame(ParameterFrame *pluralFrame) : Frame(ParameterKind, pluralFrame) { }
ParameterFrame(ParameterFrame *pluralFrame) : Frame(ParameterKind, pluralFrame), thisObject(JS2VAL_UNDEFINED), prototype(pluralFrame->prototype) { }
Plurality plurality;
js2val thisObject; // The value of this; none if this function doesn't define this;

Просмотреть файл

@ -104,22 +104,6 @@
}
break;
case ePushFrame:
{
Frame *f = bCon->mFrameList[BytecodeContainer::getShort(pc)];
pc += sizeof(short);
meta->env.addFrame(f);
f->instantiate(&meta->env);
}
break;
case ePopFrame:
{
meta->env.removeTopFrame();
}
break;
// Read an index property from a base object, push the value onto the stack
case eBracketRead:
{

Просмотреть файл

@ -97,16 +97,27 @@
runtimeThis = OBJECT_TO_JS2VAL(g);
}
}
Frame *runtimeFrame = new ParameterFrame(fWrap->compileFrame);
meta->env.addFrame(runtimeFrame);
ParameterFrame *runtimeFrame = new ParameterFrame(fWrap->compileFrame);
runtimeFrame->instantiate(&meta->env);
runtimeFrame->thisObject = runtimeThis;
// assignArguments(runtimeFrame, fWrap->compileFrame->signature);
jsr(fWrap->bCon);
jsr(fWrap->bCon); // seems out of order, but we need to catch the current top frame
meta->env.addFrame(runtimeFrame);
}
else
ASSERT(false);
if (fObj->kind == MethodClosureKind) {
MethodClosure *mc = checked_cast<MethodClosure *>(fObj);
FixedInstance *fInst = mc->method->fInst;
FunctionWrapper *fWrap = fInst->fWrap;
ParameterFrame *runtimeFrame = new ParameterFrame(fWrap->compileFrame);
runtimeFrame->instantiate(&meta->env);
runtimeFrame->thisObject = mc->thisObject;
// assignArguments(runtimeFrame, fWrap->compileFrame->signature);
jsr(fWrap->bCon); // seems out of order, but we need to catch the current top frame
meta->env.addFrame(meta->objectType(mc->thisObject));
meta->env.addFrame(runtimeFrame);
}
}
break;
@ -118,9 +129,9 @@
case eReturn:
{
retval = pop();
// retval = pop();
if (activationStackEmpty())
return retval;
return pop();
else
rts();
}
@ -135,3 +146,18 @@
}
break;
case ePushFrame:
{
Frame *f = bCon->mFrameList[BytecodeContainer::getShort(pc)];
pc += sizeof(short);
meta->env.addFrame(f);
f->instantiate(&meta->env);
}
break;
case ePopFrame:
{
meta->env.removeTopFrame();
}
break;

Просмотреть файл

@ -60,6 +60,7 @@
that a variable has to have it's initializer run */
#define JS2VAL_IS_FUTURE(v) (v == JS2VAL_FUTUREVALUE)
#define JS2VAL_IS_SPECIALREF(v) ( (v & ~0xF0) == 0 )
/* Type tag bitfield length and derived macros. */
#define JS2VAL_TAGBITS 3
@ -81,7 +82,7 @@
#define JS2VAL_TRUE BOOLEAN_TO_JS2VAL(true)
/* Predicates for type testing. */
#define JS2VAL_IS_OBJECT(v) (JS2VAL_TAG(v) == JS2VAL_OBJECT)
#define JS2VAL_IS_OBJECT(v) ((JS2VAL_TAG(v) == JS2VAL_OBJECT) && !JS2VAL_IS_SPECIALREF(v))
#define JS2VAL_IS_NUMBER(v) (JS2VAL_IS_INT(v) || JS2VAL_IS_DOUBLE(v))
#define JS2VAL_IS_INT(v) (((v) & JS2VAL_INT) && (v) != JS2VAL_VOID)
#define JS2VAL_IS_DOUBLE(v) (JS2VAL_TAG(v) == JS2VAL_DOUBLE)