From 137a2c82d282c30b9e130bc92d96180234fc0fe6 Mon Sep 17 00:00:00 2001 From: "rogerl%netscape.com" Date: Sat, 7 Sep 2002 00:33:31 +0000 Subject: [PATCH] Variable initialization support. --- js2/src/js2metadata.cpp | 158 ++++++++++++++++++++++++++++++++-------- js2/src/js2metadata.h | 59 ++++++++------- js2/src/js2value.h | 10 +++ js2/src/parser.h | 9 +++ 4 files changed, 180 insertions(+), 56 deletions(-) diff --git a/js2/src/js2metadata.cpp b/js2/src/js2metadata.cpp index 4ae1554deda..f856bd326e4 100644 --- a/js2/src/js2metadata.cpp +++ b/js2/src/js2metadata.cpp @@ -129,17 +129,18 @@ namespace MetaData { attr = EvalAttributeExpression(env, CompilePhase, vs->attributes); } - VariableBinding *v = vs->bindings; + VariableBinding *vb = vs->bindings; Frame *regionalFrame = env->getRegionalFrame(); - while (v) { - const StringAtom *name = v->name; - ValidateTypeExpression(v->type); + while (vb) { + const StringAtom *name = vb->name; + ValidateTypeExpression(cxt, env, vb->type); + vb->member = NULL; if (cxt->strict && ((regionalFrame->kind == GlobalObjectKind) || (regionalFrame->kind == FunctionKind)) && !immutable && (vs->attributes == NULL) - && (v->type == NULL)) { + && (vb->type == NULL)) { defineHoistedVar(env, *name, p); } else { @@ -153,18 +154,19 @@ namespace MetaData { switch (memberMod) { case Attribute::NoModifier: 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' + Variable *v = new Variable(FUTURE_TYPE, immutable ? JS2VAL_FUTUREVALUE : JS2VAL_INACCESSIBLE, immutable); + vb->member = v; + v->vb = vb; + vb->mn = defineStaticMember(env, *name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, v, p->pos); } break; case Attribute::Virtual: case Attribute::Final: { JS2Class *c = checked_cast(env->getTopFrame()); - InstanceMember *m = new InstanceVariable(immutable, (memberMod == Attribute::Final), c->slotCount++); - defineInstanceMember(c, cxt, *name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, m, p->pos); + InstanceMember *m = new InstanceVariable(FUTURE_TYPE, immutable, (memberMod == Attribute::Final), c->slotCount++); + vb->member = m; + vb->osp = defineInstanceMember(c, cxt, *name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, m, p->pos); } break; default: @@ -173,7 +175,7 @@ namespace MetaData { } } - v = v->next; + vb = vb->next; } } break; @@ -292,6 +294,30 @@ namespace MetaData { return retval; } + JS2Class *JS2Metadata::getVariableType(Variable *v, Phase phase, size_t pos) + { + JS2Class *type = v->type; + if (type == NULL) { // Inaccessible, Note that this can only happen when phase = compile + // because the compilation phase ensures that all types are valid, + // so invalid types will not occur during the run phase. + ASSERT(phase == CompilePhase); + reportError(Exception::compileExpressionError, "No type assigned", pos); + } + else { + if (v->type == FUTURE_TYPE) { + // Note that phase = compile because all futures are resolved by the end of the compilation phase. + ASSERT(phase == CompilePhase); + if (v->vb->type) { + v->type = NULL; + v->type = EvalTypeExpression(&env, CompilePhase, v->vb->type); + } + else + v->type = objectClass; + } + } + return v->type; + } + /* * Evaluate an individual statement 'p', including it's children * - this generates bytecode for each statement, but doesn't actually @@ -348,12 +374,76 @@ namespace MetaData { case StmtNode::Var: case StmtNode::Const: { - VariableStmtNode *vs = checked_cast(p); - - VariableBinding *v = vs->bindings; - while (v) { + // Note that the code here is the PreEval code plus the emit of the Eval bytecode + VariableStmtNode *vs = checked_cast(p); + VariableBinding *vb = vs->bindings; + while (vb) { + if (vb->member) { + if (vb->member->kind == Member::Variable) { + Variable *v = checked_cast(vb->member); + JS2Class *type = getVariableType(v, CompilePhase, p->pos); + if (JS2VAL_IS_FUTURE(v->value)) { + v->value = JS2VAL_INACCESSIBLE; + try { + if (vb->initializer) { + js2val newValue = EvalExpression(env, CompilePhase, vb->initializer); + v->value = engine->assignmentConversion(newValue, type); + } + else + // Would only have come here if the variable was immutable + reportError(Exception::compileExpressionError, "Missing compile time expression", p->pos); + } + catch (Exception x) { + // If a compileExpressionError occurred, then the initialiser is not a compile-time + // constant expression. In this case, ignore the error and leave the value of the + // variable inaccessible until it is defined at run time. + if (x.kind != Exception::compileExpressionError) + throw x; + } + if (vb->initializer) { + // XXX more here - + // + // eGET_TOP_FRAME <-- establish base + // eDotRead mn> + // eIS_INACCESSIBLE + // eBRANCH_FALSE + // eGET_TOP_FRAME + // initializer code> + // + // eDotWrite mn> + // : + } - v = v->next; + } + } + else { + ASSERT(vb->member->kind == Member::InstanceVariableKind); + InstanceVariable *v = checked_cast(vb->member); + JS2Class *t; + if (vb->type) + t = EvalTypeExpression(env, CompilePhase, vb->type); + else { + if (vb->osp->first->overriddenMember && (vb->osp->first->overriddenMember != POTENTIAL_CONFLICT)) + t = vb->osp->first->overriddenMember->type; + else + if (vb->osp->second->overriddenMember && (vb->osp->second->overriddenMember != POTENTIAL_CONFLICT)) + t = vb->osp->second->overriddenMember->type; + else + t = objectClass; + } + v->type = t; + } + } + else { // HoistedVariable + if (vb->initializer) { + Reference *r = EvalExprNode(env, phase, vb->initializer); + if (r) r->emitReadBytecode(bCon, p->pos); + LexicalReference *lVal = new LexicalReference(*vb->name, cxt.strict); + lVal->variableMultiname->addNamespace(publicNamespace); + lVal->emitWriteBytecode(bCon, p->pos); + } + } + vb = vb->next; } } break; @@ -687,7 +777,7 @@ namespace MetaData { /* - * Evaluate an expression 'p' and execute the assocaited bytecode + * Evaluate an expression 'p' and execute the associated bytecode */ js2val JS2Metadata::EvalExpression(Environment *env, Phase phase, ExprNode *p) { @@ -856,8 +946,15 @@ doBinary: return returnRef; } - void JS2Metadata::ValidateTypeExpression(ExprNode *e) + JS2Class *JS2Metadata::EvalTypeExpression(Environment *env, Phase phase, ExprNode *p) { + js2val retval = EvalExpression(env, phase, p); + if (JS2VAL_IS_PRIMITIVE(retval)) + reportError(Exception::badValueError, "Type expected", p->pos); + JS2Object *obj = JS2VAL_TO_OBJECT(retval); + if (obj->kind != ClassKind) + reportError(Exception::badValueError, "Type expected", p->pos); + return checked_cast(obj); } /************************************************************************************ @@ -1011,7 +1108,7 @@ doBinary: // - If the binding exists (not forbidden) in lower frames in the regional environment, it's an error. // - Define a forbidden binding in all the lower frames. // - void JS2Metadata::defineStaticMember(Environment *env, const StringAtom &id, NamespaceList *namespaces, Attribute::OverrideModifier overrideMod, bool xplicit, Access access, StaticMember *m, size_t pos) + Multiname *JS2Metadata::defineStaticMember(Environment *env, const StringAtom &id, NamespaceList *namespaces, Attribute::OverrideModifier overrideMod, bool xplicit, Access access, StaticMember *m, size_t pos) { NamespaceList publicNamespaceList; @@ -1077,7 +1174,7 @@ doBinary: fr = fr->nextFrame; } } - + return mn; } // Look through 'c' and all it's super classes for a identifier @@ -1147,7 +1244,7 @@ doBinary: os->multiname.addNamespace(namespaces); } else { - os->overriddenMember = PotentialConflict; // Didn't find the member with a specified namespace, but did with + os->overriddenMember = POTENTIAL_CONFLICT; // Didn't find the member with a specified namespace, but did with // the use'd ones. That'll be an error unless the override is // disallowed (in defineInstanceMember below) os->multiname.addNamespace(namespaces); @@ -1180,11 +1277,11 @@ doBinary: } // Make sure we're getting what we expected if (expectMethod) { - if (os->overriddenMember && (os->overriddenMember != PotentialConflict) && (os->overriddenMember->kind != InstanceMember::InstanceMethodKind)) + if (os->overriddenMember && (os->overriddenMember != POTENTIAL_CONFLICT) && (os->overriddenMember->kind != InstanceMember::InstanceMethodKind)) reportError(Exception::definitionError, "Illegal override, expected method", pos); } else { - if (os->overriddenMember && (os->overriddenMember != PotentialConflict) && (os->overriddenMember->kind == InstanceMember::InstanceMethodKind)) + if (os->overriddenMember && (os->overriddenMember != POTENTIAL_CONFLICT) && (os->overriddenMember->kind == InstanceMember::InstanceMethodKind)) reportError(Exception::definitionError, "Illegal override, didn't expect method", pos); } @@ -1193,7 +1290,7 @@ doBinary: // Define an instance member in the class. Verify that, if any overriding is happening, it's legal. The result pair indicates // the members being overridden. - OverrideStatusPair JS2Metadata::defineInstanceMember(JS2Class *c, Context *cxt, const StringAtom &id, NamespaceList *namespaces, Attribute::OverrideModifier overrideMod, bool xplicit, Access access, InstanceMember *m, size_t pos) + OverrideStatusPair *JS2Metadata::defineInstanceMember(JS2Class *c, Context *cxt, const StringAtom &id, NamespaceList *namespaces, Attribute::OverrideModifier overrideMod, bool xplicit, Access access, InstanceMember *m, size_t pos) { OverrideStatus *readStatus; OverrideStatus *writeStatus; @@ -1210,13 +1307,13 @@ doBinary: else writeStatus = new OverrideStatus(NULL, id); - if ((readStatus->overriddenMember && (readStatus->overriddenMember != PotentialConflict)) - || (writeStatus->overriddenMember && (writeStatus->overriddenMember != PotentialConflict))) { + if ((readStatus->overriddenMember && (readStatus->overriddenMember != POTENTIAL_CONFLICT)) + || (writeStatus->overriddenMember && (writeStatus->overriddenMember != POTENTIAL_CONFLICT))) { if ((overrideMod != Attribute::DoOverride) && (overrideMod != Attribute::OverrideUndefined)) reportError(Exception::definitionError, "Illegal override", pos); } else { - if ((readStatus->overriddenMember == PotentialConflict) || (writeStatus->overriddenMember == PotentialConflict)) { + if ((readStatus->overriddenMember == POTENTIAL_CONFLICT) || (writeStatus->overriddenMember == POTENTIAL_CONFLICT)) { if ((overrideMod != Attribute::DontOverride) && (overrideMod != Attribute::OverrideUndefined)) reportError(Exception::definitionError, "Illegal override", pos); } @@ -1237,8 +1334,7 @@ doBinary: c->instanceWriteBindings.insert(e); } - OverrideStatusPair osp(readStatus, writeStatus); - return osp; + return new OverrideStatusPair(readStatus, writeStatus);; } // Define a hoisted var in the current frame (either Global or a Function) @@ -1579,7 +1675,7 @@ readClassProperty: return &checked_cast(thisObj)->slots[id->slotIndex]; } - + // Read the value of an instanceMember, if valid bool JS2Metadata::readInstanceMember(js2val containerVal, JS2Class *c, QualifiedName *qname, Phase phase, js2val *rval) { InstanceMember *m = findInstanceMember(c, qname, ReadAccess); diff --git a/js2/src/js2metadata.h b/js2/src/js2metadata.h index 65015db37e2..246515ac555 100644 --- a/js2/src/js2metadata.h +++ b/js2/src/js2metadata.h @@ -216,25 +216,36 @@ class Signature { bool returnType; // The type of this function's result }; -// A static member is either forbidden, a variable, a hoisted variable, a constructor method, or an accessor: -class StaticMember { +// A base class for Instance and Static members for convenience. +class Member { public: - enum StaticMemberKind { Forbidden, Variable, HoistedVariable, ConstructorMethod, Accessor }; + enum MemberKind { Forbidden, Variable, HoistedVariable, ConstructorMethod, Accessor, InstanceVariableKind, InstanceMethodKind, InstanceAccessorKind }; + + Member(MemberKind kind) : kind(kind) { } - StaticMember(StaticMemberKind kind) : kind(kind) { } + MemberKind kind; - StaticMemberKind kind; #ifdef DEBUG virtual void uselessVirtual() { } // want the checked_cast stuff to work, so need a virtual function #endif }; +// A static member is either forbidden, a variable, a hoisted variable, a constructor method, or an accessor: +class StaticMember : public Member { +public: + StaticMember(MemberKind kind) : Member(kind) { } + +}; + +#define FUTURE_TYPE ((JS2Class *)(-1)) + class Variable : public StaticMember { public: - Variable() : StaticMember(StaticMember::Variable), type(NULL), value(JS2VAL_VOID), immutable(false) { } + Variable() : StaticMember(Member::Variable), type(NULL), value(JS2VAL_VOID), immutable(false) { } Variable(JS2Class *type, js2val value, bool immutable) : StaticMember(StaticMember::Variable), type(type), value(value), immutable(immutable) { } - JS2Class *type; // Type of values that may be stored in this variable + JS2Class *type; // Type of values that may be stored in this variable, NULL if INACCESSIBLE, FUTURE_TYPE if pending + VariableBinding *vb; // The variable definition node, to resolve future types js2val value; // This variable's current value; future if the variable has not been declared yet; // uninitialised if the variable must be written before it can be read bool immutable; // true if this variable's value may not be changed once set @@ -242,21 +253,21 @@ public: class HoistedVar : public StaticMember { public: - HoistedVar() : StaticMember(StaticMember::HoistedVariable), value(JS2VAL_VOID), hasFunctionInitializer(false) { } + HoistedVar() : StaticMember(Member::HoistedVariable), value(JS2VAL_VOID), hasFunctionInitializer(false) { } js2val value; // This variable's current value bool hasFunctionInitializer; // true if this variable was created by a function statement }; class ConstructorMethod : public StaticMember { public: - ConstructorMethod() : StaticMember(StaticMember::ConstructorMethod), code(NULL) { } + ConstructorMethod() : StaticMember(Member::ConstructorMethod), code(NULL) { } Invokable *code; // This function itself (a callable object) }; class Accessor : public StaticMember { public: - Accessor() : StaticMember(StaticMember::Accessor), type(NULL), code(NULL) { } + Accessor() : StaticMember(Member::Accessor), type(NULL), code(NULL) { } JS2Class *type; // The type of the value read from the getter or written into the setter Invokable *code; // calling this object does the read or write @@ -292,14 +303,12 @@ public: StaticMember *content; // The member to which this qualified name was bound }; -class InstanceMember { +class InstanceMember : public Member { public: - enum InstanceMemberKind { InstanceVariableKind, InstanceMethodKind, InstanceAccessorKind }; + InstanceMember(MemberKind kind, JS2Class *type, bool final) : Member(kind), type(type), final(final) { } - InstanceMember(InstanceMemberKind kind, bool final) : kind(kind), final(final) { } - - InstanceMemberKind kind; - bool final; // true if this member may not be overridden in subclasses + 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 @@ -308,8 +317,7 @@ public: class InstanceVariable : public InstanceMember { public: - InstanceVariable(bool immutable, bool final, uint32 slotIndex) : InstanceMember(InstanceVariableKind, final), immutable(immutable), slotIndex(slotIndex) { } - JS2Class *type; // Type of values that may be stored in this variable + InstanceVariable(JS2Class *type, bool immutable, bool final, uint32 slotIndex) : InstanceMember(InstanceVariableKind, type, final), immutable(immutable), slotIndex(slotIndex) { } 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 @@ -317,15 +325,14 @@ public: class InstanceMethod : public InstanceMember { public: - InstanceMethod() : InstanceMember(InstanceMethodKind, false) { } + InstanceMethod() : InstanceMember(InstanceMethodKind, NULL, false) { } Signature type; // This method's signature Invokable *code; // This method itself (a callable object); null if this method is abstract }; class InstanceAccessor : public InstanceMember { public: - InstanceAccessor(Invokable *code, bool final) : InstanceMember(InstanceAccessorKind, final), code(code) { } - JS2Class *type; // The type of the value read from the getter or written into the setter + InstanceAccessor(Invokable *code, JS2Class *type, bool final) : InstanceMember(InstanceAccessorKind, type, final), code(code) { } Invokable *code; // A callable object which does the read or write; null if this method is abstract }; @@ -338,7 +345,7 @@ public: }; // Override status is used to resolve overriden definitions for instance members -#define PotentialConflict ((InstanceMember *)(-1)) +#define POTENTIAL_CONFLICT ((InstanceMember *)(-1)) class OverrideStatus { public: OverrideStatus(InstanceMember *overriddenMember, const StringAtom &name) @@ -667,13 +674,14 @@ public: void ValidateStmtList(Context *cxt, Environment *env, StmtNode *p); - void ValidateTypeExpression(ExprNode *e); + void ValidateTypeExpression(Context *cxt, Environment *env, ExprNode *e) { ValidateExpression(cxt, env, e); } void ValidateStmt(Context *cxt, Environment *env, StmtNode *p); void ValidateExpression(Context *cxt, Environment *env, ExprNode *p); void ValidateAttributeExpression(Context *cxt, Environment *env, ExprNode *p); js2val ExecuteStmtList(Phase phase, StmtNode *p); js2val EvalExpression(Environment *env, Phase phase, ExprNode *p); + JS2Class *EvalTypeExpression(Environment *env, Phase phase, ExprNode *p); Reference *EvalExprNode(Environment *env, Phase phase, ExprNode *p); Attribute *EvalAttributeExpression(Environment *env, Phase phase, ExprNode *p); void EvalStmt(Environment *env, Phase phase, StmtNode *p); @@ -685,13 +693,14 @@ public: InstanceBinding *resolveInstanceMemberName(JS2Class *js2class, Multiname *multiname, Access access, Phase phase); void defineHoistedVar(Environment *env, const StringAtom &id, StmtNode *p); - void defineStaticMember(Environment *env, const StringAtom &id, NamespaceList *namespaces, Attribute::OverrideModifier overrideMod, bool xplicit, Access access, StaticMember *m, size_t pos); - OverrideStatusPair defineInstanceMember(JS2Class *c, Context *cxt, const StringAtom &id, NamespaceList *namespaces, Attribute::OverrideModifier overrideMod, bool xplicit, Access access, InstanceMember *m, size_t pos); + Multiname *defineStaticMember(Environment *env, const StringAtom &id, NamespaceList *namespaces, Attribute::OverrideModifier overrideMod, bool xplicit, Access access, StaticMember *m, size_t pos); + OverrideStatusPair *defineInstanceMember(JS2Class *c, Context *cxt, const StringAtom &id, NamespaceList *namespaces, Attribute::OverrideModifier overrideMod, bool xplicit, Access access, InstanceMember *m, size_t pos); OverrideStatus *resolveOverrides(JS2Class *c, Context *cxt, const StringAtom &id, NamespaceList *namespaces, Access access, bool expectMethod, size_t pos); OverrideStatus *searchForOverrides(JS2Class *c, const StringAtom &id, NamespaceList *namespaces, Access access, size_t pos); InstanceMember *findInstanceMember(JS2Class *c, QualifiedName *qname, Access access); Slot *findSlot(js2val thisObjVal, InstanceVariable *id); bool findStaticMember(JS2Class *c, Multiname *multiname, Access access, Phase phase, MemberDescriptor *result); + JS2Class *getVariableType(Variable *v, Phase phase, size_t pos); bool readProperty(js2val container, Multiname *multiname, LookupKind *lookupKind, Phase phase, js2val *rval); diff --git a/js2/src/js2value.h b/js2/src/js2value.h index bf682605c92..d07ec0dff04 100644 --- a/js2/src/js2value.h +++ b/js2/src/js2value.h @@ -51,6 +51,16 @@ #define JS2VAL_IS_INITIALIZED(v) (v != JS2VAL_UNINITIALIZED) #define JS2VAL_IS_UNINITIALIZED(v) (v == JS2VAL_UNINITIALIZED) +#define JS2VAL_INACCESSIBLE 0x90 /* reserve this object reference value as an indication + that a variable has yet to become available */ +#define JS2VAL_IS_ACCESSIBLE(v) (v != JS2VAL_INACCESSIBLE) +#define JS2VAL_IS_INACCESSIBLE(v) (v == JS2VAL_INACCESSIBLE) + +#define JS2VAL_FUTUREVALUE 0xA0 /* reserve this object reference value as an indication + that a variable has to have it's initializer run */ +#define JS2VAL_IS_FUTURE(v) (v == JS2VAL_FUTUREVALUE) + + /* Type tag bitfield length and derived macros. */ #define JS2VAL_TAGBITS 3 #define JS2VAL_TAGMASK JS2_BITMASK(JS2VAL_TAGBITS) diff --git a/js2/src/parser.h b/js2/src/parser.h index 8163e623ac9..c6e733ca074 100644 --- a/js2/src/parser.h +++ b/js2/src/parser.h @@ -60,6 +60,10 @@ namespace JavaScript { namespace MetaData { class Context; class JS2Class; + class Member; + class Multiname; + class OverrideStatus; + typedef std::pair OverrideStatusPair; } #endif @@ -141,6 +145,11 @@ namespace JavaScript { JS2Runtime::Property *prop; // the sematics/codegen passes stuff their data in here. JS2Runtime::JSObject *scope; // ditto #endif +#ifdef EPIMETHEUS + MetaData::Member *member; + MetaData::Multiname *mn; + MetaData::OverrideStatusPair *osp; +#endif VariableBinding(size_t pos, const StringAtom *name, ExprNode *type, ExprNode *initializer, bool constant): ParseNode(pos), next(0), name(name), type(type), initializer(initializer), constant(constant) {}