GC fixes, added debug info to roots.

This commit is contained in:
rogerl%netscape.com 2003-03-31 21:38:32 +00:00
Родитель 0ce213e52c
Коммит 41420ce62b
11 изменённых файлов: 119 добавлений и 64 удалений

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

@ -320,7 +320,7 @@ js2val load(JS2Metadata *meta, const js2val /* thisValue */, js2val argv[], uint
if (argc) {
// Save off the current top frame and root it.
Environment *curEnv = meta->env;
RootKeeper rk(&curEnv);
DEFINE_ROOTKEEPER(rk, curEnv);
// Set the environment to global object and system frame so that the
// load happens into the top frame.
meta->env = new Environment(curEnv->getSystemFrame(), curEnv->getPackageFrame());
@ -354,7 +354,7 @@ int main(int argc, char **argv)
stdOut << "Welcome to Epimetheus.\n";
#endif
RootKeeper rk(&metadata);
DEFINE_ROOTKEEPER(rk, metadata);
metadata = new MetaData::JS2Metadata(world);

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

@ -83,7 +83,7 @@ js2val setLength(JS2Metadata *meta, JS2Object *obj, uint32 newLength)
}
}
Multiname *mn = new Multiname(meta->engine->length_StringAtom, meta->publicNamespace);
RootKeeper rk(&mn);
DEFINE_ROOTKEEPER(rk, mn);
LookupKind lookup(false, JS2VAL_NULL);
defaultWriteProperty(meta, OBJECT_TO_JS2VAL(obj), meta->arrayClass, mn, &lookup, true, result);
}
@ -98,7 +98,7 @@ js2val Array_Constructor(JS2Metadata *meta, const js2val /*thisValue*/, js2val *
{
js2val thatValue = OBJECT_TO_JS2VAL(new ArrayInstance(meta, meta->arrayClass->prototype, meta->arrayClass));
ArrayInstance *arrInst = checked_cast<ArrayInstance *>(JS2VAL_TO_OBJECT(thatValue));
RootKeeper rk(&arrInst);
DEFINE_ROOTKEEPER(rk, arrInst);
if (argc > 0) {
if (argc == 1) {
if (JS2VAL_IS_NUMBER(argv[0])) {
@ -290,8 +290,8 @@ static js2val Array_reverse(JS2Metadata *meta, const js2val thisValue, js2val *
// XXX Need to root the Strings somewhere, this'll do for now..
Multiname *mn1 = new Multiname(meta->publicNamespace);
Multiname *mn2 = new Multiname(meta->publicNamespace);
RootKeeper rk1(&mn1);
RootKeeper rk2(&mn2);
DEFINE_ROOTKEEPER(rk1, mn1);
DEFINE_ROOTKEEPER(rk2, mn2);
for (uint32 k = 0; k < halfway; k++) {
bool deleteResult;
@ -345,8 +345,8 @@ static js2val Array_shift(JS2Metadata *meta, const js2val thisValue, js2val * /*
// XXX Need to root the Strings somewhere, this'll do for now..
Multiname *mn1 = new Multiname(meta->publicNamespace);
Multiname *mn2 = new Multiname(meta->publicNamespace);
RootKeeper rk1(&mn1);
RootKeeper rk2(&mn2);
DEFINE_ROOTKEEPER(rk1, mn1);
DEFINE_ROOTKEEPER(rk2, mn2);
js2val result;
bool deleteResult;
@ -425,8 +425,8 @@ static js2val Array_slice(JS2Metadata *meta, const js2val thisValue, js2val *arg
// XXX Need to root the Strings somewhere, this'll do for now..
Multiname *mn1 = new Multiname(meta->publicNamespace);
Multiname *mn2 = new Multiname(meta->publicNamespace);
RootKeeper rk1(&mn1);
RootKeeper rk2(&mn2);
DEFINE_ROOTKEEPER(rk1, mn1);
DEFINE_ROOTKEEPER(rk2, mn2);
uint32 n = 0;
while (start < end) {
mn1->name = meta->engine->numberToString(start);
@ -584,7 +584,7 @@ static js2val Array_sort(JS2Metadata *meta, const js2val thisValue, js2val *argv
JS2Class *c = meta->objectType(thisObj);
// XXX Need to root the Strings somewhere, this'll do for now..
Multiname *mn1 = new Multiname(meta->publicNamespace);
RootKeeper rk1(&mn1);
DEFINE_ROOTKEEPER(rk1, mn1);
for (i = 0; i < length; i++) {
mn1->name = meta->engine->numberToString(i);
c->readPublic(meta, &thatValue, c, mn1->name, RunPhase, &vec[i]);
@ -643,8 +643,8 @@ static js2val Array_splice(JS2Metadata *meta, const js2val thisValue, js2val *ar
// XXX Need to root the Strings somewhere, this'll do for now..
Multiname *mn1 = new Multiname(meta->publicNamespace);
Multiname *mn2 = new Multiname(meta->publicNamespace);
RootKeeper rk1(&mn1);
RootKeeper rk2(&mn2);
DEFINE_ROOTKEEPER(rk1, mn1);
DEFINE_ROOTKEEPER(rk2, mn2);
for (k = 0; k < deleteCount; k++) {
mn1->name = meta->engine->numberToString(start + k);
@ -682,7 +682,7 @@ static js2val Array_splice(JS2Metadata *meta, const js2val thisValue, js2val *ar
bool deleteResult;
mn1->name = meta->engine->numberToString(k + deleteCount - 1);
mn2->name = meta->engine->numberToString(k + newItemCount - 1);
if (meta->hasOwnProperty(thisObj, meta->mn1->name)) {
if (meta->hasOwnProperty(thisObj, mn1->name)) {
js2val rval;
c->readPublic(meta, &thatValue, c, mn1->name, RunPhase, &rval);
meta->arrayClass->writePublic(meta, result, meta->arrayClass, mn2->name, true, rval);
@ -716,8 +716,8 @@ static js2val Array_unshift(JS2Metadata *meta, const js2val thisValue, js2val *a
// XXX Need to root the Strings somewhere, this'll do for now..
Multiname *mn1 = new Multiname(meta->publicNamespace);
Multiname *mn2 = new Multiname(meta->publicNamespace);
RootKeeper rk1(&mn1);
RootKeeper rk2(&mn2);
DEFINE_ROOTKEEPER(rk1, mn1);
DEFINE_ROOTKEEPER(rk2, mn2);
for (k = length; k > 0; k--) {
bool deleteResult;

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

@ -59,7 +59,7 @@ namespace MetaData {
{
js2val thatValue = OBJECT_TO_JS2VAL(new BooleanInstance(meta, meta->booleanClass->prototype, meta->booleanClass));
BooleanInstance *boolInst = checked_cast<BooleanInstance *>(JS2VAL_TO_OBJECT(thatValue));
RootKeeper rk(&boolInst);
DEFINE_ROOTKEEPER(rk, boolInst);
if (argc > 0)
boolInst->mValue = meta->toBoolean(argv[0]);

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

@ -871,7 +871,7 @@ js2val Date_Constructor(JS2Metadata *meta, const js2val /* thisValue */, js2val
{
js2val thatValue = OBJECT_TO_JS2VAL(new DateInstance(meta, meta->dateClass->prototype, meta->dateClass));
DateInstance *thisInst = checked_cast<DateInstance *>(JS2VAL_TO_OBJECT(thatValue));
RootKeeper rk(&thisInst);
DEFINE_ROOTKEEPER(rk, thisInst);
/* Date called as constructor */
if (argc == 0) {

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

@ -267,7 +267,7 @@ namespace MetaData {
CompilationData *oldData = startCompilationUnit(bCon, bCon->mSource, bCon->mSourceLocation);
ParameterFrame *runtimeFrame = new ParameterFrame(fWrap->compileFrame);
RootKeeper rk(&runtimeFrame);
DEFINE_ROOTKEEPER(rk, runtimeFrame);
runtimeFrame->instantiate(env);
runtimeFrame->thisObject = thisValue;
runtimeFrame->assignArguments(this, fnObj, argv, argc);
@ -591,7 +591,7 @@ namespace MetaData {
// XXX could speed up by pushing knowledge of single namespace?
LookupKind lookup(false, JS2VAL_NULL);
Multiname *mn = new Multiname(name, meta->publicNamespace);
RootKeeper rk(&mn);
DEFINE_ROOTKEEPER(rk, mn);
return defaultReadProperty(meta, base, limit, mn, &lookup, phase, rval);
}
@ -600,7 +600,7 @@ namespace MetaData {
// XXX could speed up by pushing knowledge of single namespace & lookup?
LookupKind lookup(false, JS2VAL_NULL);
Multiname *mn = new Multiname(name, meta->publicNamespace);
RootKeeper rk(&mn);
DEFINE_ROOTKEEPER(rk, mn);
return defaultDeleteProperty(meta, base, limit, mn, &lookup, result);
}
@ -609,7 +609,7 @@ namespace MetaData {
// XXX could speed up by pushing knowledge of single namespace?
LookupKind lookup(false, JS2VAL_NULL);
Multiname *mn = new Multiname(name, meta->publicNamespace);
RootKeeper rk(&mn);
DEFINE_ROOTKEEPER(rk, mn);
return defaultWriteProperty(meta, base, limit, mn, &lookup, createIfMissing, newValue);
}
@ -666,7 +666,7 @@ namespace MetaData {
|| ( (JS2VAL_TO_OBJECT(base)->kind == PackageKind) && !checked_cast<Package *>(JS2VAL_TO_OBJECT(base))->sealed)) ) {
QualifiedName qName = multiname->selectPrimaryName(meta);
Multiname *mn = new Multiname(qName);
RootKeeper rk(&mn);
DEFINE_ROOTKEEPER(rk, mn);
if ( (meta->findBaseInstanceMember(limit, mn, ReadAccess) == NULL)
&& (meta->findCommonMember(&base, mn, ReadAccess, true) == NULL) ) {
meta->createDynamicProperty(JS2VAL_TO_OBJECT(base), &qName, newValue, ReadWriteAccess, false, true);

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

@ -77,7 +77,7 @@ namespace MetaData {
ASSERT(parser.lexer.peek(true).hasKind(Token::end));
ASSERT(fnExpr); // otherwise, an exception would have been thrown out of here
fnExpr->obj = NULL;
RootKeeper rk(&fnExpr->obj);
DEFINE_ROOTKEEPER(rk, fnExpr->obj);
JS2Class *exprType;
meta->ValidateExpression(&meta->cxt, meta->env, fnExpr);
meta->SetupExprNode(meta->env, RunPhase, fnExpr, &exprType);

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

@ -88,10 +88,10 @@ namespace MetaData {
FunctionInstance *JS2Metadata::validateStaticFunction(FunctionDefinition *fnDef, js2val compileThis, bool prototype, bool unchecked, Context *cxt, Environment *env)
{
ParameterFrame *compileFrame = new ParameterFrame(compileThis, prototype);
RootKeeper rk1(&compileFrame);
DEFINE_ROOTKEEPER(rk1, compileFrame);
FunctionInstance *result = new FunctionInstance(this, functionClass->prototype, functionClass);
RootKeeper rk2(&result);
DEFINE_ROOTKEEPER(rk2, result);
result->fWrap = new FunctionWrapper(unchecked, compileFrame, env);
fnDef->fWrap = result->fWrap;
@ -140,7 +140,7 @@ namespace MetaData {
void JS2Metadata::ValidateStmt(Context *cxt, Environment *env, Plurality pl, StmtNode *p)
{
CompoundAttribute *a = NULL;
RootKeeper rk(&a);
DEFINE_ROOTKEEPER(rk, a);
Frame *curTopFrame = env->getTopFrame();
try {
@ -2858,7 +2858,7 @@ doUnary:
if (s) {
for (NamespaceListIterator nli = multiname->nsList->begin(), nlend = multiname->nsList->end(); (nli != nlend); nli++) {
Multiname *mn = new Multiname(multiname->name, *nli);
RootKeeper rk(&mn);
DEFINE_ROOTKEEPER(rk, mn);
InstanceMember *m = findBaseInstanceMember(s, mn, access);
if (mBase == NULL)
mBase = m;
@ -3235,8 +3235,6 @@ static const uint8 urlCharType[256] =
world(world),
engine(new JS2Engine(world)),
publicNamespace(new Namespace(engine->public_StringAtom)),
mn1(new Multiname(NULL, publicNamespace)),
mn2(new Multiname(NULL, publicNamespace)),
bCon(new BytecodeContainer()),
glob(new Package(new Namespace(&world.identifiers["internal"]))),
env(new Environment(new MetaData::SystemFrame(), glob)),
@ -3245,9 +3243,6 @@ static const uint8 urlCharType[256] =
{
engine->meta = this;
JS2Object::addRoot(&mn1);
JS2Object::addRoot(&mn2);
cxt.openNamespaces.clear();
cxt.openNamespaces.push_back(publicNamespace);
@ -3713,7 +3708,7 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now...
bool JS2Metadata::hasOwnProperty(JS2Object *obj, const String *name)
{
Multiname *mn = new Multiname(name, publicNamespace);
RootKeeper rk(&mn);
DEFINE_ROOTKEEPER(rk, mn);
js2val val = OBJECT_TO_JS2VAL(obj);
return (findCommonMember(&val, mn, ReadWriteAccess, true) != NULL);
}
@ -4224,14 +4219,14 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now...
void ParameterFrame::assignArguments(JS2Metadata *meta, JS2Object *fnObj, js2val *argBase, uint32 argCount)
{
Multiname *mn = new Multiname(NULL, meta->publicNamespace);
RootKeeper rk1(&mn);
DEFINE_ROOTKEEPER(rk1, mn);
ASSERT(pluralFrame->kind == ParameterKind);
ParameterFrame *plural = checked_cast<ParameterFrame *>(pluralFrame);
ASSERT((plural->positionalCount == 0) || (plural->positional != NULL));
SimpleInstance *argsObj = new SimpleInstance(meta, meta->objectClass->prototype, meta->objectClass);;
RootKeeper rk2(&argsObj);
DEFINE_ROOTKEEPER(rk2, argsObj);
// Add the 'arguments' property
String *name = &meta->world.identifiers["arguments"];
@ -4326,17 +4321,28 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now...
************************************************************************************/
Pond JS2Object::pond(POND_SIZE, NULL);
#ifdef DEBUG
std::list<RootKeeper *> JS2Object::rootList;
#else
std::list<PondScum **> JS2Object::rootList;
#endif
// Add a pointer to a gc-allocated object to the root list
// Add a pointer to the (address of a) gc-allocated object to the root list
// (Note - we hand out an iterator, so it's essential to
// use something like std::list that doesn't mess with locations)
#ifdef DEBUG
JS2Object::RootIterator JS2Object::addRoot(RootKeeper *t)
{
return rootList.insert(rootList.end(), t);
}
#else
JS2Object::RootIterator JS2Object::addRoot(void *t)
{
PondScum **p = (PondScum **)t;
ASSERT(p);
return rootList.insert(rootList.end(), p);
}
#endif
// Remove a root pointer
void JS2Object::removeRoot(RootIterator ri)
@ -4348,26 +4354,44 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now...
uint32 JS2Object::gc()
{
pond.resetMarks();
// Anything on the root list is a pointer to a JS2Object.
for (std::list<PondScum **>::iterator i = rootList.begin(), end = rootList.end(); (i != end); i++) {
// Anything on the root list may also be a pointer to a JS2Object.
for (RootIterator i = rootList.begin(), end = rootList.end(); (i != end); i++) {
#ifdef DEBUG
RootKeeper *r = *i;
if (*(r->p)) {
PondScum *p = (*(r->p) - 1);
ASSERT(p->owner && (p->getSize() >= sizeof(PondScum)) && (p->owner->sanity == POND_SANITY));
if (p->isJS2Object()) {
JS2Object *obj = (JS2Object *)(p + 1);
GCMARKOBJECT(obj)
}
else
mark(p);
}
#else
if (**i) {
PondScum *p = (**i) - 1;
ASSERT(p->owner && (p->getSize() >= sizeof(PondScum)) && (p->owner->sanity == POND_SANITY));
JS2Object *obj = (JS2Object *)(p + 1);
GCMARKOBJECT(obj)
if (p->isJS2Object()) {
JS2Object *obj = (JS2Object *)(p + 1);
GCMARKOBJECT(obj)
}
else
mark(p);
}
#endif
}
return pond.moveUnmarkedToFreeList();
}
// Allocate a chunk of size s
void *JS2Object::alloc(size_t s)
void *JS2Object::alloc(size_t s, bool isJS2Object)
{
s += sizeof(PondScum);
// make sure that the thing is a multiple of 16 bytes
if (s & 0xF) s += 16 - (s & 0xF);
ASSERT(s <= 0x7FFFFFFF);
void *p = pond.allocFromPond(s);
void *p = pond.allocFromPond(s, isJS2Object);
ASSERT(((ptrdiff_t)p & 0xF) == 0);
return p;
}
@ -4429,7 +4453,7 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now...
}
// Allocate from this or the next Pond (make a new one if necessary)
void *Pond::allocFromPond(size_t sz)
void *Pond::allocFromPond(size_t sz, bool isJS2Object)
{
// See if there's room left...
@ -4459,15 +4483,16 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now...
// there isn't one; run the gc
uint32 released = JS2Object::gc();
if (released > sz)
return JS2Object::alloc(sz - sizeof(PondScum));
return JS2Object::alloc(sz - sizeof(PondScum), isJS2Object);
nextPond = new Pond(sz, nextPond);
}
return nextPond->allocFromPond(sz);
return nextPond->allocFromPond(sz, isJS2Object);
}
// there was room, so acquire it
PondScum *p = (PondScum *)pondTop;
p->owner = this;
p->setSize(sz);
if (isJS2Object) p->setIsJS2Object();
pondTop += sz;
pondSize -= sz;
#ifdef DEBUG

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

@ -147,16 +147,22 @@ enum Hint { NoHint, NumberHint, StringHint };
class PondScum {
public:
void resetMark() { size &= 0x7FFFFFFF; }
void mark() { size |= 0x80000000; }
bool isMarked() { return ((size & 0x80000000) != 0); }
uint32 getSize() { return size & 0x7FFFFFFF; }
void setSize(uint32 sz) { ASSERT((sz & 0x8000000) == 0); size = (sz & 0x7FFFFFFF); }
void setIsJS2Object() { size |= 0x40000000; }
bool isJS2Object() { return ((size & 0x40000000) != 0); }
uint32 getSize() { return size & 0x3FFFFFFF; }
void setSize(uint32 sz) { ASSERT((sz & 0xC000000) == 0); size = (sz & 0x3FFFFFFF); }
Pond *owner; // for a piece of scum in use, this points to it's own Pond
// otherwise it's a link to the next item on the free list
private:
uint32 size; // The high bit is used as the gc mark flag
// The next high bit is the flag that mark JS2Objects (1) or generic pointers
};
// A pond is a place to get chunks of PondScum from and to return them to
@ -166,7 +172,7 @@ class Pond {
public:
Pond(size_t sz, Pond *nextPond);
void *allocFromPond(size_t sz);
void *allocFromPond(size_t sz, bool isJS2Object);
uint32 returnToPond(PondScum *p);
void resetMarks();
@ -187,6 +193,8 @@ public:
#define GCMARKOBJECT(n) if ((n) && !(n)->isMarked()) { (n)->markObject(); (n)->markChildren(); }
#define GCMARKVALUE(v) JS2Object::markJS2Value(v)
class RootKeeper;
class JS2Object {
// Every object is either undefined, null, a Boolean,
// a number, a string, a namespace, a compound attribute, a class, a method closure,
@ -198,18 +206,23 @@ public:
ObjectKind kind;
static Pond pond;
#ifdef DEBUG
static std::list<RootKeeper *> rootList;
typedef std::list<RootKeeper *>::iterator RootIterator;
static RootIterator addRoot(RootKeeper *t);
#else
static std::list<PondScum **> rootList;
typedef std::list<PondScum **>::iterator RootIterator;
static uint32 gc();
static RootIterator addRoot(void *t); // pass the address of any JS2Object pointer
// Note: Not the address of a JS2VAL!
#endif
static uint32 gc();
static void removeRoot(RootIterator ri);
static void *alloc(size_t s);
static void *alloc(size_t s, bool isJS2Object = false);
static void unalloc(void *p);
void *operator new(size_t s) { return alloc(s); }
void *operator new(size_t s) { return alloc(s, true); }
void operator delete(void *p) { unalloc(p); }
virtual void markChildren() { } // XXX !!!! XXXX these are supposed to not have vtables !!!!
@ -222,12 +235,33 @@ public:
class RootKeeper {
public:
RootKeeper(void *t) : ri(JS2Object::addRoot(t)) { }
#ifdef DEBUG
RootKeeper(void *p, int line, char *pfile) : p((PondScum **)p), line(line)
{
file = new char[strlen(pfile) + 1];
strcpy(file, pfile);
ri = JS2Object::addRoot(this);
}
#else
RootKeeper(void *p) :{ ri = JS2Object::addRoot(p); }
#endif
~RootKeeper() { JS2Object::removeRoot(ri); }
JS2Object::RootIterator ri;
#ifdef DEBUG
PondScum **p;
int line;
char *file;
#endif
};
#ifdef DEBUG
#define DEFINE_ROOTKEEPER(rk_var, obj) RootKeeper rk_var(&obj, __LINE__, __FILE__);
#else
#define DEFINE_ROOTKEEPER(rk_var, obj) RootKeeper rk_var(&obj);
#endif
class Attribute : public JS2Object {
public:
enum AttributeKind { TrueAttr, FalseAttr, NamespaceAttr, CompoundAttr };
@ -1248,8 +1282,6 @@ public:
// The one and only 'public' namespace
Namespace *publicNamespace;
Multiname *mn1, *mn2; // useful, gc-rooted multiname temps.
LocalMember *forbiddenMember; // just need one of these hanging around
// The base classes:

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

@ -183,7 +183,7 @@ namespace MetaData {
// XXX Change constructors to take js2val pointer for the result (which would be an already
// rooted pointer).
RegExpInstance *thisInst = new RegExpInstance(meta, meta->regexpClass->prototype, meta->regexpClass);
RootKeeper rk(&thisInst);
DEFINE_ROOTKEEPER(rk, thisInst);
js2val thatValue = OBJECT_TO_JS2VAL(thisInst);
REuint32 flags = 0;

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

@ -59,7 +59,7 @@ js2val String_Constructor(JS2Metadata *meta, const js2val /*thisValue*/, js2val
{
js2val thatValue = OBJECT_TO_JS2VAL(new StringInstance(meta, meta->stringClass->prototype, meta->stringClass));
StringInstance *strInst = checked_cast<StringInstance *>(JS2VAL_TO_OBJECT(thatValue));
RootKeeper rk(&strInst);
DEFINE_ROOTKEEPER(rk, strInst);
if (argc > 0)
strInst->mValue = meta->engine->allocStringPtr(meta->toString(argv[0]));
else
@ -169,7 +169,7 @@ static js2val String_match(JS2Metadata *meta, const js2val thisValue, js2val *ar
}
else {
ArrayInstance *A = new ArrayInstance(meta, meta->arrayClass->prototype, meta->arrayClass);
RootKeeper rk(&A);
DEFINE_ROOTKEEPER(rk, A);
int32 index = 0;
int32 lastIndex = 0;
while (true) {
@ -405,7 +405,7 @@ static js2val String_split(JS2Metadata *meta, const js2val thisValue, js2val *ar
js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta, meta->arrayClass->prototype, meta->arrayClass));
ArrayInstance *A = checked_cast<ArrayInstance *>(JS2VAL_TO_OBJECT(result));
RootKeeper rk(&A);
DEFINE_ROOTKEEPER(rk, A);
uint32 lim;
js2val separatorV = (argc > 0) ? argv[0] : JS2VAL_UNDEFINED;

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

@ -35,8 +35,6 @@
#define js2value_h___
#define JS2_BIT(n) ((uint32)1 << (n))
#define JS2_BITMASK(n) (JS2_BIT(n) - 1)
/*
* Type tags stored in the low bits of a js2val.
*/
@ -67,11 +65,11 @@
/* Type tag bitfield length and derived macros. */
#define JS2VAL_TAGBITS 4
#define JS2VAL_TAGMASK JS2_BITMASK(JS2VAL_TAGBITS)
#define JS2VAL_TAGMASK JS_BITMASK(JS2VAL_TAGBITS)
#define JS2VAL_TAG(v) ((v) & JS2VAL_TAGMASK)
#define JS2VAL_SETTAG(v,t) ((v) | (t))
#define JS2VAL_CLRTAG(v) ((v) & ~(js2val)JS2VAL_TAGMASK)
#define JS2VAL_ALIGN JS2_BIT(JS2VAL_TAGBITS)
#define JS2VAL_ALIGN JS_BIT(JS2VAL_TAGBITS)
#define JS2VAL_INT_POW2(n) ((js2val)1 << (n))
#define JS2VAL_INT_MAX (JS2VAL_INT_POW2(30) - 1)