Variable initialization support.

This commit is contained in:
rogerl%netscape.com 2002-09-07 00:33:31 +00:00
parent f78a02f328
commit 11229c4920
4 changed files with 180 additions and 56 deletions

View File

@ -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<JS2Class *>(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<VariableStmtNode *>(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<VariableStmtNode *>(p);
VariableBinding *vb = vs->bindings;
while (vb) {
if (vb->member) {
if (vb->member->kind == Member::Variable) {
Variable *v = checked_cast<Variable *>(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 <v->mn>
// eIS_INACCESSIBLE
// eBRANCH_FALSE <lbl>
// eGET_TOP_FRAME
// <vb->initializer code>
// <convert to 'type'>
// eDotWrite <v->mn>
// <lbl>:
}
v = v->next;
}
}
else {
ASSERT(vb->member->kind == Member::InstanceVariableKind);
InstanceVariable *v = checked_cast<InstanceVariable *>(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<JS2Class *>(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<FixedInstance *>(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);

View File

@ -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);

View File

@ -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)

View File

@ -60,6 +60,10 @@ namespace JavaScript {
namespace MetaData {
class Context;
class JS2Class;
class Member;
class Multiname;
class OverrideStatus;
typedef std::pair<OverrideStatus *, OverrideStatus *> 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) {}