Function invocation & parameter frame instantiation. Labelled statements.

This commit is contained in:
rogerl%netscape.com 2002-09-10 23:36:00 +00:00
parent 70c0fd6bb0
commit 3098c11277
9 changed files with 219 additions and 88 deletions

View File

@ -105,7 +105,7 @@ public:
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) { adjustStack(op, getStackEffect(op)); }
void adjustStack(JS2Op op, int32 effect);
void addByte(uint8 v) { mBuffer.push_back(v); }

View File

@ -241,10 +241,10 @@ JS2Engine::JS2Engine(World &world)
float64Table[i] = NULL;
sp = execStack = new js2val[MAX_EXEC_STACK];
activationStackTop = activationStack = new ActivationFrame[MAX_ACTIVATION_STACK];
}
int JS2Engine::getStackEffect(JS2Op op)
int getStackEffect(JS2Op op)
{
switch (op) {
case eReturn:
@ -345,6 +345,28 @@ JS2Object *JS2Engine::defaultConstructor(JS2Engine *engine)
return new FixedInstance(c);
}
void JS2Engine::jsr(BytecodeContainer *new_bCon)
{
ASSERT(activationStackTop < (activationStack + MAX_ACTIVATION_STACK));
activationStackTop->bCon = bCon;
activationStackTop->pc = pc;
activationStackTop++;
bCon = new_bCon;
pc = new_bCon->getCodeStart();
}
void JS2Engine::rts()
{
ASSERT(activationStackTop > activationStack);
activationStackTop--;
bCon = activationStackTop->bCon;
pc = activationStackTop->pc;
}
}
}

View File

@ -92,6 +92,8 @@ enum JS2Op {
};
int getStackEffect(JS2Op op);
class JS2Object;
class JS2Metadata;
@ -103,23 +105,11 @@ public:
JS2Engine(World &world);
js2val interpret(JS2Metadata *metadata, Phase execPhase, BytecodeContainer *targetbCon);
js2val interpreterLoop();
void *gc_alloc_8();
float64 *newDoubleValue(float64 x);
// Use the pc map in the current bytecode container to get a source offset
size_t errorPos();
js2val allocNumber(float64 x);
js2val pushNumber(float64 x) { js2val retval = allocNumber(x); push(retval); return retval; }
#define MAX_EXEC_STACK (20)
void push(js2val x) { ASSERT(sp < (execStack + MAX_EXEC_STACK)); *sp++ = x; }
js2val pop() { ASSERT(sp > execStack); return *--sp; }
js2val top() { return *(sp - 1); }
js2val top(int argCount) { return *(sp - (1 + argCount)); }
String *convertValueToString(js2val x);
js2val convertValueToPrimitive(js2val x);
@ -134,15 +124,27 @@ public:
js2val assignmentConversion(js2val val, JS2Class *type) { return val; } // XXX s'more code, please
// Current engine execution state
uint8 *pc;
BytecodeContainer *bCon;
JS2Metadata *meta;
Phase phase;
World &world;
// A cache of f.p. values (XXX experimentally trying to reduce # of double pointers XXX)
float64 *nanValue;
float64 *float64Table[256];
js2val allocNumber(float64 x);
js2val pushNumber(float64 x) { js2val retval = allocNumber(x); push(retval); return retval; }
void *gc_alloc_8();
float64 *newDoubleValue(float64 x);
// Cached StringAtoms for handy access
StringAtom &true_StringAtom;
StringAtom &false_StringAtom;
StringAtom &null_StringAtom;
@ -151,17 +153,39 @@ public:
StringAtom &private_StringAtom;
StringAtom &function_StringAtom;
StringAtom &object_StringAtom;
// The activation stack, when it's empty and a return is executed, the
// interpreter quits
#define MAX_ACTIVATION_STACK (20)
struct ActivationFrame {
uint8 *pc;
BytecodeContainer *bCon;
};
void jsr(BytecodeContainer *bCon);
bool activationStackEmpty() { return (activationStackTop == activationStack); }
void rts();
ActivationFrame *activationStack;
ActivationFrame *activationStackTop;
// The execution stack for expression evaluation, should be empty
// between statements.
#define MAX_EXEC_STACK (20)
js2val *execStack;
js2val *sp;
void push(js2val x) { ASSERT(sp < (execStack + MAX_EXEC_STACK)); *sp++ = x; }
js2val pop() { ASSERT(sp > execStack); return *--sp; }
js2val top() { return *(sp - 1); }
js2val top(int argCount) { return *(sp - (1 + argCount)); }
static JS2Object *defaultConstructor(JS2Engine *engine);
static int getStackEffect(JS2Op op);
};

