From 91f00d84f6cf75de419fc5d20b5c76891c704a9b Mon Sep 17 00:00:00 2001 From: "rogerl%netscape.com" Date: Wed, 28 Aug 2002 00:45:55 +0000 Subject: [PATCH] Ongoing namespace implementation + first gc. --- js2/src/js2engine.cpp | 4 +- js2/src/js2engine.h | 1 + js2/src/js2metadata.cpp | 185 ++++++++++++++++++++++++++++++++------- js2/src/js2metadata.h | 53 ++++++++--- js2/src/js2op_access.cpp | 23 ++++- 5 files changed, 223 insertions(+), 43 deletions(-) diff --git a/js2/src/js2engine.cpp b/js2/src/js2engine.cpp index 024c57cc25de..ae3863670514 100644 --- a/js2/src/js2engine.cpp +++ b/js2/src/js2engine.cpp @@ -88,7 +88,7 @@ js2val JS2Engine::interpreterLoop() // return a pointer to an 8 byte chunk in the gc heap void *JS2Engine::gc_alloc_8() { - return STD::malloc(8); + return JS2Object::alloc(8); } // See if the double value is in the hash table, return it's pointer if so @@ -243,6 +243,8 @@ int JS2Engine::getStackEffect(JS2Op op) case eQMultiname: return 1; // push the multiname object + case eUse: + return -1; // consume a namespace object default: ASSERT(false); diff --git a/js2/src/js2engine.h b/js2/src/js2engine.h index 4e85ff51a876..b65fab8a3f59 100644 --- a/js2/src/js2engine.h +++ b/js2/src/js2engine.h @@ -62,6 +62,7 @@ enum JS2Op { eNewObject, // eMultiname, // eQMultiname, // + eUse, }; diff --git a/js2/src/js2metadata.cpp b/js2/src/js2metadata.cpp index af514aaf1807..47f01797e833 100644 --- a/js2/src/js2metadata.cpp +++ b/js2/src/js2metadata.cpp @@ -107,6 +107,7 @@ namespace MetaData { case StmtNode::Var: case StmtNode::Const: { + bool immutable = (p->getKind() == StmtNode::Const); Attribute *attr = NULL; VariableStmtNode *vs = checked_cast(p); @@ -118,14 +119,15 @@ namespace MetaData { VariableBinding *v = vs->bindings; Frame *regionalFrame = env->getRegionalFrame(); while (v) { + const StringAtom *name = v->name; ValidateTypeExpression(v->type); if (cxt->strict && ((regionalFrame->kind == GlobalObjectKind) || (regionalFrame->kind == FunctionKind)) - && (p->getKind() == StmtNode::Var) // !immutable + && !immutable && (vs->attributes == NULL) && (v->type == NULL)) { - defineHoistedVar(env, *v->name, p); + defineHoistedVar(env, *name, p); } else { CompoundAttribute *a = Attribute::toCompoundAttribute(attr); @@ -137,15 +139,18 @@ namespace MetaData { memberMod = Attribute::Final; switch (memberMod) { case Attribute::NoModifier: - case Attribute::Static: + case Attribute::Static: { + Variable *var = new Variable(NULL, JS2VAL_UNDEFINED, immutable); + defineStaticMember(env, *name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, var, p->pos); + // XXX - not done !!! XXX + // the type and the value are 'future' + } break; } } v = v->next; } - if (attr) - delete attr; } break; case StmtNode::expression: @@ -171,6 +176,10 @@ namespace MetaData { defineStaticMember(env, ns->name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, v, p->pos); } break; + case StmtNode::Use: + { + } + break; } // switch (p->getKind()) } @@ -180,12 +189,16 @@ namespace MetaData { */ js2val JS2Metadata::EvalStmtList(Phase phase, StmtNode *p) { + BytecodeContainer *saveBacon = bCon; + bCon = new BytecodeContainer(); while (p) { EvalStmt(&env, phase, p); p = p->next; } bCon->emitOp(eReturnVoid); - return engine->interpret(this, phase, bCon); + js2val retval = engine->interpret(this, phase, bCon); + bCon = saveBacon; + return retval; } /* @@ -236,6 +249,18 @@ namespace MetaData { { } break; + case StmtNode::Use: + { + UseStmtNode *u = checked_cast(p); + ExprList *eList = u->namespaces; + while (eList) { + Reference *r = EvalExprNode(env, phase, eList->expr); + if (r) r->emitReadBytecode(bCon); + bCon->emitOp(eUse); + eList = eList->next; + } + } + break; default: NOT_REACHED("Not Yet Implemented"); } // switch (p->getKind()) @@ -358,10 +383,12 @@ namespace MetaData { // anything else (just references of one kind or another) must // be compile-time constant values that resolve to namespaces js2val av = EvalExpression(env, CompilePhase, p); - if (JS2VAL_IS_NULL(av) || JS2VAL_IS_VOID(av) || !JS2VAL_IS_OBJECT(av)) + if (JS2VAL_IS_NULL(av) || !JS2VAL_IS_OBJECT(av)) reportError(Exception::badValueError, "Namespace expected in attribute", p->pos); JS2Object *obj = JS2VAL_TO_OBJECT(av); - + if ((obj->kind != AttributeObjectKind) || (checked_cast(obj)->attrKind != Attribute::NamespaceAttr)) + reportError(Exception::badValueError, "Namespace expected in attribute", p->pos); + return checked_cast(obj); } break; @@ -435,9 +462,13 @@ namespace MetaData { // add the namespace to our list, but only if it's not there already void CompoundAttribute::addNamespace(Namespace *n) { - for (NamespaceListIterator i = namespaces->begin(), end = namespaces->end(); (i != end); i++) - if (*i == n) - return; + if (namespaces) { + for (NamespaceListIterator i = namespaces->begin(), end = namespaces->end(); (i != end); i++) + if (*i == n) + return; + } + else + namespaces = new NamespaceList(); namespaces->push_back(n); } @@ -538,11 +569,11 @@ namespace MetaData { { if (phase == CompilePhase) reportError(Exception::compileExpressionError, "Inappropriate compile time expression", p->pos); BinaryExprNode *b = checked_cast(p); - returnRef = EvalExprNode(env, phase, b->op1); - if (returnRef) { + Reference *lVal = EvalExprNode(env, phase, b->op1); + if (lVal) { Reference *rVal = EvalExprNode(env, phase, b->op2); if (rVal) rVal->emitReadBytecode(bCon); - returnRef->emitWriteBytecode(bCon); + lVal->emitWriteBytecode(bCon); } else reportError(Exception::semanticError, "Assignment needs an lValue", p->pos); @@ -564,7 +595,7 @@ namespace MetaData { if (phase == CompilePhase) reportError(Exception::compileExpressionError, "Inappropriate compile time expression", p->pos); UnaryExprNode *u = checked_cast(p); Reference *lVal = EvalExprNode(env, phase, u->op); - ASSERT(false); // not an lvalue + ASSERT(false); } break; @@ -578,14 +609,17 @@ namespace MetaData { { QualifyExprNode *qe = checked_cast(p); const StringAtom &name = checked_cast(p)->name; - EvalExprNode(env, phase, qe->qualifier); + Reference *rVal = EvalExprNode(env, phase, qe->qualifier); + if (rVal) rVal->emitReadBytecode(bCon); returnRef = new LexicalReference(name, cxt.strict, true); + ((LexicalReference *)returnRef)->emitBindBytecode(bCon); } break; case ExprNode::identifier: { IdentifierExprNode *i = checked_cast(p); returnRef = new LexicalReference(i->name, cxt.strict); + ((LexicalReference *)returnRef)->emitBindBytecode(bCon); } break; case ExprNode::boolean: @@ -826,7 +860,7 @@ namespace MetaData { while (fr != regionalFrame) { for (b = fr->staticReadBindings.lower_bound(id), end = fr->staticReadBindings.upper_bound(id); (b != end); b++) { - if (mn->matches(b->second->qname) && (b->second->content->kind != StaticMember::Forbidden)) + if (mn->matches(b->second->qname) && (b->second->content->kind == StaticMember::Forbidden)) reportError(Exception::definitionError, "Duplicate definition {0}", pos, id); } fr = fr->nextFrame; @@ -841,7 +875,7 @@ namespace MetaData { for (NamespaceListIterator nli = mn->nsList.begin(), nlend = mn->nsList.end(); (nli != nlend); nli++) { QualifiedName qName(*nli, id); - StaticBinding *sb = new StaticBinding(qName, new HoistedVar()); + StaticBinding *sb = new StaticBinding(qName, m); const StaticBindingMap::value_type e(id, sb); if (access & ReadAccess) regionalFrame->staticReadBindings.insert(e); @@ -849,13 +883,12 @@ namespace MetaData { regionalFrame->staticWriteBindings.insert(e); } - StaticMember *forbidden = new StaticMember(StaticMember::Forbidden); if (localFrame != regionalFrame) { Frame *fr = localFrame->nextFrame; while (fr != regionalFrame) { for (NamespaceListIterator nli = mn->nsList.begin(), nlend = mn->nsList.end(); (nli != nlend); nli++) { QualifiedName qName(*nli, id); - StaticBinding *sb = new StaticBinding(qName, forbidden); + StaticBinding *sb = new StaticBinding(qName, forbiddenMember); const StaticBindingMap::value_type e(id, sb); if (access & ReadAccess) fr->staticReadBindings.insert(e); @@ -1063,7 +1096,8 @@ namespace MetaData { reportError(Exception::propertyAccessError, "Forbidden access", errorPos); break; case StaticMember::Variable: - break; + *rval = (checked_cast(m))->value; + return true; case StaticMember::HoistedVariable: *rval = (checked_cast(m))->value; return true; @@ -1086,7 +1120,8 @@ namespace MetaData { reportError(Exception::propertyAccessError, "Forbidden access", errorPos); break; case StaticMember::Variable: - break; + (checked_cast(m))->value = newValue; + return true; case StaticMember::HoistedVariable: (checked_cast(m))->value = newValue; return true; @@ -1241,23 +1276,85 @@ namespace MetaData { * ************************************************************************************/ - Pond::Pond(size_t sz, Pond *next) : pondSize(sz + POND_SIZE), pondBase(new uint8[pondSize]), pondTop(pondBase), nextPond(next) + Pond::Pond(size_t sz, Pond *next) : sanity(POND_SANITY), pondSize(sz + POND_SIZE), pondBase(new uint8[pondSize]), pondTop(pondBase), freeHeader(NULL), nextPond(next) { } - void *Pond::allocFromPond(size_t sz) + // Allocate from this or the next Pond (make a new one if necessary) + void *Pond::allocFromPond(int32 sz) { + // Try scannning the free list... + PondScum *p = freeHeader; + PondScum *pre = NULL; + while (p) { + ASSERT(p->size > 0); + if (p->size >= sz) { + if (pre) + pre->owner = p->owner; + else + freeHeader = (PondScum *)(p->owner); + p->owner = this; + return (p + 1); + } + pre = p; + p = (PondScum *)(p->owner); + } + + // See if there's room left... if (sz > (pondSize - (pondTop - pondBase))) { if (nextPond == NULL) nextPond = new Pond(sz, nextPond); return nextPond->allocFromPond(sz); } - void *p = pondTop; + p = (PondScum *)pondTop; + p->owner = this; + p->size = sz; pondTop += sz; - return p; +#ifdef DEBUG + memset((p + 1), 0xB7, p->size - sizeof(PondScum)); +#endif + return (p + 1); + } + + // Stick the chunk at the start of the free list + void Pond::returnToPond(PondScum *p) + { + p->owner = (Pond *)freeHeader; + p->size &= 0x7FFFFFFF; // might have lingering mark from previous gc + uint8 *t = (uint8 *)(p + 1); +#ifdef DEBUG + memset(t, 0xB7, p->size - sizeof(PondScum)); +#endif + freeHeader = p; + } + + // Clear the mark bit from all PondScums + void Pond::resetMarks() + { + uint8 *t = pondBase; + while (t != pondTop) { + PondScum *p = (PondScum *)t; + p->resetMark(); + t += p->size; + } + if (nextPond) + nextPond->resetMarks(); + } + + // Anything left unmarked is now moved to the free list + void Pond::moveUnmarkedToFreeList() + { + uint8 *t = pondBase; + while (t != pondTop) { + PondScum *p = (PondScum *)t; + if (!p->isMarked()) + returnToPond(p); + t += p->size; + } + if (nextPond) + nextPond->moveUnmarkedToFreeList(); } - /************************************************************************************ * * JS2Object @@ -1265,14 +1362,42 @@ namespace MetaData { ************************************************************************************/ Pond JS2Object::pond(POND_SIZE, NULL); + std::vector JS2Object::rootList; - void *JS2Object::operator new(size_t s) + void JS2Object::addRoot(void *t) { + PondScum **p = (PondScum **)t; + ASSERT(p); + rootList.push_back(p); + } + + void JS2Object::gc() + { + pond.resetMarks(); + for (std::vector::iterator i = rootList.begin(), end = rootList.end(); (i != end); i++) { + PondScum *p = **i; + if (p) { + ASSERT(p->owner && (p->size >= sizeof(PondScum)) && (p->owner->sanity == POND_SANITY)); + p->mark(); + } + } + pond.moveUnmarkedToFreeList(); + } + + void *JS2Object::alloc(size_t s) + { + s += sizeof(PondScum); // make sure that the thing is 8-byte aligned if (s & 0x7) s += 8 - (s & 0x7); + ASSERT(s <= 0x7FFFFFFF); + return pond.allocFromPond((int32)s); + } - return pond.allocFromPond(s); - + void JS2Object::unalloc(void *t) + { + PondScum *p = (PondScum *)t - 1; + ASSERT(p->owner && (p->size >= sizeof(PondScum)) && (p->owner->sanity == POND_SANITY)); + p->owner->returnToPond(p); } }; // namespace MetaData diff --git a/js2/src/js2metadata.h b/js2/src/js2metadata.h index edad791bdf3a..1d52c0dcb8e7 100644 --- a/js2/src/js2metadata.h +++ b/js2/src/js2metadata.h @@ -40,7 +40,7 @@ namespace JavaScript { namespace MetaData { -// forward definitions: +// forward declarations: class JS2Object; class JS2Metadata; class JS2Class; @@ -49,6 +49,8 @@ class Environment; class Context; class CompoundAttribute; class BytecodeContainer; +class Pond; + typedef void (Invokable)(); typedef Invokable Callor; @@ -68,17 +70,37 @@ enum ObjectKind { MultinameKind }; -#define POND_SIZE (8000) +class PondScum { +public: + void resetMark() { size &= 0x7FFFFFFF; } + void mark() { size = -size; } + bool isMarked() { return (size < 0); } + Pond *owner; + int32 size; +}; + +// A pond is a place to get chunks of PondScum from and to return them to +#define POND_SIZE (8000) +#define POND_SANITY (0xFADE2BAD) class Pond { public: Pond(size_t sz, Pond *nextPond); - void *allocFromPond(size_t t); + void *allocFromPond(int32 sz); + void returnToPond(PondScum *p); + + void resetMarks(); + void moveUnmarkedToFreeList(); + + uint32 sanity; size_t pondSize; uint8 *pondBase; uint8 *pondTop; + + PondScum *freeHeader; + Pond *nextPond; }; @@ -92,8 +114,16 @@ public: ObjectKind kind; - static Pond pond; - void *operator new(size_t s); + static Pond pond; + static std::vector rootList; + static void gc(); + static void addRoot(void *t); // pass the address of any JS2Object pointer + + static void *alloc(size_t s); + static void unalloc(void *p); + + void *operator new(size_t s) { return alloc(s); } + void operator delete(void *p) { unalloc(p); } #ifdef DEBUG @@ -255,17 +285,18 @@ public: typedef std::map DynamicPropertyMap; typedef DynamicPropertyMap::iterator DynamicPropertyIterator; + // A STATICBINDING describes the member to which one qualified name is bound in a frame. Multiple // qualified names may be bound to the same member in a frame, but a qualified name may not be // bound to multiple members in a frame (except when one binding is for reading only and // the other binding is for writing only). class StaticBinding { public: - StaticBinding(QualifiedName &qname, StaticMember *content) : qname(qname), content(content), xplicit(false) { } + StaticBinding(QualifiedName &qname, StaticMember *content) : qname(qname), xplicit(false), content(content) { } QualifiedName qname; // The qualified name bound by this binding - StaticMember *content; // The member to which this qualified name was bound bool xplicit; // true if this binding should not be imported into the global scope by an import statement + StaticMember *content; // The member to which this qualified name was bound }; @@ -435,9 +466,9 @@ public: bool strict; // The strict setting from the context in effect at the point where the reference was created - - virtual void emitReadBytecode(BytecodeContainer *bCon) { variableMultiname->emitBytecode(bCon); bCon->emitOp(eLexicalRead); } - virtual void emitWriteBytecode(BytecodeContainer *bCon) { variableMultiname->emitBytecode(bCon); bCon->emitOp(eLexicalWrite); } + void emitBindBytecode(BytecodeContainer *bCon) { variableMultiname->emitBytecode(bCon); } + virtual void emitReadBytecode(BytecodeContainer *bCon) { bCon->emitOp(eLexicalRead); } + virtual void emitWriteBytecode(BytecodeContainer *bCon) { bCon->emitOp(eLexicalWrite); } }; class DotReference : public Reference { @@ -643,6 +674,8 @@ public: // The one and only 'public' namespace Namespace *publicNamespace; + StaticMember *forbiddenMember; // just need one of these hanging around + // The base classes: JS2Class *undefinedClass; JS2Class *nullClass; diff --git a/js2/src/js2op_access.cpp b/js2/src/js2op_access.cpp index 64b43b248ce9..7a48734cd8ae 100644 --- a/js2/src/js2op_access.cpp +++ b/js2/src/js2op_access.cpp @@ -31,6 +31,8 @@ * file under either the NPL or the GPL. */ + // Get a multiname literal and add the currently open namespaces from the context + // Push the resulting multiname object case eMultiname: { Multiname *mn = bCon->mMultinameList[BytecodeContainer::getShort(pc)]; pc += sizeof(short); @@ -39,6 +41,8 @@ } break; + // Get a multiname literal and pop a namespace value to add to it + // Push the resulting multiname object case eQMultiname: { js2val nsVal = pop(); if (!JS2VAL_IS_OBJECT(nsVal)) @@ -53,23 +57,38 @@ } break; + // Pop a multiname object and read it's value from the environment on to the stack. case eLexicalRead: { js2val mnVal = pop(); ASSERT(JS2VAL_IS_OBJECT(mnVal)); JS2Object *obj = JS2VAL_TO_OBJECT(mnVal); Multiname *mn = checked_cast(obj); - push(meta->env.lexicalRead(meta, mn, phase)); + retval = meta->env.lexicalRead(meta, mn, phase); + push(retval); } break; + // Pop a value and a multiname. Write the value to the multiname in the environment, leave + // the value on the stack top. case eLexicalWrite: { + retval = pop(); js2val mnVal = pop(); ASSERT(JS2VAL_IS_OBJECT(mnVal)); JS2Object *obj = JS2VAL_TO_OBJECT(mnVal); Multiname *mn = checked_cast(obj); - retval = pop(); meta->env.lexicalWrite(meta, mn, retval, true, phase); + push(retval); } break; + // Pop a namespace object and add it to the list of currently open namespaces in the context + case eUse: { + js2val nsVal = pop(); + if (!JS2VAL_IS_OBJECT(nsVal)) + meta->reportError(Exception::badValueError, "Expected a namespace", meta->errorPos); + JS2Object *obj = JS2VAL_TO_OBJECT(nsVal); + if ((obj->kind != AttributeObjectKind) || ((checked_cast(obj))->attrKind != Attribute::NamespaceAttr)) + meta->reportError(Exception::badValueError, "Expected a namespace", meta->errorPos); + } + break; \ No newline at end of file