From 4f0cf5d56ed42d74c5be0a18c568a976102ff87d Mon Sep 17 00:00:00 2001 From: "rogerl%netscape.com" Date: Thu, 5 Sep 2002 00:26:34 +0000 Subject: [PATCH] WriteProperty work-in-progress (broken!) --- js2/src/exception.cpp | 3 +- js2/src/exception.h | 3 +- js2/src/js2engine.h | 3 + js2/src/js2metadata.cpp | 132 +++++++++++++++++++++++++-------------- js2/src/js2metadata.h | 15 +++-- js2/src/js2op_access.cpp | 22 ++++++- js2/src/js2value.h | 1 + 7 files changed, 122 insertions(+), 57 deletions(-) diff --git a/js2/src/exception.cpp b/js2/src/exception.cpp index f44e3df13ef..242dc4e400a 100644 --- a/js2/src/exception.cpp +++ b/js2/src/exception.cpp @@ -54,7 +54,8 @@ static const char *const kindStrings[] = { "User exception", // 'throw' from user code "Definition error", // a monkey is a small cup of milk "Bad Value error", // bad value, no biscuit - "Compile Expression error" // invalid compile-time execution + "Compile expression error", // invalid compile-time execution + "Uninitialized error" // read before write }; // Return a null-terminated string describing the exception's kind. diff --git a/js2/src/exception.h b/js2/src/exception.h index 53ed22ee5f6..9e0ff5076ba 100644 --- a/js2/src/exception.h +++ b/js2/src/exception.h @@ -60,7 +60,8 @@ namespace JavaScript definitionError, badValueError, compileExpressionError, - propertyAccessError + propertyAccessError, + uninitializedError }; Kind kind; // The exception's kind diff --git a/js2/src/js2engine.h b/js2/src/js2engine.h index b1253747e63..23158709304 100644 --- a/js2/src/js2engine.h +++ b/js2/src/js2engine.h @@ -108,6 +108,9 @@ public: float64 toNumber(js2val x) { if (JS2VAL_IS_INT(x)) return JS2VAL_TO_INT(x); else if (JS2VAL_IS_DOUBLE(x)) return *JS2VAL_TO_DOUBLE(x); else return convertValueToDouble(x); } bool toBoolean(js2val x) { if (JS2VAL_IS_BOOLEAN(x)) return JS2VAL_TO_BOOLEAN(x); else return convertValueToBoolean(x); } + + js2val assignmentConversion(js2val val, JS2Class *type) { return val; } // XXX s'more code, please + uint8 *pc; BytecodeContainer *bCon; JS2Metadata *meta; diff --git a/js2/src/js2metadata.cpp b/js2/src/js2metadata.cpp index e98d9c88371..fe8ae22e28b 100644 --- a/js2/src/js2metadata.cpp +++ b/js2/src/js2metadata.cpp @@ -159,30 +159,17 @@ namespace MetaData { // the type and the value are 'future' } break; - case Attribute::Abstract: case Attribute::Virtual: case Attribute::Final: { JS2Class *c = checked_cast(env->getTopFrame()); - InstanceMember *m = NULL; - switch (memberMod) { - case Attribute::Abstract: - if (v->initializer) - reportError(Exception::syntaxError, "Abstract member may not have initializer", p->pos); - m = new InstanceAccessor(NULL, false); - break; - case Attribute::Virtual: - m = new InstanceVariable(immutable, false); - c->slotCount++; - break; - case Attribute::Final: - m = new InstanceVariable(immutable, true); - c->slotCount++; - break; - } + InstanceMember *m = new InstanceVariable(immutable, (memberMod == Attribute::Final), c->slotCount++); defineInstanceMember(c, cxt, *name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, m, p->pos); } break; + default: + reportError(Exception::definitionError, "Illegal attribute", p->pos); + break; } } @@ -1219,13 +1206,13 @@ doBinary: else writeStatus = new OverrideStatus(NULL, id); - if ((!readStatus->overriddenMember && (readStatus->overriddenMember != PotentialConflict)) - || (!writeStatus->overriddenMember && (writeStatus->overriddenMember != PotentialConflict))) { + if ((readStatus->overriddenMember && (readStatus->overriddenMember != PotentialConflict)) + || (writeStatus->overriddenMember && (writeStatus->overriddenMember != PotentialConflict))) { if ((overrideMod != Attribute::DoOverride) && (overrideMod != Attribute::OverrideUndefined)) reportError(Exception::definitionError, "Illegal override", pos); } else { - if ((readStatus->overriddenMember = PotentialConflict) || (writeStatus->overriddenMember == PotentialConflict)) { + if ((readStatus->overriddenMember == PotentialConflict) || (writeStatus->overriddenMember == PotentialConflict)) { if ((overrideMod != Attribute::DontOverride) && (overrideMod != Attribute::OverrideUndefined)) reportError(Exception::definitionError, "Illegal override", pos); } @@ -1411,7 +1398,7 @@ doBinary: return false; // 'None' } - bool JS2Metadata::writeDynamicProperty(Frame *container, Multiname *multiname, bool createIfMissing, js2val newValue, Phase phase) + bool JS2Metadata::writeDynamicProperty(JS2Object *container, Multiname *multiname, bool createIfMissing, js2val newValue, Phase phase) { ASSERT(container && ((container->kind == DynamicInstanceKind) || (container->kind == GlobalObjectKind) @@ -1533,14 +1520,8 @@ readClassProperty: case PackageKind: case FunctionKind: case BlockKind: - { - StaticMember *m = findFlatMember(checked_cast(container), multiname, ReadAccess, phase); - if (!m && (container->kind == GlobalObjectKind)) - return readDynamicProperty(container, multiname, lookupKind, phase, rval); - else - return readStaticMember(m, phase, rval); - } - break; + return readProperty(checked_cast(container), multiname, lookupKind, ReadAccess, phase); + case ClassKind: { MemberDescriptor m2; @@ -1559,6 +1540,7 @@ readClassProperty: case PrototypeInstanceKind: return readDynamicProperty(container, multiname, lookupKind, phase, rval); + default: ASSERT(false); return false; @@ -1571,22 +1553,10 @@ readClassProperty: && ((JS2VAL_TO_OBJECT(thisObjVal)->kind == DynamicInstanceKind) || (JS2VAL_TO_OBJECT(thisObjVal)->kind == FixedInstanceKind))); JS2Object *thisObj = JS2VAL_TO_OBJECT(thisObjVal); - Slot *s; - uint32 slotCount; - if (thisObj->kind == DynamicInstanceKind) { - s = checked_cast(thisObj)->slots; - slotCount = checked_cast(thisObj)->type->slotCount; - } - else { - s = checked_cast(thisObj)->slots; - slotCount = checked_cast(thisObj)->type->slotCount; - } - for (uint32 i = 0; i < slotCount; i++) { - if (s[i].id == id) - return &s[i]; - } - ASSERT(false); - return NULL; + if (thisObj->kind == DynamicInstanceKind) + return &checked_cast(thisObj)->slots[id->slotIndex]; + else + return &checked_cast(thisObj)->slots[id->slotIndex]; } @@ -1601,7 +1571,8 @@ readClassProperty: if ((phase == CompilePhase) && !mv->immutable) reportError(Exception::compileExpressionError, "Inappropriate compile time expression", engine->errorPos()); Slot *s = findSlot(containerVal, mv); - + if (JS2VAL_IS_UNINITIALIZED(s->value)) + reportError(Exception::uninitializedError, "Reference to uninitialized instance variable", engine->errorPos()); *rval = s->value; return true; } @@ -1626,6 +1597,75 @@ readClassProperty: return readStaticMember(m, phase, rval); } + // Write the value of a property in the container. Return true/false if that container has + // the property or not. + bool JS2Metadata::writeProperty(js2val containerVal, Multiname *multiname, LookupKind *lookupKind, bool createIfMissing, js2val newValue, Phase phase) + { + if (JS2VAL_IS_PRIMITIVE(containerVal)) + return false; + JS2Object *container = JS2VAL_TO_OBJECT(containerVal); + switch (container->kind) { + case AttributeObjectKind: + case MultinameKind: + return false; + + case FixedInstanceKind: + case DynamicInstanceKind: + { + InstanceBinding *ib = resolveInstanceMemberName(objectType(containerVal), multiname, WriteAccess, phase); + if ((ib == NULL) && (container->kind == DynamicInstanceKind)) + return writeDynamicProperty(container, multiname, createIfMissing, newValue, phase); + else + return writeInstanceMember(); + } + + case SystemKind: + case GlobalObjectKind: + case PackageKind: + case FunctionKind: + case BlockKind: + return writeProperty(checked_cast(container), multiname, lookupKind, createIfMissing, newValue, phase); + + case ClassKind: + break; + + case PrototypeInstanceKind: + return writeDynamicProperty(container, multiname, createIfMissing, newValue, phase); + + default: + ASSERT(false); + return false; + } + + } + + bool JS2Metadata::writeInstanceMember(JS2Object *thisObj, JS2Class *c, QualifiedName *qname, js2val newValue, Phase phase) + { + if (phase == CompilePhase) + reportError(Exception::compileExpressionError, "Inappropriate compile time expression", engine->errorPos()); + InstanceMember *m = findInstanceMember(c, qname, WriteAccess); + if (m == NULL) return false; + switch (m->kind) { + case InstanceMember::InstanceVariableKind: + { + InstanceVariable *mv = checked_cast(m); + Slot *s = findSlot(containerVal, mv); + if (mv->immutable && JS2VAL_IS_INITIALIZED(s->value)) + reportError(Exception::propertyAccessError, "Reinitialization of constant", engine->errorPos()); + s->value = engine->assignmentConversion(newValue, mv->type); + return true; + } + + case InstanceMember::InstanceMethodKind: + case InstanceMember::InstanceAccessorKind: + reportError(Exception::propertyAccessError, "Attempt to write to a method", engine->errorPos()); + break; + } + ASSERT(false); + return false; + + } + // Write the value of a property in the frame. Return true/false if that frame has // the property or not. bool JS2Metadata::writeProperty(Frame *container, Multiname *multiname, LookupKind *lookupKind, bool createIfMissing, js2val newValue, Phase phase) diff --git a/js2/src/js2metadata.h b/js2/src/js2metadata.h index 62a765d6570..b092bfa314b 100644 --- a/js2/src/js2metadata.h +++ b/js2/src/js2metadata.h @@ -134,7 +134,7 @@ public: class Attribute : public JS2Object { public: enum AttributeKind { TrueAttr, FalseAttr, NamespaceAttr, CompoundAttr }; - enum MemberModifier { NoModifier, Static, Constructor, Operator, Abstract, Virtual, Final}; + enum MemberModifier { NoModifier, Static, Constructor, Abstract, Virtual, Final}; enum OverrideModifier { NoOverride, DoOverride, DontOverride, OverrideUndefined }; @@ -308,10 +308,11 @@ public: class InstanceVariable : public InstanceMember { public: - InstanceVariable(bool immutable, bool final) : InstanceMember(InstanceVariableKind, final), immutable(immutable) { } + 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 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 }; class InstanceMethod : public InstanceMember { @@ -418,7 +419,8 @@ public: // A SLOT record describes the value of one fixed property of one instance. class Slot { public: - InstanceVariable *id; // The instance variable whose value this slot carries +// We keep the slotIndex in the InstanceVariable rather than go looking for a specific id +// InstanceVariable *id; // The instance variable whose value this slot carries js2val value; // This fixed property's current value; uninitialised if the fixed property is an uninitialised constant }; @@ -509,8 +511,8 @@ public: Multiname *propertyMultiname; // A nonempty set of qualified names to which this reference can refer (b // qualified with the namespace q or all currently open namespaces in the // example above) - virtual void emitReadBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eDotRead, pos); } - virtual void emitWriteBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eDotWrite, pos); } + virtual void emitReadBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eDotRead, pos); bCon->addMultiname(propertyMultiname); } + virtual void emitWriteBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eDotWrite, pos); bCon->addMultiname(propertyMultiname); } }; @@ -699,8 +701,9 @@ public: bool readInstanceMember(js2val containerVal, JS2Class *c, QualifiedName *qname, Phase phase, js2val *rval); + bool writeProperty(js2val container, Multiname *multiname, LookupKind *lookupKind, bool createIfMissing, js2val newValue, Phase phase); bool writeProperty(Frame *container, Multiname *multiname, LookupKind *lookupKind, bool createIfMissing, js2val newValue, Phase phase); - bool writeDynamicProperty(Frame *container, Multiname *multiname, bool createIfMissing, js2val newValue, Phase phase); + bool writeDynamicProperty(JS2Object *container, Multiname *multiname, bool createIfMissing, js2val newValue, Phase phase); bool writeStaticMember(StaticMember *m, js2val newValue, Phase phase); diff --git a/js2/src/js2op_access.cpp b/js2/src/js2op_access.cpp index 0658aa9606d..19689828aa6 100644 --- a/js2/src/js2op_access.cpp +++ b/js2/src/js2op_access.cpp @@ -31,6 +31,7 @@ * file under either the NPL or the GPL. */ + // Read a multiname property from a base object, push the value onto the stack case eDotRead: { LookupKind lookup(false, NULL); @@ -42,7 +43,22 @@ } break; - // Pop a multiname object and read it's value from the environment on to the stack. + // Write the top value to a multiname property in a base object, leave + // the value on the stack top + case eDotWrite: + { + retval = pop(); + LookupKind lookup(false, NULL); + Multiname *mn = bCon->mMultinameList[BytecodeContainer::getShort(pc)]; + pc += sizeof(short); + js2val baseVal = pop(); + if (!meta->readProperty(baseVal, mn, &lookup, RunPhase, &retval)) + meta->reportError(Exception::propertyAccessError, "No property named {0}", errorPos(), mn->name); + push(retval); + } + break; + + // Read the multiname from the current environment, push it's value on the stack case eLexicalRead: { Multiname *mn = bCon->mMultinameList[BytecodeContainer::getShort(pc)]; @@ -52,14 +68,14 @@ } break; - // Pop a value and a multiname. Write the value to the multiname in the environment, leave + // Write the top value to the multiname in the environment, leave // the value on the stack top. case eLexicalWrite: { + retval = top(); Multiname *mn = bCon->mMultinameList[BytecodeContainer::getShort(pc)]; pc += sizeof(short); meta->env.lexicalWrite(meta, mn, retval, true, phase); - push(retval); } break; diff --git a/js2/src/js2value.h b/js2/src/js2value.h index 466dc1f2cb6..bf682605c92 100644 --- a/js2/src/js2value.h +++ b/js2/src/js2value.h @@ -49,6 +49,7 @@ #define JS2VAL_UNINITIALIZED 0x80 /* reserve this object reference value as an indication that a variable has yet to be initialized */ #define JS2VAL_IS_INITIALIZED(v) (v != JS2VAL_UNINITIALIZED) +#define JS2VAL_IS_UNINITIALIZED(v) (v == JS2VAL_UNINITIALIZED) /* Type tag bitfield length and derived macros. */ #define JS2VAL_TAGBITS 3