View File

@ -71,15 +71,17 @@ namespace MetaData {
* Validate the linked list of statement nodes beginning at 'p'
*/
void JS2Metadata::ValidateStmtList(StmtNode *p) {
LabelSet stmtLbl;
JumpTarget jt;
while (p) {
ValidateStmt(&cxt, &env, p);
ValidateStmt(&cxt, &env, p, &stmtLbl, &jt);
p = p->next;
}
}
void JS2Metadata::ValidateStmtList(Context *cxt, Environment *env, StmtNode *p) {
void JS2Metadata::ValidateStmtList(Context *cxt, Environment *env, StmtNode *p, LabelSet *stmtLbl, JumpTarget *jt) {
while (p) {
ValidateStmt(cxt, env, p);
ValidateStmt(cxt, env, p, stmtLbl, jt);
p = p->next;
}
}
@ -89,40 +91,55 @@ namespace MetaData {
/*
* Validate an individual statement 'p', including it's children
*/
void JS2Metadata::ValidateStmt(Context *cxt, Environment *env, StmtNode *p) {
void JS2Metadata::ValidateStmt(Context *cxt, Environment *env, StmtNode *p, LabelSet *stmtLbl, JumpTarget *jt) {
switch (p->getKind()) {
case StmtNode::block:
case StmtNode::group:
{
BlockStmtNode *b = checked_cast<BlockStmtNode *>(p);
ValidateStmtList(cxt, env, b->statements);
b->compileFrame = new BlockFrame();
env->addFrame(b->compileFrame);
ValidateStmtList(cxt, env, b->statements, stmtLbl, jt);
env->removeTopFrame();
}
break;
case StmtNode::label:
{
LabelStmtNode *l = checked_cast<LabelStmtNode *>(p);
ValidateStmt(cxt, env, l->stmt);
l->labelID = bCon->getLabel();
std::pair<LabelSet::iterator, bool> result = stmtLbl->insert(LabelSet::value_type(&l->name, l->labelID));
if (!result.second)
reportError(Exception::syntaxError, "Duplicate statement label", p->pos);
ValidateStmt(cxt, env, l->stmt, stmtLbl, jt);
}
break;
case StmtNode::If:
{
UnaryStmtNode *i = checked_cast<UnaryStmtNode *>(p);
ValidateExpression(cxt, env, i->expr);
ValidateStmt(cxt, env, i->stmt);
ValidateStmt(cxt, env, i->stmt, stmtLbl, jt);
}
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);
ValidateStmt(cxt, env, i->stmt, stmtLbl, jt);
ValidateStmt(cxt, env, i->stmt2, stmtLbl, jt);
}
break;
case StmtNode::While:
{
}
break;
case StmtNode::Return:
{
ExprStmtNode *e = checked_cast<ExprStmtNode *>(p);
if (e->expr) {
ValidateExpression(cxt, env, e->expr);
}
}
break;
case StmtNode::Function:
{
Attribute *attr = NULL;
@ -163,13 +180,16 @@ namespace MetaData {
|| (memberMod == Attribute::Virtual)
|| (memberMod == Attribute::Final))
compileThis = JS2VAL_INACCESSIBLE;
ParameterFrame *compileFrame = new ParameterFrame();
compileFrame->thisObject = compileThis;
compileFrame->prototype = prototype;
ParameterFrame *compileFrame = new ParameterFrame(compileThis, prototype);
Frame *topFrame = env->getTopFrame();
env->addFrame(compileFrame);
// ValidateStmt(cxt, env, f->function.parameters);
ValidateStmt(cxt, env, f->function.body);
VariableBinding *pb = f->function.parameters;
while (pb) {
// XXX define a static binding for each parameter
pb = pb->next;
}
ValidateStmt(cxt, env, f->function.body, stmtLbl, jt);
env->removeTopFrame();
if (unchecked
&& ((topFrame->kind == GlobalObjectKind)
|| (topFrame->kind == ParameterKind))
@ -182,16 +202,14 @@ namespace MetaData {
case Attribute::Static:
{
FixedInstance *fInst = new FixedInstance(functionClass);
fInst->fWrap = new FunctionWrapper();
fInst->fWrap->compileThis = compileThis;
fInst->fWrap->unchecked = unchecked;
fInst->fWrap = new FunctionWrapper(unchecked, compileFrame);
f->fWrap = fInst->fWrap;
Variable *v = new Variable(functionClass, OBJECT_TO_JS2VAL(fInst), true);
defineStaticMember(env, *f->function.name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, v, p->pos);
}
break;
}
}
env->removeTopFrame();
}
break;
case StmtNode::Var:
@ -345,7 +363,7 @@ namespace MetaData {
defineStaticMember(env, classStmt->name, a->namespaces, a->overrideMod, a->xplicit, ReadWriteAccess, v, p->pos);
if (classStmt->body) {
env->addFrame(c);
ValidateStmtList(cxt, env, classStmt->body->statements);
ValidateStmtList(cxt, env, classStmt->body->statements, stmtLbl, jt);
ASSERT(env->getTopFrame() == c);
env->removeTopFrame();
}
@ -411,11 +429,16 @@ namespace MetaData {
case StmtNode::group:
{
BlockStmtNode *b = checked_cast<BlockStmtNode *>(p);
env->addFrame(b->compileFrame);
bCon->emitOp(ePushFrame, p->pos);
bCon->addFrame(b->compileFrame);
StmtNode *bp = b->statements;
while (bp) {
EvalStmt(env, phase, bp);
bp = bp->next;
}
bCon->emitOp(ePopFrame, p->pos);
env->removeTopFrame();
}
break;
case StmtNode::label:
@ -456,13 +479,23 @@ namespace MetaData {
{
}
break;
case StmtNode::Return:
{
ExprStmtNode *e = checked_cast<ExprStmtNode *>(p);
if (e->expr) {
EvalExprNode(env, phase, e->expr);
bCon->emitOp(eReturn, p->pos);
}
}
break;
case StmtNode::Function:
{
FunctionStmtNode *f = checked_cast<FunctionStmtNode *>(p);
BytecodeContainer *saveBacon = bCon;
f->fWrap->bCon = new BytecodeContainer();
bCon = f->fWrap->bCon;
env->addFrame(f->fWrap->compileFrame);
EvalStmt(env, phase, f->function.body);
env->removeTopFrame();
bCon = saveBacon;
}
break;
@ -1131,7 +1164,7 @@ doBinary:
argCount++;
args = args->next;
}
bCon->emitOp(eCall, p->pos);
bCon->emitOp(eCall, p->pos, -argCount + 1); // pop argCount args and push a result
bCon->addShort(argCount);
}
break;
@ -1147,7 +1180,7 @@ doBinary:
argCount++;
args = args->next;
}
bCon->emitOp(eNew, p->pos, -argCount + 1); // pop argCount args and push a result
bCon->emitOp(eNew, p->pos, -argCount + 1); // pop argCount args and push a new object
bCon->addShort(argCount);
}
break;
@ -1260,6 +1293,42 @@ doBinary:
meta->reportError(Exception::referenceError, "{0} is undefined", meta->engine->errorPos(), multiname->name);
}
// Clone the pluralFrame bindings into the singularFrame, instantiating new members for each binding
void Environment::instantiateFrame(Frame *pluralFrame, Frame *singularFrame)
{
StaticBindingIterator sbi, sbend;
for (sbi = pluralFrame->staticReadBindings.begin(), sbend = pluralFrame->staticReadBindings.end(); (sbi != sbend); sbi++) {
sbi->second->content->cloneContent = NULL;
}
for (sbi = pluralFrame->staticWriteBindings.begin(), sbend = pluralFrame->staticWriteBindings.end(); (sbi != sbend); sbi++) {
sbi->second->content->cloneContent = NULL;
}
for (sbi = pluralFrame->staticReadBindings.begin(), sbend = pluralFrame->staticReadBindings.end(); (sbi != sbend); sbi++) {
StaticBinding *sb;
StaticBinding *m = sbi->second;
if (m->content->cloneContent == NULL) {
m->content->cloneContent = m->content->clone();
}
sb = new StaticBinding(m->qname, m->content->cloneContent);
sb->xplicit = m->xplicit;
const StaticBindingMap::value_type e(sbi->first, sb);
singularFrame->staticReadBindings.insert(e);
}
for (sbi = pluralFrame->staticWriteBindings.begin(), sbend = pluralFrame->staticWriteBindings.end(); (sbi != sbend); sbi++) {
StaticBinding *sb;
StaticBinding *m = sbi->second;
if (m->content->cloneContent == NULL) {
m->content->cloneContent = new Variable();
}
sb = new StaticBinding(m->qname, m->content->cloneContent);
sb->xplicit = m->xplicit;
const StaticBindingMap::value_type e(sbi->first, sb);
singularFrame->staticWriteBindings.insert(e);
}
}
/************************************************************************************
@ -2059,6 +2128,7 @@ readClassProperty:
access = WriteAccess;
b = container->staticWriteBindings.lower_bound(multiname->name);
end = container->staticWriteBindings.upper_bound(multiname->name);
continue;
}
else
break;
@ -2160,6 +2230,7 @@ readClassProperty:
access = WriteAccess;
b = c->instanceWriteBindings.lower_bound(multiname->name);
end = c->instanceWriteBindings.upper_bound(multiname->name);
continue;
}
else
break;

View File

@ -104,6 +104,7 @@ public:
Pond *nextPond;
};
class JS2Object {
// Every object is either undefined, null, a Boolean,
// a number, a string, a namespace, a compound attribute, a class, a method closure,
@ -235,6 +236,9 @@ class StaticMember : public Member {
public:
StaticMember(MemberKind kind) : Member(kind) { }
StaticMember *cloneContent; // Used during cloning operation
virtual StaticMember *clone() { ASSERT(false); return NULL; }
};
#define FUTURE_TYPE ((JS2Class *)(-1))
@ -244,6 +248,8 @@ public:
Variable() : StaticMember(Member::Variable), type(NULL), vb(NULL), value(JS2VAL_VOID), immutable(false) { }
Variable(JS2Class *type, js2val value, bool immutable) : StaticMember(StaticMember::Variable), type(type), vb(NULL), value(value), immutable(immutable) { }
virtual StaticMember *clone() { return new Variable(type, value, immutable); }
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;
@ -254,6 +260,7 @@ public:
class HoistedVar : public StaticMember {
public:
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
};
@ -589,6 +596,7 @@ public:
// Frames holding bindings for invoked functions
class ParameterFrame : public Frame {
public:
ParameterFrame(js2val thisObject, bool prototype) : Frame(ParameterKind), thisObject(thisObject), prototype(prototype) { }
ParameterFrame() : Frame(ParameterKind) { }
Plurality plurality;
@ -637,6 +645,9 @@ public:
js2val lexicalRead(JS2Metadata *meta, Multiname *multiname, Phase phase);
void lexicalWrite(JS2Metadata *meta, Multiname *multiname, js2val newValue, bool createIfMissing, Phase phase);
void instantiateFrame(Frame *pluralFrame, Frame *singularFrame);
private:
Frame *firstFrame;
};
@ -644,8 +655,10 @@ private:
class FunctionWrapper {
public:
FunctionWrapper(bool unchecked, ParameterFrame *compileFrame)
: bCon(new BytecodeContainer), unchecked(unchecked), compileFrame(compileFrame) { }
BytecodeContainer *bCon;
js2val compileThis; // The value of 'this' established at Validate time
bool unchecked; // true if the function is untyped, non-method, normal
ParameterFrame *compileFrame;
};
@ -697,6 +710,15 @@ public:
bool unused; // true if the unused attribute has been given
};
typedef std::map<const StringAtom *, BytecodeContainer::LabelID> LabelSet;
class JumpTarget {
public:
JumpTarget() : breakTargets(new LabelSet()), continueTargets(new LabelSet()) { }
LabelSet *breakTargets;
LabelSet *continueTargets;
};
struct MemberDescriptor {
StaticMember *staticMember;
QualifiedName *qname;
@ -713,9 +735,9 @@ public:
js2val EvalStmtList(Phase phase, StmtNode *p);
void ValidateStmtList(Context *cxt, Environment *env, StmtNode *p);
void ValidateStmtList(Context *cxt, Environment *env, StmtNode *p, LabelSet *stmtLbl, JumpTarget *jt);
void ValidateTypeExpression(Context *cxt, Environment *env, ExprNode *e) { ValidateExpression(cxt, env, e); }
void ValidateStmt(Context *cxt, Environment *env, StmtNode *p);
void ValidateStmt(Context *cxt, Environment *env, StmtNode *p, LabelSet *stmtLbl, JumpTarget *jt);
void ValidateExpression(Context *cxt, Environment *env, ExprNode *p);
void ValidateAttributeExpression(Context *cxt, Environment *env, ExprNode *p);

View File

@ -31,19 +31,6 @@
* file under either the NPL or the GPL.
*/
case eReturn:
{
retval = pop();
return retval;
}
break;
case eReturnVoid:
{
return retval;
}
break;
case eBranchTrue:
{
retval = pop();

View File

@ -87,7 +87,7 @@
if (fObj->kind == FixedInstanceKind) {
FixedInstance *fInst = checked_cast<FixedInstance *>(fObj);
FunctionWrapper *fWrap = fInst->fWrap;
js2val compileThis = fWrap->compileThis;
js2val compileThis = fWrap->compileFrame->thisObject;
js2val runtimeThis;
if (JS2VAL_IS_VOID(compileThis))
runtimeThis = JS2VAL_VOID;
@ -101,37 +101,33 @@
}
Frame *runtimeFrame = new ParameterFrame();
meta->env.addFrame(runtimeFrame);
// instantiateFrame(fWrap->compileFrame, runtimeFrame, meta->env);
meta->env.instantiateFrame(fWrap->compileFrame, runtimeFrame);
// assignArguments(runtimeFrame, fWrap->compileFrame->signature);
jsr(fWrap->bCon);
/*
proc call(this: OBJECT, args: ARGUMENTLIST, runtimeEnv: ENVIRONMENT, phase: PHASE): OBJECT
if phase = compile then throw compileExpressionError end if;
runtimeThis: OBJECTOPT;
case compileThis of
{none} do runtimeThis ¨ none;
{inaccessible} do
runtimeThis ¨ this;
g: PACKAGE » GLOBAL ¨ getPackageOrGlobalFrame(runtimeEnv);
if prototype and runtimeThis Œ {null, undefined} and g Œ GLOBAL then
runtimeThis ¨ g
end if
end case;
runtimeFrame: PARAMETERFRAME ¨ new PARAMETERFRAME··staticReadBindings: {},
staticWriteBindings: {}, plurality: singular, this: runtimeThis, prototype: prototype,
signature: compileFrame.signatureÒÒ;
instantiateFrame(compileFrame, runtimeFrame, [runtimeFrame] ! runtimeEnv);
assignArguments(runtimeFrame, compileFrame.signature, unchecked, args);
try
Eval[Block]([runtimeFrame] ! runtimeEnv, undefined);
throw RETURNEDVALUE·value: undefinedÒ
catch x: SEMANTICEXCEPTION do
if x Œ RETURNEDVALUE then return x.value else throw x end if
end try
*/
}
else
ASSERT(false);
}
}
break;
case eReturn:
{
retval = pop();
if (activationStackEmpty())
return retval;
else
rts();
}
break;
case eReturnVoid:
{
if (activationStackEmpty())
return retval;
else
rts();
}
break;

