mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Labor day progress. DotReference, class instance variable definition and
associated oevrride resolution support. Branch logic support.
This commit is contained in:
parent
89f954057e
commit
ff6ac62d63
@ -60,14 +60,34 @@
|
||||
namespace JavaScript {
|
||||
namespace MetaData {
|
||||
|
||||
|
||||
|
||||
BytecodeContainer::~BytecodeContainer()
|
||||
{
|
||||
for (std::vector<Multiname *>::iterator i = mMultinameList.begin(), end = mMultinameList.end(); (i != end); i++)
|
||||
delete *i;
|
||||
// Establish the label's location in a bytecode container
|
||||
void Label::setLocation(BytecodeContainer *bCon, uint32 location)
|
||||
{
|
||||
mHasLocation = true;
|
||||
mLocation = location;
|
||||
for (std::vector<uint32>::iterator i = mFixupList.begin(), end = mFixupList.end();
|
||||
(i != end); i++)
|
||||
{
|
||||
uint32 branchLocation = *i;
|
||||
bCon->setOffset(branchLocation, int32(mLocation - branchLocation));
|
||||
}
|
||||
}
|
||||
|
||||
// Add a branch location to the list of fixups for this label
|
||||
// (or resolve the branch if the location is known already)
|
||||
void Label::addFixup(BytecodeContainer *bCon, uint32 branchLocation)
|
||||
{
|
||||
if (mHasLocation)
|
||||
bCon->addOffset(int32(mLocation - branchLocation));
|
||||
else {
|
||||
mFixupList.push_back(branchLocation);
|
||||
bCon->addLong(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Look up the pc and return a position from the map
|
||||
size_t BytecodeContainer::getPosition(uint16 pc)
|
||||
{
|
||||
for (std::vector<MapEntry>::iterator i = pcMap.begin(), end = pcMap.end(); (i != end); i++)
|
||||
@ -76,6 +96,57 @@ namespace MetaData {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// insert the opcode, marking it's position in the pcmap and
|
||||
// adjusting the stack as appropriate.
|
||||
void BytecodeContainer::emitOp(JS2Op op, size_t pos)
|
||||
{
|
||||
adjustStack(op);
|
||||
addByte((uint8)op);
|
||||
pcMap.push_back(MapEntry(mBuffer.size(), pos));
|
||||
}
|
||||
|
||||
// insert the opcode, marking it's position in the pcmap and
|
||||
// adjusting the stack as supplied.
|
||||
void BytecodeContainer::emitOp(JS2Op op, size_t pos, int32 effect)
|
||||
{
|
||||
adjustStack(op, effect);
|
||||
addByte((uint8)op);
|
||||
pcMap.push_back(std::pair<uint16, size_t>(mBuffer.size(), pos));
|
||||
}
|
||||
|
||||
// Track the high-water mark for the stack
|
||||
// and watch for a bad negative stack
|
||||
void BytecodeContainer::adjustStack(JS2Op op, int32 effect)
|
||||
{
|
||||
mStackTop += effect;
|
||||
if (mStackTop > mStackMax)
|
||||
mStackMax = mStackTop;
|
||||
ASSERT(mStackTop >= 0);
|
||||
}
|
||||
|
||||
// get a new label
|
||||
BytecodeContainer::LabelID BytecodeContainer::getLabel()
|
||||
{
|
||||
LabelID result = mLabelList.size();
|
||||
mLabelList.push_back(Label());
|
||||
return result;
|
||||
}
|
||||
// set the current pc as needing a fixup to a label
|
||||
void BytecodeContainer::addFixup(LabelID label)
|
||||
{
|
||||
ASSERT(label < mLabelList.size());
|
||||
mLabelList[label].addFixup(this, mBuffer.size());
|
||||
}
|
||||
// set the current pc as the position for a label
|
||||
void BytecodeContainer::setLabel(LabelID label)
|
||||
{
|
||||
ASSERT(label < mLabelList.size());
|
||||
mLabelList[label].setLocation(this, mBuffer.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,40 @@
|
||||
namespace JavaScript {
|
||||
namespace MetaData {
|
||||
|
||||
class BytecodeContainer;
|
||||
|
||||
class Label {
|
||||
public:
|
||||
|
||||
typedef enum { InternalLabel, NamedLabel, BreakLabel, ContinueLabel } LabelKind;
|
||||
|
||||
Label() : mKind(InternalLabel), mHasLocation(false) { }
|
||||
Label(LabelStmtNode *lbl) : mKind(NamedLabel), mHasLocation(false), mLabelStmt(lbl) { }
|
||||
Label(LabelKind kind) : mKind(kind), mHasLocation(false) { }
|
||||
|
||||
bool matches(const StringAtom *name)
|
||||
{
|
||||
return ((mKind == NamedLabel) && (mLabelStmt->name.compare(*name) == 0));
|
||||
}
|
||||
|
||||
bool matches(LabelKind kind)
|
||||
{
|
||||
return (mKind == kind);
|
||||
}
|
||||
|
||||
void addFixup(BytecodeContainer *bcg, uint32 branchLocation);
|
||||
void setLocation(BytecodeContainer *bcg, uint32 location);
|
||||
|
||||
std::vector<uint32> mFixupList;
|
||||
|
||||
LabelKind mKind;
|
||||
bool mHasLocation;
|
||||
LabelStmtNode *mLabelStmt;
|
||||
|
||||
uint32 mLocation;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Multiname;
|
||||
class Frame;
|
||||
@ -54,22 +88,25 @@ class Frame;
|
||||
class BytecodeContainer {
|
||||
public:
|
||||
BytecodeContainer() : mStackTop(0), mStackMax(0) { }
|
||||
BytecodeContainer::~BytecodeContainer() ;
|
||||
BytecodeContainer::~BytecodeContainer() { }
|
||||
|
||||
|
||||
uint8 *getCodeStart() { return mBuffer.begin(); }
|
||||
|
||||
typedef std::pair<uint16, size_t> MapEntry;
|
||||
std::vector<MapEntry> pcMap;
|
||||
typedef uint32 LabelID;
|
||||
|
||||
size_t getPosition(uint16 pc);
|
||||
|
||||
void emitOp(JS2Op op, size_t pos) { adjustStack(op); addByte((uint8)op); pcMap.push_back(MapEntry(mBuffer.size(), pos)); }
|
||||
void emitOp(JS2Op op, size_t pos, int32 effect)
|
||||
{ adjustStack(op, effect); addByte((uint8)op); pcMap.push_back(std::pair<uint16, size_t>(mBuffer.size(), pos)); }
|
||||
void emitOp(JS2Op op, size_t pos);
|
||||
void emitOp(JS2Op op, size_t pos, int32 effect);
|
||||
|
||||
void emitBranch(JS2Op op, LabelID tgt, size_t pos)
|
||||
{ emitOp(op, pos); addFixup(tgt); }
|
||||
|
||||
void adjustStack(JS2Op op) { adjustStack(op, JS2Engine::getStackEffect(op)); }
|
||||
void adjustStack(JS2Op op, int32 effect){ mStackTop += effect; if (mStackTop > mStackMax) mStackMax = mStackTop; ASSERT(mStackTop >= 0); }
|
||||
void adjustStack(JS2Op op, int32 effect);
|
||||
|
||||
void addByte(uint8 v) { mBuffer.push_back(v); }
|
||||
|
||||
@ -89,12 +126,15 @@ public:
|
||||
|
||||
void addFrame(Frame *f) { mFrameList.push_back(f); addShort(mFrameList.size() - 1); }
|
||||
|
||||
void addOffset(int32 v) { mBuffer.insert(mBuffer.end(), (uint8 *)&v, (uint8 *)(&v) + sizeof(int32)); }
|
||||
void setOffset(uint32 index, int32 v) { *((int32 *)(mBuffer.begin() + index)) = v; }
|
||||
|
||||
void addString(const StringAtom &x, size_t pos) { emitOp(eString, pos); addPointer(&x); }
|
||||
void addString(String &x, size_t pos) { emitOp(eString, pos); addPointer(&x); }
|
||||
void addString(String *x, size_t pos) { emitOp(eString, pos); addPointer(x); }
|
||||
static String *getString(void *pc) { return (String *)getPointer(pc); }
|
||||
// XXX We lose StringAtom here - is there anyway of stashing these in a bytecodecontainer?
|
||||
// XXX We lose StringAtom here (and is it safe to stash the address of a StringAtom?)
|
||||
// - is there any way of keeping StringAtoms themselves in a bytecodeContainer?
|
||||
|
||||
typedef std::vector<uint8> CodeBuffer;
|
||||
|
||||
@ -102,9 +142,14 @@ public:
|
||||
std::vector<Multiname *> mMultinameList; // gc tracking
|
||||
std::vector<Frame *> mFrameList; // gc tracking
|
||||
|
||||
int32 mStackTop; // keep these as signed so as to
|
||||
int32 mStackMax; // track if they go negative.
|
||||
int32 mStackTop; // keep these as signed so as to...
|
||||
int32 mStackMax; // ...track if they go negative.
|
||||
|
||||
std::vector<Label> mLabelList;
|
||||
|
||||
LabelID getLabel();
|
||||
void addFixup(LabelID label);
|
||||
void setLabel(LabelID label);
|
||||
};
|
||||
|
||||
|
||||
|
@ -44,11 +44,6 @@
|
||||
#include "bytecodecontainer.h"
|
||||
#include "js2metadata.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "tracer.h"
|
||||
#include "collector.h"
|
||||
#endif
|
||||
|
||||
#if defined(XP_MAC) && !defined(XP_MAC_MPW)
|
||||
#include <SIOUX.h>
|
||||
#include <MacTypes.h>
|
||||
|
@ -80,6 +80,7 @@ js2val JS2Engine::interpreterLoop()
|
||||
#include "js2op_invocation.cpp"
|
||||
#include "js2op_access.cpp"
|
||||
#include "js2op_literal.cpp"
|
||||
#include "js2op_flowcontrol.cpp"
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
@ -200,6 +201,27 @@ float64 JS2Engine::convertValueToDouble(js2val x)
|
||||
|
||||
}
|
||||
|
||||
// x is not a bool
|
||||
bool JS2Engine::convertValueToBoolean(js2val x)
|
||||
{
|
||||
if (JS2VAL_IS_UNDEFINED(x))
|
||||
return false;
|
||||
if (JS2VAL_IS_NULL(x))
|
||||
return false;
|
||||
if (JS2VAL_IS_INT(x))
|
||||
return (JS2VAL_TO_INT(x) != 0);
|
||||
if (JS2VAL_IS_DOUBLE(x)) {
|
||||
float64 *xd = JS2VAL_TO_DOUBLE(x);
|
||||
return ! (JSDOUBLE_IS_POSZERO(*xd) || JSDOUBLE_IS_NEGZERO(*xd) || JSDOUBLE_IS_NaN(*xd));
|
||||
}
|
||||
if (JS2VAL_IS_STRING(x)) {
|
||||
String *str = JS2VAL_TO_STRING(x);
|
||||
return (str->length() != 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#define INIT_STRINGATOM(n) n##_StringAtom(world.identifiers[#n])
|
||||
|
||||
JS2Engine::JS2Engine(World &world)
|
||||
@ -226,28 +248,47 @@ int JS2Engine::getStackEffect(JS2Op op)
|
||||
{
|
||||
switch (op) {
|
||||
case eReturn:
|
||||
case ePlus:
|
||||
return -1;
|
||||
return -1;
|
||||
|
||||
case eAdd: // pop two, push one
|
||||
case eSubtract:
|
||||
return -1;
|
||||
|
||||
case eString:
|
||||
case eTrue:
|
||||
case eFalse:
|
||||
case eNumber:
|
||||
return 1;
|
||||
return 1; // push literal value
|
||||
|
||||
case eLexicalRead:
|
||||
return 0; // consumes a multiname, pushes the value
|
||||
return 1; // push the value
|
||||
case eLexicalWrite:
|
||||
return -2; // consumes a multiname and the value
|
||||
return -1; // pop the value
|
||||
|
||||
case eDotRead:
|
||||
return 0; // pop a base, push the value
|
||||
case eDotWrite:
|
||||
return -2; // pop a base and the value
|
||||
|
||||
case eReturnVoid:
|
||||
case eBranch:
|
||||
return 0;
|
||||
|
||||
case eToBoolean: // pop object, push boolean
|
||||
return 0;
|
||||
|
||||
case eMultiname:
|
||||
return 1; // push the multiname object
|
||||
|
||||
case ePushFrame:
|
||||
case ePopFrame:
|
||||
case ePushFrame: // affect the frame stack...
|
||||
case ePopFrame: // ...not the exec stack
|
||||
return 0;
|
||||
|
||||
case eBranchFalse:
|
||||
case eBranchTrue:
|
||||
return -1; // pop the boolean condition
|
||||
|
||||
case eNew: // pop the class or function, push the new instance
|
||||
return 0;
|
||||
|
||||
default:
|
||||
@ -261,5 +302,22 @@ size_t JS2Engine::errorPos()
|
||||
return bCon->getPosition(pc - bCon->getCodeStart());
|
||||
}
|
||||
|
||||
JS2Object *JS2Engine::defaultConstructor(JS2Engine *engine)
|
||||
{
|
||||
js2val v = engine->pop();
|
||||
ASSERT(JS2VAL_IS_OBJECT(v) && !JS2VAL_IS_NULL(v));
|
||||
JS2Object *obj = JS2VAL_TO_OBJECT(v);
|
||||
ASSERT(obj->kind == ClassKind);
|
||||
JS2Class *c = checked_cast<JS2Class *>(obj);
|
||||
if (c->dynamic)
|
||||
return new DynamicInstance(c);
|
||||
else
|
||||
if (c->prototype)
|
||||
return new PrototypeInstance(NULL);
|
||||
else
|
||||
return new FixedInstance(c);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -49,23 +49,30 @@ namespace MetaData {
|
||||
|
||||
|
||||
enum JS2Op {
|
||||
ePlus,
|
||||
eAdd,
|
||||
eSubtract,
|
||||
eTrue,
|
||||
eFalse,
|
||||
eNumber,
|
||||
eString, // <string pointer>
|
||||
eObject, // <named argument count>
|
||||
eLexicalRead,
|
||||
eLexicalWrite,
|
||||
eString, // <string pointer:u32>
|
||||
eNewObject, // <argCount:u16>
|
||||
eLexicalRead, // <multiname index:u16>
|
||||
eLexicalWrite, // <multiname index:u16>
|
||||
eDotRead, // <multiname index:u16>
|
||||
eDotWrite, // <multiname index:u16>
|
||||
eReturn,
|
||||
eReturnVoid,
|
||||
eNewObject, // <argCount:16>
|
||||
eMultiname, // <multiname index>
|
||||
ePushFrame, // <frame index>
|
||||
ePopFrame
|
||||
ePushFrame, // <frame index:u16>
|
||||
ePopFrame,
|
||||
eToBoolean,
|
||||
eBranchFalse, // <branch displacement:s32> XXX save space with short and long versions instead ?
|
||||
eBranchTrue, // <branch displacement:s32>
|
||||
eBranch, // <branch displacement:s32>
|
||||
eNew
|
||||
};
|
||||
|
||||
|
||||
class JS2Object;
|
||||
class JS2Metadata;
|
||||
class BytecodeContainer;
|
||||
|
||||
@ -89,14 +96,17 @@ public:
|
||||
|
||||
void push(js2val x) { ASSERT(sp < (execStack + MAX_EXEC_STACK)); *sp++ = x; }
|
||||
js2val pop() { ASSERT(sp > execStack); return *--sp; }
|
||||
js2val top() { return *(sp - 1); }
|
||||
|
||||
String *convertValueToString(js2val x);
|
||||
js2val convertValueToPrimitive(js2val x);
|
||||
float64 convertValueToDouble(js2val x);
|
||||
bool convertValueToBoolean(js2val x);
|
||||
|
||||
String *toString(js2val x) { if (JS2VAL_IS_STRING(x)) return JS2VAL_TO_STRING(x); else return convertValueToString(x); }
|
||||
js2val toPrimitive(js2val x) { if (JS2VAL_IS_PRIMITIVE(x)) return x; else return convertValueToPrimitive(x); }
|
||||
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); }
|
||||
|
||||
uint8 *pc;
|
||||
BytecodeContainer *bCon;
|
||||
@ -119,6 +129,9 @@ public:
|
||||
js2val *sp;
|
||||
|
||||
|
||||
static JS2Object *defaultConstructor(JS2Engine *engine);
|
||||
|
||||
|
||||
static int getStackEffect(JS2Op op);
|
||||
|
||||
|
||||
|
@ -102,6 +102,21 @@ namespace MetaData {
|
||||
ValidateStmt(cxt, env, l->stmt);
|
||||
}
|
||||
break;
|
||||
case StmtNode::If:
|
||||
{
|
||||
UnaryStmtNode *i = checked_cast<UnaryStmtNode *>(p);
|
||||
ValidateExpression(cxt, env, i->expr);
|
||||
ValidateStmt(cxt, env, i->stmt);
|
||||
}
|
||||
break;
|
||||
case StmtNode::IfElse:
|
||||
{
|
||||
BinaryStmtNode *i = checked_cast<BinaryStmtNode *>(p);
|
||||
ValidateExpression(cxt, env, i->expr);
|
||||
ValidateStmt(cxt, env, i->stmt);
|
||||
ValidateStmt(cxt, env, i->stmt2);
|
||||
}
|
||||
break;
|
||||
case StmtNode::Var:
|
||||
case StmtNode::Const:
|
||||
{
|
||||
@ -144,6 +159,28 @@ namespace MetaData {
|
||||
// the type and the value are 'future'
|
||||
}
|
||||
break;
|
||||
case Attribute::Abstract:
|
||||
case Attribute::Virtual:
|
||||
case Attribute::Final:
|
||||
{
|
||||
JS2Class *c = checked_cast<JS2Class *>(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);
|
||||
break;
|
||||
case Attribute::Final:
|
||||
m = new InstanceVariable(immutable, true);
|
||||
break;
|
||||
}
|
||||
defineInstanceMember(c, cxt, *name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, m, p->pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,7 +267,7 @@ namespace MetaData {
|
||||
reportError(Exception::definitionError, "Illegal modifier for class definition", p->pos);
|
||||
break;
|
||||
}
|
||||
JS2Class *c = new JS2Class(superClass, proto, new Namespace(engine->public_StringAtom), (a->dynamic || superClass->dynamic), final);
|
||||
JS2Class *c = new JS2Class(superClass, proto, new Namespace(engine->private_StringAtom), (a->dynamic || superClass->dynamic), final, classStmt->name);
|
||||
classStmt->c = c;
|
||||
Variable *v = new Variable(classClass, OBJECT_TO_JS2VAL(c), true);
|
||||
defineStaticMember(env, classStmt->name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, v, p->pos);
|
||||
@ -291,6 +328,34 @@ namespace MetaData {
|
||||
EvalStmt(env, phase, l->stmt);
|
||||
}
|
||||
break;
|
||||
case StmtNode::If:
|
||||
{
|
||||
BytecodeContainer::LabelID skipOverStmt = bCon->getLabel();
|
||||
UnaryStmtNode *i = checked_cast<UnaryStmtNode *>(p);
|
||||
Reference *r = EvalExprNode(env, phase, i->expr);
|
||||
if (r) r->emitReadBytecode(bCon, p->pos);
|
||||
bCon->emitOp(eToBoolean, p->pos);
|
||||
bCon->emitBranch(eBranchFalse, skipOverStmt, p->pos);
|
||||
EvalStmt(env, phase, i->stmt);
|
||||
bCon->setLabel(skipOverStmt);
|
||||
}
|
||||
break;
|
||||
case StmtNode::IfElse:
|
||||
{
|
||||
BytecodeContainer::LabelID falseStmt = bCon->getLabel();
|
||||
BytecodeContainer::LabelID skipOverFalseStmt = bCon->getLabel();
|
||||
BinaryStmtNode *i = checked_cast<BinaryStmtNode *>(p);
|
||||
Reference *r = EvalExprNode(env, phase, i->expr);
|
||||
if (r) r->emitReadBytecode(bCon, p->pos);
|
||||
bCon->emitOp(eToBoolean, p->pos);
|
||||
bCon->emitBranch(eBranchFalse, falseStmt, p->pos);
|
||||
EvalStmt(env, phase, i->stmt);
|
||||
bCon->emitBranch(eBranch, skipOverFalseStmt, p->pos);
|
||||
bCon->setLabel(falseStmt);
|
||||
EvalStmt(env, phase, i->stmt2);
|
||||
bCon->setLabel(skipOverFalseStmt);
|
||||
}
|
||||
break;
|
||||
case StmtNode::Var:
|
||||
case StmtNode::Const:
|
||||
{
|
||||
@ -593,11 +658,15 @@ namespace MetaData {
|
||||
break;
|
||||
case ExprNode::dot:
|
||||
{
|
||||
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
|
||||
ValidateExpression(cxt, env, b->op1);
|
||||
ValidateExpression(cxt, env, b->op2);
|
||||
}
|
||||
break;
|
||||
|
||||
case ExprNode::assignment:
|
||||
case ExprNode::add:
|
||||
case ExprNode::subtract:
|
||||
{
|
||||
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
|
||||
ValidateExpression(cxt, env, b->op1);
|
||||
@ -610,6 +679,17 @@ namespace MetaData {
|
||||
// IdentifierExprNode *i = checked_cast<IdentifierExprNode *>(p);
|
||||
}
|
||||
break;
|
||||
case ExprNode::New:
|
||||
{
|
||||
InvokeExprNode *i = checked_cast<InvokeExprNode *>(p);
|
||||
ValidateExpression(cxt, env, i->op);
|
||||
ExprPairList *args = i->pairs;
|
||||
while (args) {
|
||||
ValidateExpression(cxt, env, args->value);
|
||||
args = args->next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED("Not Yet Implemented");
|
||||
} // switch (p->getKind())
|
||||
@ -638,6 +718,7 @@ namespace MetaData {
|
||||
Reference *JS2Metadata::EvalExprNode(Environment *env, Phase phase, ExprNode *p)
|
||||
{
|
||||
Reference *returnRef = NULL;
|
||||
JS2Op binaryOp;
|
||||
|
||||
switch (p->getKind()) {
|
||||
|
||||
@ -656,13 +737,19 @@ namespace MetaData {
|
||||
}
|
||||
break;
|
||||
case ExprNode::add:
|
||||
binaryOp = eAdd;
|
||||
goto doBinary;
|
||||
case ExprNode::subtract:
|
||||
binaryOp = eSubtract;
|
||||
goto doBinary;
|
||||
doBinary:
|
||||
{
|
||||
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
|
||||
Reference *lVal = EvalExprNode(env, phase, b->op1);
|
||||
if (lVal) lVal->emitReadBytecode(bCon, p->pos);
|
||||
Reference *rVal = EvalExprNode(env, phase, b->op2);
|
||||
if (rVal) rVal->emitReadBytecode(bCon, p->pos);
|
||||
bCon->emitOp(ePlus, p->pos);
|
||||
bCon->emitOp(binaryOp, p->pos);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -684,7 +771,7 @@ namespace MetaData {
|
||||
case ExprNode::qualify:
|
||||
{
|
||||
QualifyExprNode *qe = checked_cast<QualifyExprNode *>(p);
|
||||
const StringAtom &name = checked_cast<IdentifierExprNode *>(p)->name;
|
||||
const StringAtom &name = qe->name;
|
||||
|
||||
js2val av = EvalExpression(env, CompilePhase, qe->qualifier);
|
||||
if (JS2VAL_IS_NULL(av) || !JS2VAL_IS_OBJECT(av))
|
||||
@ -695,6 +782,7 @@ namespace MetaData {
|
||||
Namespace *ns = checked_cast<Namespace *>(obj);
|
||||
|
||||
returnRef = new LexicalReference(name, ns, cxt.strict);
|
||||
// Calling emitBindBytecode causes the multiname to get loaded onto the stack
|
||||
((LexicalReference *)returnRef)->emitBindBytecode(bCon, p->pos);
|
||||
}
|
||||
break;
|
||||
@ -706,6 +794,27 @@ namespace MetaData {
|
||||
((LexicalReference *)returnRef)->emitBindBytecode(bCon, p->pos);
|
||||
}
|
||||
break;
|
||||
case ExprNode::dot:
|
||||
{
|
||||
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
|
||||
Reference *baseVal = EvalExprNode(env, phase, b->op1);
|
||||
if (baseVal) baseVal->emitReadBytecode(bCon, p->pos);
|
||||
|
||||
if (b->op2->getKind() == ExprNode::identifier) {
|
||||
returnRef = new DotReference(i->name);
|
||||
((DotReference *)returnRef)->emitBindBytecode(bCon, p->pos);
|
||||
}
|
||||
else {
|
||||
if (b->op2->getKind() == ExprNode::qualify) {
|
||||
Reference *rVal = EvalExprNode(env, phase, b->op2);
|
||||
ASSERT(rVal && checked_cast<LexicalReference *>(rVal));
|
||||
returnRef = new DotReference(((LexicalReference *)rVal)->variableMultiname);
|
||||
((DotReference *)returnRef)->emitBindBytecode(bCon, p->pos);
|
||||
}
|
||||
// else bracketRef...
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExprNode::boolean:
|
||||
if (checked_cast<BooleanExprNode *>(p)->value)
|
||||
bCon->emitOp(eTrue, p->pos);
|
||||
@ -741,15 +850,19 @@ namespace MetaData {
|
||||
bCon->addShort(argCount);
|
||||
}
|
||||
break;
|
||||
case ExprNode::dot:
|
||||
case ExprNode::New:
|
||||
{
|
||||
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
|
||||
if (b->op2->getKind() == ExprNode::identifier) {
|
||||
}
|
||||
else {
|
||||
if (b->op2->getKind() == ExprNode::qualify) {
|
||||
}
|
||||
InvokeExprNode *i = checked_cast<InvokeExprNode *>(p);
|
||||
Reference *rVal = EvalExprNode(env, phase, i->op);
|
||||
if (rVal) rVal->emitReadBytecode(bCon, p->pos);
|
||||
ExprPairList *args = i->pairs;
|
||||
uint32 argCount = 0;
|
||||
while (args) {
|
||||
EvalExprNode(env, phase, args->value);
|
||||
argCount++;
|
||||
args = args->next;
|
||||
}
|
||||
bCon->emitOp(eNew, p->pos);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -787,7 +900,7 @@ namespace MetaData {
|
||||
prev = pf;
|
||||
pf = pf->nextFrame;
|
||||
}
|
||||
if (pf->nextFrame && (pf->kind == ClassKind))
|
||||
if ((pf != firstFrame) && (pf->kind == ClassKind))
|
||||
pf = prev;
|
||||
return pf;
|
||||
}
|
||||
@ -980,7 +1093,165 @@ namespace MetaData {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Look through 'c' and all it's super classes for a identifier
|
||||
// matching the qualified name and access.
|
||||
InstanceMember *JS2Metadata::findInstanceMember(JS2Class *c, QualifiedName *qname, Access access)
|
||||
{
|
||||
if (qname == NULL)
|
||||
return NULL;
|
||||
JS2Class *s = c;
|
||||
while (s) {
|
||||
if (access & ReadAccess) {
|
||||
for (InstanceBindingIterator b = s->instanceReadBindings.lower_bound(qname->id),
|
||||
end = s->instanceReadBindings.upper_bound(qname->id); (b != end); b++) {
|
||||
if (*qname == b->second->qname)
|
||||
return b->second->content;
|
||||
}
|
||||
}
|
||||
if (access & WriteAccess) {
|
||||
for (InstanceBindingIterator b = s->instanceWriteBindings.lower_bound(qname->id),
|
||||
end = s->instanceWriteBindings.upper_bound(qname->id); (b != end); b++) {
|
||||
if (*qname == b->second->qname)
|
||||
return b->second->content;
|
||||
}
|
||||
}
|
||||
s = s->super;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Examine class 'c' and find all instance members that would be overridden
|
||||
// by 'id' in any of the given namespaces.
|
||||
OverrideStatus *JS2Metadata::searchForOverrides(JS2Class *c, const StringAtom &id, NamespaceList *namespaces, Access access, size_t pos)
|
||||
{
|
||||
OverrideStatus *os = new OverrideStatus(NULL, id);
|
||||
for (NamespaceListIterator ns = namespaces->begin(), end = namespaces->end(); (ns != end); ns++) {
|
||||
QualifiedName qname(*ns, id);
|
||||
InstanceMember *m = findInstanceMember(c, &qname, access);
|
||||
if (m) {
|
||||
os->multiname.addNamespace(*ns);
|
||||
if (os->overriddenMember == NULL)
|
||||
os->overriddenMember = m;
|
||||
else
|
||||
if (os->overriddenMember != m) // different instance members by same id
|
||||
reportError(Exception::definitionError, "Illegal override", pos);
|
||||
}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
// Find the possible override conflicts that arise from the given id and namespaces
|
||||
// Fall back on the currently open namespace list if no others are specified.
|
||||
OverrideStatus *JS2Metadata::resolveOverrides(JS2Class *c, Context *cxt, const StringAtom &id, NamespaceList *namespaces, Access access, bool expectMethod, size_t pos)
|
||||
{
|
||||
OverrideStatus *os = NULL;
|
||||
if ((namespaces == NULL) || namespaces->empty()) {
|
||||
os = searchForOverrides(c, id, &cxt->openNamespaces, access, pos);
|
||||
if (os->overriddenMember == NULL) {
|
||||
ASSERT(os->multiname.nsList.empty());
|
||||
os->multiname.addNamespace(publicNamespace);
|
||||
}
|
||||
}
|
||||
else {
|
||||
OverrideStatus *os2 = searchForOverrides(c, id, namespaces, access, pos);
|
||||
if (os2->overriddenMember == NULL) {
|
||||
OverrideStatus *os3 = searchForOverrides(c, id, &cxt->openNamespaces, access, pos);
|
||||
if (os3->overriddenMember == NULL) {
|
||||
os->multiname.addNamespace(namespaces);
|
||||
}
|
||||
else {
|
||||
os->potentialConflict = true; // 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);
|
||||
}
|
||||
delete os3;
|
||||
delete os2;
|
||||
}
|
||||
else {
|
||||
os = os2;
|
||||
os->multiname.addNamespace(namespaces);
|
||||
}
|
||||
}
|
||||
// For all the discovered possible overrides, make sure the member doesn't already exist in the class
|
||||
for (NamespaceListIterator nli = os->multiname.nsList.begin(), nlend = os->multiname.nsList.end(); (nli != nlend); nli++) {
|
||||
QualifiedName qname(*nli, id);
|
||||
if (access & ReadAccess) {
|
||||
for (InstanceBindingIterator b = c->instanceReadBindings.lower_bound(id),
|
||||
end = c->instanceReadBindings.upper_bound(id); (b != end); b++) {
|
||||
if (qname == b->second->qname)
|
||||
reportError(Exception::definitionError, "Illegal override", pos);
|
||||
}
|
||||
}
|
||||
if (access & WriteAccess) {
|
||||
for (InstanceBindingIterator b = c->instanceWriteBindings.lower_bound(id),
|
||||
end = c->instanceWriteBindings.upper_bound(id); (b != end); b++) {
|
||||
if (qname == b->second->qname)
|
||||
reportError(Exception::definitionError, "Illegal override", pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make sure we're getting what we expected
|
||||
if (expectMethod) {
|
||||
if (os->overriddenMember && !os->potentialConflict && (os->overriddenMember->kind != InstanceMember::InstanceMethodKind))
|
||||
reportError(Exception::definitionError, "Illegal override, expected method", pos);
|
||||
}
|
||||
else {
|
||||
if (os->overriddenMember && !os->potentialConflict && (os->overriddenMember->kind == InstanceMember::InstanceMethodKind))
|
||||
reportError(Exception::definitionError, "Illegal override, didn't expect method", pos);
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
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;
|
||||
if (xplicit)
|
||||
reportError(Exception::definitionError, "Illegal use of explicit", pos);
|
||||
|
||||
if (access & ReadAccess)
|
||||
readStatus = resolveOverrides(c, cxt, id, namespaces, ReadAccess, (m->kind == InstanceMember::InstanceMethodKind), pos);
|
||||
else
|
||||
readStatus = new OverrideStatus(NULL, id);
|
||||
|
||||
if (access & WriteAccess)
|
||||
writeStatus = resolveOverrides(c, cxt, id, namespaces, WriteAccess, (m->kind == InstanceMember::InstanceMethodKind), pos);
|
||||
else
|
||||
writeStatus = new OverrideStatus(NULL, id);
|
||||
|
||||
if ((!readStatus->potentialConflict && (readStatus->overriddenMember != NULL))
|
||||
|| (!writeStatus->potentialConflict && (writeStatus->overriddenMember != NULL))) {
|
||||
if ((overrideMod != Attribute::DoOverride) && (overrideMod != Attribute::OverrideUndefined))
|
||||
reportError(Exception::definitionError, "Illegal override", pos);
|
||||
}
|
||||
else {
|
||||
if (readStatus->potentialConflict || writeStatus->potentialConflict) {
|
||||
if ((overrideMod != Attribute::DontOverride) && (overrideMod != Attribute::OverrideUndefined))
|
||||
reportError(Exception::definitionError, "Illegal override", pos);
|
||||
}
|
||||
}
|
||||
|
||||
NamespaceListIterator nli, nlend;
|
||||
for (nli = readStatus->multiname.nsList.begin(), nlend = readStatus->multiname.nsList.end(); (nli != nlend); nli++) {
|
||||
QualifiedName qName(*nli, id);
|
||||
InstanceBinding *ib = new InstanceBinding(qName, m);
|
||||
const InstanceBindingMap::value_type e(id, ib);
|
||||
c->instanceReadBindings.insert(e);
|
||||
}
|
||||
|
||||
for (nli = writeStatus->multiname.nsList.begin(), nlend = writeStatus->multiname.nsList.end(); (nli != nlend); nli++) {
|
||||
QualifiedName qName(*nli, id);
|
||||
InstanceBinding *ib = new InstanceBinding(qName, m);
|
||||
const InstanceBindingMap::value_type e(id, ib);
|
||||
c->instanceWriteBindings.insert(e);
|
||||
}
|
||||
|
||||
OverrideStatusPair osp(readStatus, writeStatus);
|
||||
return osp;
|
||||
}
|
||||
|
||||
// Define a hoisted var in the current frame (either Global or a Function)
|
||||
void JS2Metadata::defineHoistedVar(Environment *env, const StringAtom &id, StmtNode *p)
|
||||
{
|
||||
@ -1042,6 +1313,9 @@ namespace MetaData {
|
||||
{
|
||||
cxt.openNamespaces.clear();
|
||||
cxt.openNamespaces.push_back(publicNamespace);
|
||||
|
||||
objectClass = new JS2Class(NULL, new JS2Object(PrototypeInstanceKind), new Namespace(engine->private_StringAtom), true, false, engine->object_StringAtom);
|
||||
objectClass->complete = true;
|
||||
}
|
||||
|
||||
// objectType(o) returns an OBJECT o's most specific type.
|
||||
@ -1062,16 +1336,33 @@ namespace MetaData {
|
||||
return stringClass;
|
||||
}
|
||||
ASSERT(JS2VAL_IS_OBJECT(obj));
|
||||
return NULL;
|
||||
/*
|
||||
NAMESPACE do return namespaceClass;
|
||||
COMPOUNDATTRIBUTE do return attributeClass;
|
||||
CLASS do return classClass;
|
||||
METHODCLOSURE do return functionClass;
|
||||
PROTOTYPE do return prototypeClass;
|
||||
INSTANCE do return resolveAlias(o).type;
|
||||
PACKAGE or GLOBAL do return packageClass
|
||||
*/
|
||||
JS2Object *obj = JS2VAL_TO_OBJECT(obj);
|
||||
switch (obj->kind) {
|
||||
case AttributeObjectKind:
|
||||
return attributeClass;
|
||||
case MultinameKind:
|
||||
return namespaceClass;
|
||||
case ClassKind:
|
||||
return classClass;
|
||||
case PrototypeInstanceKind:
|
||||
return prototypeClass;
|
||||
|
||||
case FixedInstanceKind:
|
||||
return checked_cast<FixedInstance *>(obj)->type;
|
||||
case DynamicInstanceKind:
|
||||
return checked_cast<DynamicInstance *>(obj)->type;
|
||||
|
||||
case GlobalObjectKind:
|
||||
case PackageKind:
|
||||
return packageClass;
|
||||
|
||||
case SystemKind:
|
||||
case FunctionKind:
|
||||
case BlockKind:
|
||||
default:
|
||||
ASSERT(false);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the property from the container given by the public id in multiname - if that exists
|
||||
@ -1216,9 +1507,103 @@ namespace MetaData {
|
||||
|
||||
// Read the value of a property in the container. Return true/false if that container has
|
||||
// the property or not. If it does, return it's value
|
||||
bool JS2Metadata::readProperty(js2val container, Multiname *multiname, LookupKind *lookupKind, Phase phase, js2val *rval)
|
||||
bool JS2Metadata::readProperty(js2val containerVal, Multiname *multiname, LookupKind *lookupKind, Phase phase, js2val *rval)
|
||||
{
|
||||
return true;
|
||||
bool isDynamicInstance = false;
|
||||
if (JS2VAL_IS_PRIMITIVE(containerVal)) {
|
||||
readClassProperty:
|
||||
JS2Class *c = objectType(containerVal);
|
||||
InstanceBinding *ib = resolveInstanceMemberName(c, multiname, ReadAccess, Phase phase);
|
||||
if ((ib == NULL) && isDynamicInstance)
|
||||
return readDynamicProperty(JS2VAL_TO_OBJECT(containerVal), multiname, lookupKind, phase, rval);
|
||||
else
|
||||
// XXX passing a primitive here ???
|
||||
return readInstanceMember(containerVal, c, (ib)? &ib->qname : NULL, phase, rval);
|
||||
}
|
||||
JS2Object *obj = JS2VAL_TO_OBJECT(containerVal);
|
||||
switch (obj->kind) {
|
||||
case AttributeObjectKind:
|
||||
case MultinameKind:
|
||||
case FixedInstanceKind:
|
||||
goto readClassProperty;
|
||||
case DynamicInstanceKind:
|
||||
isDynamicInstance = true;
|
||||
goto readClassProperty;
|
||||
|
||||
case SystemKind:
|
||||
case GlobalObjectKind:
|
||||
case PackageKind:
|
||||
case FunctionKind:
|
||||
case BlockKind:
|
||||
{
|
||||
}
|
||||
break;
|
||||
case ClassKind:
|
||||
{
|
||||
}
|
||||
break;
|
||||
|
||||
case PrototypeInstanceKind:
|
||||
return readDynamicProperty(obj, multiname, lookupKind, phase, rval);
|
||||
default:
|
||||
ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/*
|
||||
m: INSTANCEMEMBEROPT ¨ findInstanceMember(c, qname, read);
|
||||
case m of
|
||||
{none} do return none;
|
||||
INSTANCEVARIABLE do
|
||||
if phase = compile and not m.immutable then throw compileExpressionError
|
||||
end if;
|
||||
v: OBJECTU ¨ findSlot(this, m).value;
|
||||
if v = uninitialised then throw uninitialisedError end if;
|
||||
return v;
|
||||
INSTANCEMETHOD do return METHODCLOSURE·this: this, method: mÒ;
|
||||
INSTANCEGETTER do return m.call(this, m.env, phase);
|
||||
INSTANCESETTER do
|
||||
m cannot be an INSTANCESETTER because these are only represented as write-only members.
|
||||
end case
|
||||
end proc;
|
||||
*/
|
||||
|
||||
proc findSlot(o: OBJECT, id: INSTANCEVARIABLE): SLOT
|
||||
o must be an INSTANCE;
|
||||
matchingSlots: SLOT{} ¨ {s | "s Œ resolveAlias(o).slots such that s.id = id};
|
||||
return the one element of matchingSlots
|
||||
end proc;
|
||||
|
||||
JS2MetaData::findSlot(js2val thisObjVal, InstanceVariable *id)
|
||||
{
|
||||
ASSERT(JS2VAL_IS_OBJECT(thisObjVal)
|
||||
&& ((JS2VAL_TO_OBJECT(thisObjVal)->kind == DynamicInstanceKind)
|
||||
|| (JS2VAL_TO_OBJECT(thisObjVal)->kind == FixedInstanceKind)));
|
||||
JS2Object *thisObj = JS2VAL_TO_OBJECT(thisObjVal);
|
||||
Slots *s;
|
||||
if (thisObj->kind == DynamicInstanceKind)
|
||||
s = checked_cast<DynamicInstance *>(thisObj)->slots;
|
||||
else
|
||||
s = checked_cast<FixedInstance *>(thisObj)->slots;
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool JS2Metadata::readInstanceMember(js2val containerVal, JS2Class *c, QualifiedName *qname, Phase phase, js2val *rval)
|
||||
{
|
||||
InstanceMember *m = findInstanceMember(c, qname, ReadAccess);
|
||||
if (m == NULL) return false;
|
||||
switch (m->kind) {
|
||||
case InstanceVariableKind:
|
||||
if ((phase == CompilePhase) && !checked_cast<InstanceVariable *>(m)->immutable)
|
||||
reportError(Exception::compileExpressionError, "Inappropriate compile time expression", engine->errorPos());
|
||||
findSlot(containerVal, m);
|
||||
|
||||
|
||||
case InstanceMethodKind:
|
||||
case InstanceAccessorKind:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the value of a property in the frame. Return true/false if that frame has
|
||||
@ -1444,7 +1829,8 @@ namespace MetaData {
|
||||
*
|
||||
************************************************************************************/
|
||||
|
||||
JS2Class::JS2Class(JS2Class *super, JS2Object *proto, Namespace *privateNamespace, bool dynamic, bool final)
|
||||
|
||||
JS2Class::JS2Class(JS2Class *super, JS2Object *proto, Namespace *privateNamespace, bool dynamic, bool final, const StringAtom &name)
|
||||
: Frame(ClassKind),
|
||||
instanceInitOrder(NULL),
|
||||
complete(false),
|
||||
@ -1455,8 +1841,25 @@ namespace MetaData {
|
||||
primitive(false),
|
||||
final(final),
|
||||
call(NULL),
|
||||
construct(NULL)
|
||||
{ }
|
||||
construct(JS2Engine::defaultConstructor),
|
||||
name(name)
|
||||
{
|
||||
}
|
||||
|
||||
// examine the instancebinding map to determine the number of slots required
|
||||
uint32 JS2Class::countSlots()
|
||||
{
|
||||
uint32 count = 0;
|
||||
InstanceBindingIterator b, end;
|
||||
for (b = instanceReadBindings.begin(), end = instanceReadBindings.end(); (b != end); b++) {
|
||||
|
||||
}
|
||||
for (b = instanceWriteBindings.begin(), end = instanceWriteBindings.end(); (b != end); b++) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************************************************************
|
||||
*
|
||||
|
@ -54,7 +54,7 @@ class Pond;
|
||||
|
||||
typedef void (Invokable)();
|
||||
typedef Invokable Callor;
|
||||
typedef JS2Object *(Constructor)();
|
||||
typedef JS2Object *(Constructor)(JS2Engine *engine);
|
||||
|
||||
enum ObjectKind {
|
||||
AttributeObjectKind,
|
||||
@ -180,8 +180,6 @@ public:
|
||||
Multiname(const StringAtom &name) : JS2Object(MultinameKind), name(name) { }
|
||||
Multiname(const StringAtom &name, Namespace *ns) : JS2Object(MultinameKind), name(name) { addNamespace(ns); }
|
||||
|
||||
void emitBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eMultiname, pos); bCon->addMultiname(this); }
|
||||
|
||||
void addNamespace(Namespace *ns) { nsList.push_back(ns); }
|
||||
void addNamespace(NamespaceList *ns);
|
||||
void addNamespace(Context &cxt);
|
||||
@ -294,38 +292,64 @@ public:
|
||||
StaticMember *content; // The member to which this qualified name was bound
|
||||
};
|
||||
|
||||
|
||||
class InstanceMember {
|
||||
public:
|
||||
enum InstanceMemberKind { InstanceVariableKind, InstanceMethodKind, InstanceAccessorKind };
|
||||
|
||||
InstanceMember(InstanceMemberKind kind, bool final) : kind(kind), final(final) { }
|
||||
|
||||
InstanceMemberKind kind;
|
||||
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
|
||||
#endif
|
||||
};
|
||||
|
||||
class InstanceVariable : public InstanceMember {
|
||||
public:
|
||||
InstanceVariable(bool immutable, bool final) : InstanceMember(InstanceVariableKind, final), immutable(immutable) { }
|
||||
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
|
||||
bool final;
|
||||
};
|
||||
|
||||
class InstanceMethod : public InstanceMember {
|
||||
public:
|
||||
InstanceMethod() : InstanceMember(InstanceMethodKind, 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
|
||||
Invokable *code; // A callable object which does the read or write; null if this method is abstract
|
||||
};
|
||||
|
||||
class InstanceBinding {
|
||||
public:
|
||||
InstanceBinding(QualifiedName &qname, InstanceMember *content) : qname(qname), content(content) { }
|
||||
|
||||
QualifiedName qname; // The qualified name bound by this binding
|
||||
InstanceMember *content; // The member to which this qualified name was bound
|
||||
};
|
||||
|
||||
// Override status is used to resolve overriden definitions for instance members
|
||||
class OverrideStatus {
|
||||
public:
|
||||
OverrideStatus(InstanceMember *overriddenMember, const StringAtom &name)
|
||||
: overriddenMember(overriddenMember), potentialConflict(false), multiname(name) { }
|
||||
|
||||
InstanceMember *overriddenMember; // NULL for none
|
||||
bool potentialConflict;
|
||||
Multiname multiname;
|
||||
};
|
||||
typedef std::pair<OverrideStatus *, OverrideStatus *> OverrideStatusPair;
|
||||
|
||||
|
||||
|
||||
|
||||
// A StaticBindingMap maps names to a list of StaticBindings. Each StaticBinding in the list
|
||||
// will have the same QualifiedName.name, but (potentially) different QualifiedName.namespace values
|
||||
@ -353,9 +377,9 @@ public:
|
||||
|
||||
class JS2Class : public Frame {
|
||||
public:
|
||||
JS2Class(JS2Class *super, JS2Object *proto, Namespace *privateNamespace, bool dynamic, bool final);
|
||||
JS2Class(JS2Class *super, JS2Object *proto, Namespace *privateNamespace, bool dynamic, bool final, const StringAtom &name);
|
||||
|
||||
StringAtom &getName();
|
||||
const StringAtom &getName() { return name; }
|
||||
|
||||
InstanceBindingMap instanceReadBindings; // Map of qualified names to readable instance members defined in this class
|
||||
InstanceBindingMap instanceWriteBindings; // Map of qualified names to writable instance members defined in this class
|
||||
@ -376,6 +400,10 @@ public:
|
||||
Callor *call; // A procedure to call when this class is used in a call expression
|
||||
Constructor *construct; // A procedure to call when this class is used in a new expression
|
||||
|
||||
uint32 countSlots();
|
||||
|
||||
const StringAtom &name;
|
||||
|
||||
};
|
||||
|
||||
class GlobalObject : public Frame {
|
||||
@ -397,13 +425,13 @@ public:
|
||||
// Instances of non-dynamic classes are represented as FIXEDINSTANCE records. These instances can contain only fixed properties.
|
||||
class FixedInstance : public JS2Object {
|
||||
public:
|
||||
FixedInstance() : JS2Object(FixedInstanceKind), typeofString(type->getName()) { }
|
||||
FixedInstance(JS2Class *type) : JS2Object(FixedInstanceKind), type(type), call(NULL), construct(NULL), env(NULL), typeofString(type->getName()) { }
|
||||
|
||||
JS2Class *type; // This instance's type
|
||||
Invokable *call; // A procedure to call when this instance is used in a call expression
|
||||
Invokable *construct; // A procedure to call when this instance is used in a new expression
|
||||
Environment *env; // The environment to pass to the call or construct procedure
|
||||
StringAtom &typeofString; // A string to return if typeof is invoked on this instance
|
||||
const StringAtom &typeofString; // A string to return if typeof is invoked on this instance
|
||||
Slot *slots; // A set of slots that hold this instance's fixed property values
|
||||
};
|
||||
|
||||
@ -416,7 +444,7 @@ public:
|
||||
Invokable *call; // A procedure to call when this instance is used in a call expression
|
||||
Invokable *construct; // A procedure to call when this instance is used in a new expression
|
||||
Environment *env; // The environment to pass to the call or construct procedure
|
||||
StringAtom &typeofString; // A string to return if typeof is invoked on this instance
|
||||
const StringAtom &typeofString; // A string to return if typeof is invoked on this instance
|
||||
Slot *slots; // A set of slots that hold this instance's fixed property values
|
||||
DynamicPropertyMap dynamicProperties; // A set of this instance's dynamic properties
|
||||
};
|
||||
@ -438,7 +466,8 @@ public:
|
||||
|
||||
|
||||
// Base class for all references (lvalues)
|
||||
// References are generated during the eval stage (bytecode generation)
|
||||
// References are generated during the eval stage (bytecode generation), but shouldn't live beyond that
|
||||
// XXX use an arena new/delete. (N.B. the contained multinames make it into the bytecode stream)
|
||||
class Reference {
|
||||
public:
|
||||
virtual void emitReadBytecode(BytecodeContainer *bCon, size_t pos) { ASSERT(false); }
|
||||
@ -460,10 +489,9 @@ public:
|
||||
Environment *env; // The environment in which the reference was created.
|
||||
bool strict; // The strict setting from the context in effect at the point where the reference was created
|
||||
|
||||
|
||||
void emitBindBytecode(BytecodeContainer *bCon, size_t pos) { variableMultiname->emitBytecode(bCon, pos); }
|
||||
virtual void emitReadBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eLexicalRead, pos); }
|
||||
virtual void emitWriteBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eLexicalWrite, pos); }
|
||||
|
||||
virtual void emitReadBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eLexicalRead, pos); bCon->addMultiname(variableMultiname); }
|
||||
virtual void emitWriteBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eLexicalWrite, pos); bCon->addMultiname(variableMultiname); }
|
||||
};
|
||||
|
||||
class DotReference : public Reference {
|
||||
@ -471,13 +499,18 @@ class DotReference : public Reference {
|
||||
// object with one of a given set of qualified names. DOTREFERENCE tuples arise from evaluating subexpressions such as a.b or
|
||||
// a.q::b.
|
||||
public:
|
||||
js2val base; // The object whose property was referenced (a in the examples above). The
|
||||
DotReference(const StringAtom &name) : propertyMultiname(new Multiname(name)) { }
|
||||
DotReference(Multiname *mn) : propertyMultiname(mn) { }
|
||||
|
||||
// js2val base; // The object whose property was referenced (a in the examples above). The
|
||||
// object may be a LIMITEDINSTANCE if a is a super expression, in which case
|
||||
// the property lookup will be restricted to members defined in proper ancestors
|
||||
// of base.limit.
|
||||
Multiname propertyMultiname; // A nonempty set of qualified names to which this reference can refer (b
|
||||
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); }
|
||||
};
|
||||
|
||||
|
||||
@ -646,12 +679,17 @@ public:
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
|
||||
bool readProperty(js2val container, Multiname *multiname, LookupKind *lookupKind, Phase phase, js2val *rval);
|
||||
bool readProperty(Frame *pf, Multiname *multiname, LookupKind *lookupKind, Phase phase, js2val *rval);
|
||||
bool readDynamicProperty(JS2Object *container, Multiname *multiname, LookupKind *lookupKind, Phase phase, js2val *rval);
|
||||
bool readStaticMember(StaticMember *m, Phase phase, js2val *rval);
|
||||
bool readInstanceMember(js2val containerVal, JS2Class *c, QualifiedName *qname, Phase phase, js2val *rval);
|
||||
|
||||
|
||||
bool writeProperty(Frame *container, Multiname *multiname, LookupKind *lookupKind, bool createIfMissing, js2val newValue, Phase phase);
|
||||
@ -684,6 +722,9 @@ public:
|
||||
JS2Class *objectClass;
|
||||
JS2Class *namespaceClass;
|
||||
JS2Class *classClass;
|
||||
JS2Class *packageClass;
|
||||
JS2Class *prototypeClass;
|
||||
JS2Class *attributeClass;
|
||||
|
||||
Parser *mParser; // used for error reporting
|
||||
|
||||
|
@ -31,51 +31,53 @@
|
||||
* 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);
|
||||
mn->addNamespace(meta->cxt);
|
||||
push(OBJECT_TO_JS2VAL(mn));
|
||||
}
|
||||
break;
|
||||
// Get a multiname literal and push the resulting multiname object
|
||||
case eMultiname:
|
||||
{
|
||||
push(OBJECT_TO_JS2VAL(mn));
|
||||
}
|
||||
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<Multiname *>(obj);
|
||||
retval = meta->env.lexicalRead(meta, mn, phase);
|
||||
push(retval);
|
||||
}
|
||||
break;
|
||||
case eDotRead:
|
||||
{
|
||||
Multiname *mn = bCon->mMultinameList[BytecodeContainer::getShort(pc)];
|
||||
pc += sizeof(short);
|
||||
js2val baseVal = pop();
|
||||
readProperty
|
||||
}
|
||||
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<Multiname *>(obj);
|
||||
meta->env.lexicalWrite(meta, mn, retval, true, phase);
|
||||
push(retval);
|
||||
}
|
||||
break;
|
||||
// Pop a multiname object and read it's value from the environment on to the stack.
|
||||
case eLexicalRead:
|
||||
{
|
||||
Multiname *mn = bCon->mMultinameList[BytecodeContainer::getShort(pc)];
|
||||
pc += sizeof(short);
|
||||
retval = meta->env.lexicalRead(meta, mn, phase);
|
||||
push(retval);
|
||||
}
|
||||
break;
|
||||
|
||||
case ePushFrame:
|
||||
{
|
||||
Frame *f = bCon->mFrameList[BytecodeContainer::getShort(pc)];
|
||||
pc += sizeof(short);
|
||||
}
|
||||
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:
|
||||
{
|
||||
Multiname *mn = bCon->mMultinameList[BytecodeContainer::getShort(pc)];
|
||||
pc += sizeof(short);
|
||||
meta->env.lexicalWrite(meta, mn, retval, true, phase);
|
||||
push(retval);
|
||||
}
|
||||
break;
|
||||
|
||||
case ePopFrame:
|
||||
{
|
||||
}
|
||||
break;
|
||||
case ePushFrame:
|
||||
{
|
||||
Frame *f = bCon->mFrameList[BytecodeContainer::getShort(pc)];
|
||||
pc += sizeof(short);
|
||||
meta->env.addFrame(f);
|
||||
}
|
||||
break;
|
||||
|
||||
case ePopFrame:
|
||||
{
|
||||
meta->env.removeTopFrame();
|
||||
}
|
||||
break;
|
||||
|
@ -34,7 +34,8 @@
|
||||
|
||||
|
||||
|
||||
case ePlus: {
|
||||
case eAdd:
|
||||
{
|
||||
js2val a = pop();
|
||||
js2val b = pop();
|
||||
a = toPrimitive(a);
|
||||
@ -55,3 +56,12 @@
|
||||
}
|
||||
break;
|
||||
|
||||
case eSubtract:
|
||||
{
|
||||
js2val a = pop();
|
||||
js2val b = pop();
|
||||
float64 anum = toNumber(a);
|
||||
float64 bnum = toNumber(b);
|
||||
retval = pushNumber(anum - bnum);
|
||||
}
|
||||
break;
|
||||
|
80
js2/src/js2op_flowcontrol.cpp
Normal file
80
js2/src/js2op_flowcontrol.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is the JavaScript 2 Prototype.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the
|
||||
* terms of the GNU Public License (the "GPL"), in which case the
|
||||
* provisions of the GPL are applicable instead of those above.
|
||||
* If you wish to allow use of your version of this file only
|
||||
* under the terms of the GPL and not to allow others to use your
|
||||
* version of this file under the NPL, indicate your decision by
|
||||
* deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this
|
||||
* file under either the NPL or the GPL.
|
||||
*/
|
||||
|
||||
case eReturn:
|
||||
{
|
||||
retval = pop();
|
||||
return retval;
|
||||
}
|
||||
break;
|
||||
|
||||
case eReturnVoid:
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
break;
|
||||
|
||||
case eBranchTrue:
|
||||
{
|
||||
retval = pop();
|
||||
ASSERT(JS2VAL_IS_BOOLEAN(retval));
|
||||
bool b = JS2VAL_TO_BOOLEAN(retval);
|
||||
if (b) {
|
||||
int32 offset = BytecodeContainer::getShort(pc);
|
||||
pc += offset;
|
||||
}
|
||||
else
|
||||
pc += sizeof(int32);
|
||||
}
|
||||
break;
|
||||
|
||||
case eBranchFalse:
|
||||
{
|
||||
retval = pop();
|
||||
ASSERT(JS2VAL_IS_BOOLEAN(retval));
|
||||
bool b = JS2VAL_TO_BOOLEAN(retval);
|
||||
if (!b) {
|
||||
int32 offset = BytecodeContainer::getShort(pc);
|
||||
pc += offset;
|
||||
}
|
||||
else
|
||||
pc += sizeof(int32);
|
||||
}
|
||||
break;
|
||||
|
||||
case eBranch:
|
||||
{
|
||||
int32 offset = BytecodeContainer::getShort(pc);
|
||||
pc += offset;
|
||||
}
|
||||
break;
|
@ -33,18 +33,8 @@
|
||||
*/
|
||||
|
||||
|
||||
case eReturn: {
|
||||
retval = pop();
|
||||
return retval;
|
||||
}
|
||||
break;
|
||||
|
||||
case eReturnVoid: {
|
||||
return retval;
|
||||
}
|
||||
break;
|
||||
|
||||
case eNewObject: {
|
||||
case eNewObject: // XXX in js2op_literal instead?
|
||||
{
|
||||
uint16 argCount = BytecodeContainer::getShort(pc);
|
||||
pc += sizeof(uint16);
|
||||
PrototypeInstance *pInst = new PrototypeInstance(NULL); // XXX Object prototype object
|
||||
@ -57,6 +47,28 @@
|
||||
const DynamicPropertyMap::value_type e(nameAtom, fieldVal);
|
||||
pInst->dynamicProperties.insert(e);
|
||||
}
|
||||
push(OBJECT_TO_JS2VAL(pInst));
|
||||
retval = OBJECT_TO_JS2VAL(pInst);
|
||||
push(retval);
|
||||
}
|
||||
break;
|
||||
|
||||
case eToBoolean:
|
||||
{
|
||||
js2val v = pop();
|
||||
bool b = toBoolean(v);
|
||||
retval = BOOLEAN_TO_JS2VAL(b);
|
||||
push(retval);
|
||||
}
|
||||
break;
|
||||
|
||||
case eNew:
|
||||
{
|
||||
js2val v = top();
|
||||
ASSERT(JS2VAL_IS_OBJECT(v) && !JS2VAL_IS_NULL(v));
|
||||
JS2Object *obj = JS2VAL_TO_OBJECT(v);
|
||||
ASSERT(obj->kind == ClassKind);
|
||||
JS2Class *c = checked_cast<JS2Class *>(obj);
|
||||
retval = OBJECT_TO_JS2VAL(c->construct(this));
|
||||
push(retval);
|
||||
}
|
||||
break;
|
Loading…
Reference in New Issue
Block a user