View File

@ -35,7 +35,7 @@
case eNumber:
{
push(DOUBLE_TO_JS2VAL(newDoubleValue(BytecodeContainer::getFloat64(pc))));
pushNumber(BytecodeContainer::getFloat64(pc));
pc += sizeof(float64);
}
break;

View File

@ -65,6 +65,8 @@ namespace JavaScript {
class OverrideStatus;
typedef std::pair<OverrideStatus *, OverrideStatus *> OverrideStatusPair;
class FunctionWrapper;
class BlockFrame;
typedef uint32 LabelID;
}
#endif
@ -550,6 +552,10 @@ namespace JavaScript {
AttributeStmtNode(pos, kind, attributes), statements(statements) {}
void print(PrettyPrinter &f, bool noSemi) const;
#ifdef EPIMETHEUS
MetaData::BlockFrame *compileFrame; // used by backend
#endif
};
struct LabelStmtNode: StmtNode {
@ -560,6 +566,9 @@ namespace JavaScript {
StmtNode(pos, label), name(name), stmt(stmt) {ASSERT(stmt);}
void print(PrettyPrinter &f, bool noSemi) const;
#ifdef EPIMETHEUS
MetaData::LabelID labelID;
#endif
};
struct UnaryStmtNode: ExprStmtNode {