netscape-revival
mocha/src/mocha.c
/*
** Mocha virtual machine.
**
** Brendan Eich, 6/20/95
*/
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "prlog.h"
#include "alloca.h"
#include "mo_atom.h"
#include "mo_bcode.h"
#include "mo_cntxt.h"
#include "mo_scope.h"
#include "mocha.h"
#include "mochaapi.h"
#include "mochalib.h"
#ifdef DEBUG
# include "mo_emit.h" /* for mocha_PCtoLineNumber() */
#endif
static void stub_taint_counter(MochaContext *mc, uint16 taint) { }
MochaTaintCounter mocha_HoldTaint = stub_taint_counter;
MochaTaintCounter mocha_DropTaint = stub_taint_counter;
/*
** MochaDatum tag values used only in this file, for secret stack data types.
** If Properties and ObjectStacks were Objects, mocha_Hold/DropRef() might be
** simpler, but the structs would be fatter, and for-in and with code would
** be complicated.
*/
#define MOCHA_PROPERTY 255 /* u.pair, but obj+prop not obj+sym */
#define MOCHA_OBJECTSTACK 254 /* u.ptr, points at MochaObjectStack */
/*
** Hold and release object references from a stack datum, a global variable,
** a property, or a stack frame's return value datum.
*/
void
mocha_HoldRef(MochaContext *mc, MochaDatum *dp)
{
switch (dp->tag) {
case MOCHA_ATOM:
case MOCHA_STRING:
mocha_HoldAtom(mc, dp->u.atom);
break;
case MOCHA_SYMBOL:
MOCHA_HoldObject(mc, dp->u.pair.obj);
break;
case MOCHA_FUNCTION:
case MOCHA_OBJECT:
if ((dp->flags & MDF_BACKEDGE) == 0)
MOCHA_HoldObject(mc, dp->u.obj);
break;
}
if (dp->taint != MOCHA_TAINT_IDENTITY)
(*mocha_HoldTaint)(mc, dp->taint);
}
void
mocha_DropRef(MochaContext *mc, MochaDatum *dp)
{
MochaProperty *prop;
MochaObjectStack *top;
switch (dp->tag) {
case MOCHA_ATOM:
case MOCHA_STRING:
dp->u.atom = mocha_DropAtom(mc, dp->u.atom);
if (!dp->u.atom)
dp->tag = MOCHA_UNDEF;
break;
case MOCHA_SYMBOL:
dp->u.pair.obj = MOCHA_DropObject(mc, dp->u.pair.obj);
if (!dp->u.pair.obj) {
dp->tag = MOCHA_UNDEF;
dp->u.pair.sym = 0;
}
break;
case MOCHA_FUNCTION:
case MOCHA_OBJECT:
if ((dp->flags & MDF_BACKEDGE) == 0) {
dp->u.obj = MOCHA_DropObject(mc, dp->u.obj);
if (!dp->u.obj)
dp->tag = MOCHA_UNDEF;
}
break;
case MOCHA_PROPERTY:
prop = (MochaProperty *)dp->u.pair.sym; /* XXX type me please */
if (prop) {
PR_ASSERT(dp->u.pair.obj);
MOCHA_DropObject(mc, dp->u.pair.obj);
dp->u.pair.obj = 0;
dp->u.pair.sym = 0;
}
break;
case MOCHA_OBJECTSTACK:
top = dp->u.ptr;
PR_ASSERT(top && top->object);
mocha_PopObject(mc, top);
dp->u.ptr = 0;
break;
}
if (dp->taint != MOCHA_TAINT_IDENTITY)
(*mocha_DropTaint)(mc, dp->taint);
}
/*
** These can't over- or underflow because the compiler computed worst-case
** stack depth, and mocha_Interpret() checks that mc has enough room before
** it starts pushing and popping.
*/
static void
Push(MochaContext *mc, MochaDatum d)
{
PR_ASSERT(mc->stack.ptr < mc->stack.limit);
MOCHA_ASSERT_VALID_DATUM_FLAGS(&d);
mocha_HoldRef(mc, &d);
*mc->stack.ptr++ = d;
}
static MochaDatum
Pop(MochaContext *mc, MochaBoolean drop)
{
MochaDatum *dp;
PR_ASSERT(mc->stack.ptr > mc->stack.base);
dp = --mc->stack.ptr;
MOCHA_ASSERT_VALID_DATUM_FLAGS(dp);
if (drop)
mocha_DropRef(mc, dp);
return *dp;
}
static void
ReportStackOverflow(MochaContext *mc)
{
MOCHA_ReportError(mc, "stack overflow in %s",
mc->stack.frame
? atom_name(mc->stack.frame->fun->atom)
: "top-level");
}
static void
PushSymbol(MochaContext *mc, MochaObject *obj, MochaSymbol *sym)
{
MochaPair pair;
MochaDatum d;
pair.obj = obj, pair.sym = sym;
MOCHA_INIT_FULL_DATUM(mc, &d, MOCHA_SYMBOL, 0, mc->taintInfo->accum,
u.pair, pair);
Push(mc, d);
}
static void
PushObject(MochaContext *mc, MochaObject *obj)
{
MochaDatum d;
MOCHA_INIT_FULL_DATUM(mc, &d, MOCHA_OBJECT, 0, mc->taintInfo->accum,
u.obj, obj);
Push(mc, d);
}
static void
PushNumber(MochaContext *mc, MochaFloat fval)
{
MochaDatum d;
MOCHA_INIT_FULL_DATUM(mc, &d, MOCHA_NUMBER, 0, mc->taintInfo->accum,
u.fval, fval);
Push(mc, d);
}
static void
PushBoolean(MochaContext *mc, MochaBoolean bval)
{
MochaDatum d;
MOCHA_INIT_FULL_DATUM(mc, &d, MOCHA_BOOLEAN, 0, mc->taintInfo->accum,
u.bval, bval);
Push(mc, d);
}
static void
PushString(MochaContext *mc, MochaAtom *atom)
{
MochaDatum d;
MOCHA_INIT_FULL_DATUM(mc, &d, MOCHA_STRING, 0, mc->taintInfo->accum,
u.atom, atom);
Push(mc, d);
}
static MochaBoolean
PopNumber(MochaContext *mc, MochaFloat *fvalp)
{
MochaDatum d;
MochaBoolean ok;
d = Pop(mc, MOCHA_FALSE);
ok = mocha_DatumToNumber(mc, d, fvalp);
mocha_DropRef(mc, &d);
return ok;
}
static MochaBoolean
PopInt(MochaContext *mc, MochaInt *ivalp, MochaBoolean *validp)
{
MochaFloat fval;
MochaInt ival;
if (!PopNumber(mc , &fval))
return MOCHA_FALSE;
ival = (MochaInt)fval;
*ivalp = ival;
#ifdef XP_PC
if (MOCHA_FLOAT_IS_NaN(fval)) {
*validp = MOCHA_FALSE;
return MOCHA_TRUE;
}
#endif
*validp &= ((MochaFloat)ival == fval ||
(MochaFloat)(MochaUint)ival == fval);
return MOCHA_TRUE;
}
static MochaBoolean
PopBoolean(MochaContext *mc, MochaBoolean *bvalp)
{
MochaDatum d;
MochaBoolean ok;
d = Pop(mc, MOCHA_FALSE);
ok = mocha_DatumToBoolean(mc, d, bvalp);
mocha_DropRef(mc, &d);
return ok;
}
MochaBoolean
mocha_ResolveSymbol(MochaContext *mc, MochaDatum *dp, MochaLookupFlag flag)
{
MochaPair pair;
MochaAtom *atom;
if (dp->tag == MOCHA_SYMBOL) {
pair = dp->u.pair;
atom = sym_atom(pair.sym);
} else {
if (dp->tag != MOCHA_ATOM)
return MOCHA_TRUE;
atom = dp->u.atom;
if (!mocha_SearchScopes(mc, atom, flag, &pair))
return MOCHA_FALSE;
if (!pair.sym) {
pair.obj = mc->objectStack ? mc->objectStack->object
: mc->staticLink;
PR_ASSERT(pair.obj);
if (!pair.obj) return MOCHA_TRUE;
}
}
if (!pair.sym || pair.sym->type == SYM_UNDEF) {
if (!OBJ_RESOLVE_NAME(mc, pair.obj, atom_name(atom)))
return MOCHA_FALSE;
if (!mocha_LookupSymbol(mc, pair.obj->scope, atom, flag, &pair.sym))
return MOCHA_FALSE;
if (!pair.sym)
return MOCHA_TRUE;
}
dp->tag = MOCHA_SYMBOL;
dp->u.pair = pair;
return MOCHA_TRUE;
}
static MochaDatum *
NewVariable(MochaContext *mc, MochaSymbol *sym)
{
MochaDatum *vp;
vp = MOCHA_malloc(mc, sizeof *vp);
if (!vp)
return 0;
*vp = MOCHA_void;
vp->nrefs = 1;
sym->entry.value = vp;
return vp;
}
MochaDatum *
mocha_ResolveVariable(MochaContext *mc, MochaSymbol *sym)
{
MochaStackFrame *fp, *fp2;
MochaDatum *vp;
MochaSlot nvars, delta;
ptrdiff_t nbytes;
for (fp = mc->stack.frame; fp && fp->fun->call; fp = fp->down)
/* find non-native function frame */;
if (!fp || sym->scope != fp->fun->object.scope)
return NewVariable(mc, sym);
switch (sym->type) {
case SYM_ARGUMENT:
PR_ASSERT((unsigned)sym->slot < fp->fun->nargs);
vp = &fp->argv[sym->slot];
break;
case SYM_VARIABLE:
PR_ASSERT((unsigned)sym->slot < fp->fun->object.scope->freeslot);
nvars = sym->slot + 1;
delta = nvars - fp->nvars;
if (delta > 0) {
/* XXX over-conservative */
if (fp->vars + nvars + mc->script->depth > mc->stack.limit) {
ReportStackOverflow(mc);
return 0;
}
/* Add delta slots to the current stack frame. */
vp = &fp->vars[fp->nvars];
fp->nvars = nvars;
nbytes = (char *)mc->stack.ptr - (char *)vp;
PR_ASSERT(nbytes >= 0);
if (nbytes > 0)
memmove(vp + delta, vp, nbytes);
mc->stack.ptr += delta;
/* Run down the stack frames from top to fp, fixing pointers. */
for (fp2 = mc->stack.frame; fp2 != fp; fp2 = fp2->down) {
fp2->argv += delta;
fp2->vars += delta;
}
/* Clear the new slots. */
do {
*vp++ = MOCHA_void;
} while (--delta > 0);
}
vp = &fp->vars[sym->slot];
break;
default:
PR_ASSERT(0);
return 0;
}
return vp;
}
MochaBoolean
mocha_ResolveValue(MochaContext *mc, MochaDatum *dp)
{
MochaSymbol *sym;
MochaObject *obj;
MochaDatum *vp, rval;
MochaProperty *prop;
if (!mocha_ResolveSymbol(mc, dp, MLF_GET))
return MOCHA_FALSE;
if (dp->tag != MOCHA_SYMBOL) {
if (dp->tag == MOCHA_ATOM) {
MOCHA_ReportError(mc, "%s is not defined", atom_name(dp->u.atom));
return MOCHA_FALSE;
}
} else {
sym = dp->u.pair.sym;
switch (sym->type) {
case SYM_UNDEF:
*dp = MOCHA_void;
break;
case SYM_ARGUMENT:
case SYM_VARIABLE:
vp = sym_datum(sym);
if (!vp) {
vp = mocha_ResolveVariable(mc, sym);
if (!vp) {
*dp = MOCHA_void;
break;
}
}
MOCHA_INIT_FULL_DATUM(mc, dp, vp->tag, 0, vp->taint, u, vp->u);
break;
case SYM_PROPERTY:
PR_ASSERT(sym->entry.value);
obj = dp->u.pair.obj;
prop = sym_property(sym);
vp = &prop->datum;
rval = *vp;
if (!(*prop->getter)(mc, obj, sym->slot, &rval))
return MOCHA_FALSE;
MOCHA_ASSERT_VALID_DATUM_FLAGS(&rval);
/* XXX cope with naughty mo_java.c and lm_img.c (and others?) */
if (rval.tag == MOCHA_STRING && !rval.u.atom) rval.u.atom = MOCHA_empty.u.atom;
/* Hold any rval reference before dropping the old ref in vp. */
mocha_HoldRef(mc, &rval);
mocha_DropRef(mc, vp);
/* Update vp from rval, now that OBJ_GET_PROPERTY has succeeded. */
MOCHA_INIT_FULL_DATUM(mc, vp, rval.tag, rval.flags, rval.taint,
u, rval.u);
/* Copy most of vp to the temporary pointed at by dp. */
MOCHA_INIT_DATUM(mc, dp, vp->tag, u, vp->u);
/* NB: dp may have different taint from vp. */
MOCHA_MIX_TAINT(mc, dp->taint, vp->taint);
break;
default:
PR_ASSERT(0);
return MOCHA_FALSE;
}
}
/* Accumulate taint according to the data tainting algebra. */
MOCHA_MIX_TAINT(mc, mc->taintInfo->accum, dp->taint);
return MOCHA_TRUE;
}
MochaBoolean
mocha_ResolvePrimitiveValue(MochaContext *mc, MochaDatum *dp)
{
MochaDatum rval;
if (!mocha_ResolveValue(mc, dp))
return MOCHA_FALSE;
if (dp->tag == MOCHA_OBJECT && dp->u.obj) {
rval = *dp;
if (!mocha_TryMethod(mc, dp->u.obj, mocha_valueOfAtom, 0, 0, &rval))
return MOCHA_FALSE;
if (rval.tag != MOCHA_UNDEF) {
MOCHA_WeakenRef(mc, &rval);
/* Copy most of rval to the temporary pointed at by dp. */
MOCHA_INIT_DATUM(mc, dp, rval.tag, u, rval.u);
/* NB: dp may have different taint from rval. */
MOCHA_MIX_TAINT(mc, dp->taint, rval.taint);
}
}
return MOCHA_TRUE;
}
static MochaBoolean
ResolveString(MochaContext *mc, MochaDatum d, MochaAtom **atomp)
{
if (!mocha_ResolveValue(mc, &d))
return MOCHA_FALSE;
if (d.tag == MOCHA_OBJECT && d.u.obj)
return mocha_RawDatumToString(mc, d, atomp);
if (d.tag != MOCHA_STRING)
return MOCHA_FALSE;
*atomp = mocha_HoldAtom(mc, d.u.atom);
return MOCHA_TRUE;
}
static MochaAtom *
CatStrings(MochaContext *mc, MochaAtom *atom1, MochaAtom *atom2)
{
const char *s1, *s2;
char *s;
s1 = atom_name(atom1), s2 = atom_name(atom2);
s = (char *)alloca(strlen(s1) + strlen(s2) + 1);
strcat(strcpy(s, s1), s2);
return mocha_Atomize(mc, s, ATOM_STRING);
}
/*
** Call() is not stack-invariant: it pushes missing formal arguments and
** predeclared local variables, calls the function at (sp->ptr - (argc + 1)),
** pops all variables and arguments, and pushes the return value.
*/
static MochaBoolean
Call(MochaContext *mc, unsigned argc)
{
MochaDatum *vp, aval;
MochaBoolean ok, no_parent;
MochaFunction *fun;
MochaObject *obj;
MochaStackFrame frame;
int missing, nslots;
uint16 accum, taint;
unsigned i;
MochaObjectStack *save;
/* Locate the function to call under the arguments on the current stack. */
vp = mc->stack.ptr - (argc + 1);
aval = *vp;
if (!mocha_ResolveSymbol(mc, &aval, MLF_GET))
return MOCHA_FALSE;
/* Resolve aval to a held function, and determine its 'this' object. */
if (!mocha_DatumToFunction(mc, aval, &fun))
return MOCHA_FALSE;
if (fun->bound)
obj = MOCHA_HoldObject(mc, fun->object.parent);
else if (aval.tag == MOCHA_SYMBOL)
obj = MOCHA_HoldObject(mc, aval.u.pair.obj);
else
obj = MOCHA_HoldObject(mc, mc->staticLink);
/* Make vp refer to fun, which is already held so it can be popped. */
if (vp->taint != MOCHA_TAINT_IDENTITY)
(*mocha_HoldTaint)(mc, vp->taint);
mocha_DropRef(mc, vp);
MOCHA_INIT_DATUM(mc, vp, MOCHA_FUNCTION, u.fun, fun);
/* Initialize a stack frame for the function. */
frame.fun = fun;
frame.thisp = obj;
frame.argc = argc;
frame.argv = mc->stack.ptr - argc;
frame.nvars = fun->object.scope->freeslot;
frame.vars = mc->stack.ptr;
frame.down = mc->stack.frame;
frame.rval = MOCHA_void;
/* Resolve args to values (call-by-value). */
accum = mc->taintInfo->accum;
for (vp = frame.argv; vp < frame.vars; vp++) {
aval = *vp;
if (!mocha_ResolveValue(mc, &aval)) {
MOCHA_DropObject(mc, obj);
return MOCHA_FALSE;
}
mocha_HoldRef(mc, &aval);
mocha_DropRef(mc, vp);
*vp = aval;
}
mc->taintInfo->accum = accum;
/* Now that we're done resolving args, push frame. */
mc->stack.frame = &frame;
/* Prepare to push missing argument and predeclared variable slots. */
missing = (argc < fun->nargs) ? fun->nargs - argc : 0;
frame.vars += missing;
if (frame.vars + frame.nvars > mc->stack.limit) {
ReportStackOverflow(mc);
MOCHA_DropObject(mc, obj);
return MOCHA_FALSE;
}
/* Save number of missing args in nslots for post-call Pop() loop. */
nslots = missing;
missing += frame.nvars;
while (--missing >= 0) {
MOCHA_INIT_FULL_DATUM(mc, &aval, MOCHA_UNDEF, 0, accum, u.ptr, 0);
Push(mc, aval);
}
/* Call the function, which is either native or interpreted. */
if (fun->call) {
ok = (*fun->call)(mc, obj, argc, frame.argv, &frame.rval);
taint = frame.argv[-1].taint;
for (i = 0; i < argc; i++)
MOCHA_MIX_TAINT(mc, taint, frame.argv[i].taint);
MOCHA_MIX_TAINT(mc, frame.rval.taint, taint);
mocha_HoldRef(mc, &frame.rval);
} else if (fun->script) {
save = mc->objectStack;
mc->objectStack = 0;
no_parent = (fun->object.parent == 0);
if (no_parent)
fun->object.parent = mc->globalObject;
ok = mocha_Interpret(mc, &fun->object, fun->script, &aval);
if (ok)
mocha_DropRef(mc, &aval);
if (no_parent)
fun->object.parent = 0;
mc->objectStack = save;
} else {
/* fun might be onerror trying to report a syntax error in itself. */
ok = MOCHA_TRUE;
}
/* Restore stack pointer, taking care to pop dynamic variables too. */
nslots += 1 + argc + frame.nvars;
while (--nslots >= 0)
(void) Pop(mc, MOCHA_TRUE);
PR_ASSERT(mc->stack.ptr == frame.argv - 1);
/* Pop stack frame and drop the method's object. */
mc->stack.frame = frame.down;
MOCHA_DropObject(mc, obj);
/* Push return value, *then* drop any object reference held by it. */
Push(mc, frame.rval);
mocha_DropRef(mc, &frame.rval);
return ok;
}
/*
** Assign is not stack-invariant: it pops two operands, taking care not to
** lose the last reference to the right hand one, stores the left hand side,
** and pushes the result expression.
*/
static MochaBoolean
Assign(MochaContext *mc, uint16 *taintp)
{
MochaDatum *vp, lval, rval, aval, aval2;
MochaBoolean ok;
MochaScope *scope;
MochaObject *slink, *obj, *assignObj;
MochaSymbol *sym, *assignSym;
MochaProperty *prop;
MochaAtom *atom;
/* Resolve the right hand side to a value. */
aval = rval = Pop(mc, MOCHA_FALSE);
ok = mocha_ResolveValue(mc, &rval);
if (!ok) {
aval2 = MOCHA_void;
goto out;
}
/* Resolve the left hand side to a symbol. */
aval2 = lval = Pop(mc, MOCHA_FALSE);
ok = mocha_ResolveSymbol(mc, &lval, MLF_SET);
if (!ok)
goto out;
if (lval.tag != MOCHA_SYMBOL) {
if (lval.tag != MOCHA_ATOM)
goto fail;
/* Try to define a new global variable for the current static link. */
slink = mc->staticLink;
do {
scope = slink->scope;
} while ((slink = slink->parent) != 0);
sym = mocha_DefineSymbol(mc, scope, lval.u.atom, SYM_VARIABLE, 0);
if (!sym) {
ok = MOCHA_FALSE;
goto out;
}
vp = NewVariable(mc, sym);
} else {
/* Set an argument, variable, or property to rval. */
obj = lval.u.pair.obj;
sym = lval.u.pair.sym;
scope = sym->scope;
/* If this symbol's value is an object, try its assign method. */
vp = sym_datum(sym);
if (vp && vp->tag == MOCHA_OBJECT && (assignObj = vp->u.obj)) {
ok = mocha_LookupSymbol(mc, assignObj->scope, mocha_assignAtom,
MLF_GET, &assignSym);
if (!ok) goto out;
if (assignSym) {
PushSymbol(mc, assignObj, assignSym);
Push(mc, rval);
ok = Call(mc, 1);
/* Don't reset taint accumulator on return from function. */
*taintp = mc->taintInfo->accum;
goto out;
}
}
/* No assign method, just mutate the symbol's value. */
switch (sym->type) {
case SYM_ARGUMENT:
case SYM_VARIABLE:
if (!vp) {
vp = mocha_ResolveVariable(mc, sym);
if (!vp)
goto fail;
}
break;
case SYM_UNDEF:
sym = mocha_SetProperty(mc, scope, sym_atom(sym), scope->minslot-1,
MOCHA_null);
if (!sym) {
ok = MOCHA_FALSE;
goto out;
}
/* FALL THROUGH */
case SYM_PROPERTY:
prop = sym_property(sym);
ok = (*prop->setter)(mc, obj, sym->slot, &rval);
if (!ok)
goto out;
vp = &prop->datum;
break;
default:
goto fail;
}
}
/* Don't set readonly properties; do report an error. */
MOCHA_ASSERT_VALID_DATUM_FLAGS(vp);
if (vp->flags & MDF_READONLY)
goto fail;
vp->flags |= MDF_ENUMERATE;
/* Hold rval before dropping the old value in case they're the same. */
mocha_HoldRef(mc, &rval);
mocha_DropRef(mc, vp);
/* Don't store a reference to a finalizing object. */
if (rval.tag == MOCHA_OBJECT &&
rval.u.obj && rval.u.obj->nrefs == MOCHA_FINALIZING) {
rval.u.obj = 0;
}
/* Store rval, taking care not to smash vp->nrefs and vp->flags. */
MOCHA_INIT_FULL_DATUM(mc, vp, rval.tag, vp->flags, rval.taint,
u, rval.u);
/* Push the return value. */
Push(mc, rval);
out:
/* Finally, drop any refs held by the left and old right hand sides. */
mocha_DropRef(mc, &aval);
mocha_DropRef(mc, &aval2);
return ok;
fail:
if (mocha_RawDatumToString(mc, lval, &atom)) {
MOCHA_ReportError(mc, "%s can't be set by assignment",
atom_name(atom));
mocha_DropAtom(mc, atom);
}
ok = MOCHA_FALSE;
goto out;
}
MochaBoolean
mocha_Call(MochaContext *mc, MochaDatum fd,
unsigned argc, MochaDatum *argv, MochaDatum *rval)
{
unsigned i;
MochaBoolean ok;
if (mc->stack.ptr + argc >= mc->stack.limit) {
ReportStackOverflow(mc);
return MOCHA_FALSE;
}
Push(mc, fd);
for (i = 0; i < argc; i++)
Push(mc, argv[i]);
ok = Call(mc, argc);
if (ok)
*rval = Pop(mc, MOCHA_FALSE);
else
(void) Pop(mc, MOCHA_TRUE);
return ok;
}
MochaBoolean
mocha_TypeOfDatum(MochaContext *mc, MochaDatum d, MochaAtom **atomp)
{
while (d.tag == MOCHA_ATOM || d.tag == MOCHA_SYMBOL) {
if (!mocha_ResolveSymbol(mc, &d, MLF_GET))
return MOCHA_FALSE;
if (d.tag == MOCHA_ATOM || d.tag == MOCHA_INTERNAL)
break;
if (!mocha_ResolveValue(mc, &d))
return MOCHA_FALSE;
}
/* XXX hide these implementation tags */
if (d.tag == MOCHA_ATOM || d.tag == MOCHA_INTERNAL)
d.tag = MOCHA_UNDEF;
*atomp = mocha_typeAtoms[d.tag];
return MOCHA_TRUE;
}
MochaBoolean
mocha_Interpret(MochaContext *mc, MochaObject *slink, MochaScript *script,
MochaDatum *result)
{
MochaObject *oldslink;
MochaCode *oldpc, *pc, *end;
MochaScript *oldscript;
MochaBranchCallback onBranch;
MochaBoolean ok, bval, valid;
MochaStack *sp;
MochaDatum *oldtos, *bottom;
uint16 taint;
int len, argc;
MochaOp op;
MochaCodeSpec *cs;
MochaDatum *vp, lval, rval, aval, aval2;
MochaObject *obj, *obj2, *prototype;
MochaObjectStack *top;
MochaProperty *prop;
MochaInt ival, ival2;
MochaFloat fval, fval2;
MochaSymbol *sym;
MochaAtom *atom, *atom2, *atom3;
MochaFunction *fun;
MochaSlot slot;
*result = MOCHA_void;
oldslink = mc->staticLink;
mc->staticLink = slink;
oldpc = mc->pc;
oldscript = mc->script;
mc->script = script;
onBranch = mc->branchCallback;
ok = MOCHA_TRUE;
#define CHECK_BRANCH() { \
if (onBranch && !(*onBranch)(mc, script)) { \
ok = MOCHA_FALSE; \
goto out; \
} \
}
sp = &mc->stack;
oldtos = sp->ptr;
if (oldtos + script->depth >= sp->limit) {
ReportStackOverflow(mc);
return MOCHA_FALSE;
}
pc = script->code;
end = pc + script->length;
while (pc < end) {
taint = mc->taintInfo->accum;
mc->pc = pc;
op = *pc;
cs = &mocha_CodeSpec[op];
len = cs->length;
#ifdef DEBUG
if (mc->tracefp) {
int nuses, n;
MochaDatum d;
fprintf(mc->tracefp, "%4u: ", mocha_PCtoLineNumber(script, pc));
mocha_Disassemble1(mc, script, pc, pc - script->code, mc->tracefp);
nuses = cs->nuses;
if (nuses) {
for (n = nuses; n > 0; n--) {
d = sp->ptr[-n];
if (mocha_RawDatumToString(mc, d, &atom)) {
fprintf(mc->tracefp, "%s %s",
(n == nuses) ? " inputs:" : ",",
atom_name(atom));
mocha_DropAtom(mc, atom);
}
}
putc('\n', mc->tracefp);
}
}
#endif
switch (op) {
case MOP_NOP:
ALLOCA_GC();
break;
case MOP_PUSH:
Push(mc, MOCHA_void); /* no need to taint (yet) */
break;
case MOP_POP:
aval = rval = Pop(mc, MOCHA_FALSE);
ok = mocha_ResolveValue(mc, &rval);
if (rval.tag != MOCHA_PROPERTY) {
PR_ASSERT(rval.tag != MOCHA_OBJECTSTACK);
mocha_HoldRef(mc, &rval);
mocha_DropRef(mc, result);
*result = rval;
}
mocha_DropRef(mc, &aval);
if (!ok)
goto out;
break;
case MOP_ENTER:
rval = Pop(mc, MOCHA_FALSE);
ok = mocha_DatumToObject(mc, rval, &obj);
mocha_DropRef(mc, &rval);
if (!obj) {
if (mocha_RawDatumToString(mc, rval, &atom)) {
MOCHA_ReportError(mc,
"%s can't be used in a with statement",
atom_name(atom));
mocha_DropAtom(mc, atom);
}
ok = MOCHA_FALSE;
}
if (!ok) goto out;
ok = mocha_PushObject(mc, obj, &top);
if (!ok) goto out;
MOCHA_INIT_FULL_DATUM(mc, &rval, MOCHA_OBJECTSTACK,
0, MOCHA_TAINT_IDENTITY,
u.ptr, top);
MOCHA_DropObject(mc, obj);
Push(mc, rval);
break;
case MOP_LEAVE:
PR_ASSERT(sp->ptr[-1].tag == MOCHA_OBJECTSTACK);
(void) Pop(mc, MOCHA_TRUE);
break;
case MOP_RETURN:
CHECK_BRANCH();
aval = rval = Pop(mc, MOCHA_FALSE);
ok = mocha_ResolveValue(mc, &rval);
mocha_HoldRef(mc, &rval);
sp->frame->rval = rval;
mocha_DropRef(mc, &aval);
goto out;
case MOP_GOTO:
CHECK_BRANCH();
len = GET_JUMP_OFFSET(pc);
break;
case MOP_IFEQ:
CHECK_BRANCH();
if (!(ok = PopBoolean(mc, &bval)))
goto out;
if (bval == MOCHA_FALSE)
len = GET_JUMP_OFFSET(pc);
taint = mc->taintInfo->accum;
break;
case MOP_IFNE:
CHECK_BRANCH();
if (!(ok = PopBoolean(mc, &bval)))
goto out;
if (bval != MOCHA_FALSE)
len = GET_JUMP_OFFSET(pc);
taint = mc->taintInfo->accum;
break;
case MOP_IN:
aval = rval = Pop(mc, MOCHA_FALSE);
lval = Pop(mc, MOCHA_TRUE);
/* If the thing to the right of 'in' isn't an object, break. */
ok = mocha_DatumToObject(mc, rval, &obj);
mocha_DropRef(mc, &aval);
if (!ok) goto out;
if (!obj) {
PushBoolean(mc, MOCHA_FALSE);
break;
}
/* Save obj held by obj2 to suppress clone-parent properties. */
obj2 = MOCHA_HoldObject(mc, obj);
again:
prototype = obj->prototype;
/*
** Don't hold a property reference here, there is no way yet for
** Mocha users to remove properties (XXX).
*/
vp = sp->ptr - 1;
if (vp->tag == MOCHA_UNDEF) {
/* Let lazy reflectors be eager so for-in works for them. */
ok = OBJ_LIST_PROPERTIES(mc, obj);
if (!ok) {
MOCHA_DropObject(mc, obj);
MOCHA_DropObject(mc, obj2);
goto out;
}
/* Set the iterator to point to the first property. */
prop = obj->scope->props;
/* Rewrite the iterator tag so we know to do the next case. */
vp->tag = MOCHA_PROPERTY;
vp->u.pair.obj = MOCHA_HoldObject(mc, obj);
} else {
/* Use the iterator to find the next property. */
PR_ASSERT(vp->tag == MOCHA_PROPERTY);
prop = (MochaProperty *)vp->u.pair.sym; /* XXX type me please */
/* If we're enumerating a prototype, reset obj and prototype. */
if (obj != vp->u.pair.obj) {
MOCHA_DropObject(mc, obj);
obj = MOCHA_HoldObject(mc, vp->u.pair.obj);
prototype = obj->prototype;
}
PR_ASSERT(!prop || prop->lastsym->scope == obj->scope);
}
MOCHA_DropObject(mc, obj);
/* Skip pre-defined properties for backward compatibility. */
while (prop) {
if (prop->datum.flags & MDF_ENUMERATE) {
/* Have we already enumerated a clone of this property? */
atom = sym_atom(prop->lastsym);
mocha_LookupSymbol(mc, obj2->scope, atom, MLF_GET, &sym);
if (sym && sym->entry.value == prop)
break;
}
prop = prop->next;
}
if (!prop) {
/* Enumerate prototype properties, if there are any. */
if (prototype) {
obj = MOCHA_HoldObject(mc, prototype);
MOCHA_DropObject(mc, vp->u.pair.obj);
vp->u.pair.obj = MOCHA_HoldObject(mc, obj);
vp->u.pair.sym = (MochaSymbol *)obj->scope->props;
goto again;
}
/* End of property list -- terminate this loop. */
PushBoolean(mc, MOCHA_FALSE);
MOCHA_DropObject(mc, obj2);
break;
}
MOCHA_DropObject(mc, obj2);
/* Make a string for the iterator name and assign it to lval. */
atom = sym_atom(prop->lastsym);
vp->u.pair.sym = (MochaSymbol *)prop->next;
Push(mc, lval);
PushString(mc, atom);
#ifdef DEBUG_brendan
mc->pc = pc;
#endif
ok = Assign(mc, &taint);
if (!ok)
goto out;
/* Throw away Assign()'s result and push true to keep looping. */
(void) Pop(mc, MOCHA_TRUE);
PushBoolean(mc, MOCHA_TRUE);
break;
case MOP_DUP:
PR_ASSERT(sp->ptr > sp->base);
Push(mc, sp->ptr[-1]);
break;
case MOP_ASSIGN:
#ifdef DEBUG_brendan
mc->pc = pc;
#endif
ok = Assign(mc, &taint);
if (!ok)
goto out;
break;
#define INTEGEROP(OP, EXTRA_CODE, LEFT_CAST) { \
valid = MOCHA_TRUE; \
if (!(ok = PopInt(mc,&ival2,&valid)) || !(ok = PopInt(mc,&ival,&valid))) \
goto out; \
EXTRA_CODE \
if (valid) \
PushNumber(mc, LEFT_CAST ival OP ival2); \
else \
PushNumber(mc, MOCHA_NaN.u.fval); \
}
#define BITWISEOP(OP) INTEGEROP(OP, (void) 0;, (MochaInt))
#define SIGNEDSHIFT(OP) INTEGEROP(OP, ival2 &= 31;, (MochaInt))
#define UNSIGNEDSHIFT(OP) INTEGEROP(OP, ival2 &= 31;, (MochaUint))
case MOP_BITOR:
BITWISEOP(|);
break;
case MOP_BITXOR:
BITWISEOP(^);
break;
case MOP_BITAND:
BITWISEOP(&);
break;
#ifdef XP_PC
#define COMPARE_FLOATS(LVAL, OP, RVAL) \
((MOCHA_FLOAT_IS_NaN(LVAL) || MOCHA_FLOAT_IS_NaN(RVAL)) \
? MOCHA_FALSE \
: (LVAL) OP (RVAL))
#else
#define COMPARE_FLOATS(LVAL, OP, RVAL) ((LVAL) OP (RVAL))
#endif
#define COMPARISON(OP, EXTRA_CODE) { \
aval = rval = Pop(mc, MOCHA_FALSE); \
aval2 = lval = Pop(mc, MOCHA_FALSE); \
ok = mocha_ResolveValue(mc, &lval) && mocha_ResolveValue(mc, &rval); \
if (ok) { \
atom = 0; \
EXTRA_CODE \
if (ResolveString(mc,lval,&atom) && ResolveString(mc,rval,&atom2)) { \
bval = strcoll(atom_name(atom), atom_name(atom2)) OP 0; \
mocha_DropAtom(mc, atom); \
mocha_DropAtom(mc, atom2); \
} else { \
if (atom) mocha_DropAtom(mc, atom); \
ok = mocha_DatumToNumber(mc, lval, &fval) && \
mocha_DatumToNumber(mc, rval, &fval2); \
if (ok) \
bval = COMPARE_FLOATS(fval, OP, fval2); \
} \
} \
mocha_DropRef(mc, &aval); \
mocha_DropRef(mc, &aval2); \
if (!ok) \
goto out; \
PushBoolean(mc, bval); \
}
#define EQUALITYOP(OP) { \
COMPARISON(OP, \
if ((lval.tag == MOCHA_FUNCTION || lval.tag == MOCHA_OBJECT) && \
(rval.tag == MOCHA_FUNCTION || rval.tag == MOCHA_OBJECT)) { \
bval = lval.u.obj OP rval.u.obj; \
} else if (MOCHA_DATUM_IS_NULL(lval) || MOCHA_DATUM_IS_NULL(rval)) { \
obj = obj2 = 0; \
ok = mocha_DatumToObject(mc, lval, &obj) && \
mocha_DatumToObject(mc, rval, &obj2); \
if (ok) \
bval = obj OP obj2; \
if (obj) MOCHA_DropObject(mc, obj); \
if (obj2) MOCHA_DropObject(mc, obj2); \
} else \
) \
}
#define RELATIONAL(OP) COMPARISON(OP, (void) 0;)
case MOP_EQ:
EQUALITYOP(==);
break;
case MOP_NE:
EQUALITYOP(!=);
break;
case MOP_LT:
RELATIONAL(<);
break;
case MOP_LE:
RELATIONAL(<=);
break;
case MOP_GT:
RELATIONAL(>);
break;
case MOP_GE:
RELATIONAL(>=);
break;
#undef COMPARISON
#undef EQUALITYOP
#undef RELATIONAL
case MOP_LSH:
SIGNEDSHIFT(<<);
break;
case MOP_RSH:
SIGNEDSHIFT(>>);
break;
case MOP_URSH:
UNSIGNEDSHIFT(>>);
break;
#undef INTEGEROP
#undef BITWISEOP
#undef SIGNEDSHIFT
#undef UNSIGNEDSHIFT
case MOP_ADD:
rval = Pop(mc, MOCHA_FALSE);
lval = Pop(mc, MOCHA_FALSE);
atom = atom2 = 0;
if (ResolveString(mc,lval,&atom) || ResolveString(mc,rval,&atom2)) {
ok = atom ? mocha_DatumToString(mc, rval, &atom2)
: mocha_DatumToString(mc, lval, &atom);
if (ok) {
if (atom == MOCHA_empty.u.atom)
atom3 = atom2;
else if (atom2 == MOCHA_empty.u.atom)
atom3 = atom;
else
atom3 = CatStrings(mc, atom, atom2);
if (!atom3)
ok = MOCHA_FALSE;
}
if (ok)
PushString(mc, atom3);
if (atom) mocha_DropAtom(mc, atom);
if (atom2) mocha_DropAtom(mc, atom2);
} else {
ok = mocha_DatumToNumber(mc, lval, &fval) &&
mocha_DatumToNumber(mc, rval, &fval2);
if (ok)
PushNumber(mc, fval + fval2);
}
mocha_DropRef(mc, &lval);
mocha_DropRef(mc, &rval);
if (!ok)
goto out;
break;
#define BINARYOP(OP) { \
if (!(ok = PopNumber(mc, &fval2)) || !(ok = PopNumber(mc, &fval))) \
goto out; \
PushNumber(mc, fval OP fval2); \
}
case MOP_SUB:
BINARYOP(-);
break;
case MOP_MUL:
BINARYOP(*);
break;
case MOP_DIV:
case MOP_MOD:
if (!(ok = PopNumber(mc, &fval2)) || !(ok = PopNumber(mc, &fval)))
goto out;
if (fval2 == 0)
PushNumber(mc, MOCHA_NaN.u.fval);
else if (op == MOP_DIV)
PushNumber(mc, fval / fval2);
else
PushNumber(mc, fmod(fval, fval2));
break;
case MOP_NOT:
if (!(ok = PopBoolean(mc, &bval)))
goto out;
PushBoolean(mc, !bval);
break;
case MOP_BITNOT:
valid = MOCHA_TRUE;
if (!(ok = PopInt(mc, &ival, &valid)))
goto out;
if (!valid)
PushNumber(mc, MOCHA_NaN.u.fval);
else
PushNumber(mc, ~ival);
break;
case MOP_NEG:
if (!(ok = PopNumber(mc, &fval)))
goto out;
PushNumber(mc, -fval);
break;
case MOP_NEW:
CHECK_BRANCH();
/* Get argc from immediate and find the constructor function. */
argc = pc[1];
vp = sp->ptr - (argc + 1);
PR_ASSERT(vp >= sp->base);
ok = mocha_DatumToFunction(mc, *vp, &fun);
if (!ok)
goto out;
/* Find the constructor name in order to name its new scope. */
lval = *vp;
ok = mocha_ResolveSymbol(mc, &lval, MLF_GET);
if (!ok) {
MOCHA_DropObject(mc, &fun->object);
goto out;
}
/* Get the prototype object for this constructor function. */
ok = mocha_LookupSymbol(mc, fun->object.scope, mocha_prototypeAtom,
MLF_GET, &sym);
if (!ok) {
MOCHA_DropObject(mc, &fun->object);
goto out;
}
if (!sym ||
sym->type != SYM_PROPERTY ||
(prop = sym_property(sym))->datum.tag != MOCHA_OBJECT ||
!(prototype = prop->datum.u.obj)) {
prototype = mocha_NewObjectByClass(mc, &mocha_ObjectClass);
if (!prototype) {
MOCHA_DropObject(mc, &fun->object);
ok = MOCHA_FALSE;
goto out;
}
if (!mocha_GetMutableScope(mc, prototype) ||
!mocha_SetPrototype(mc, fun, prototype)) {
MOCHA_DestroyObject(mc, prototype);
MOCHA_DropObject(mc, &fun->object);
ok = MOCHA_FALSE;
goto out;
}
}
/* Create a new user-allocated object. */
obj = mocha_NewObjectByPrototype(mc, prototype);
if (!obj) {
MOCHA_DropObject(mc, &fun->object);
ok = MOCHA_FALSE;
goto out;
}
obj = MOCHA_HoldObject(mc, obj);
/* Find the constructor property in obj's (prototype's) scope. */
ok = mocha_LookupSymbol(mc, obj->scope, mocha_constructorAtom,
MLF_GET, &sym);
if (!ok) {
obj->clazz = &mocha_ObjectClass;
MOCHA_DropObject(mc, obj);
MOCHA_DropObject(mc, &fun->object);
goto out;
}
/* Mutate the function reference at vp into a symbol ref. */
mocha_DropRef(mc, vp);
vp->tag = MOCHA_SYMBOL;
vp->u.pair.obj = obj;
vp->u.pair.sym = sym;
mocha_HoldRef(mc, vp);
/* Now we have an object with a constructor method -- call it. */
#ifdef DEBUG_brendan
mc->pc = pc;
#endif
ok = Call(mc, argc);
MOCHA_DropObject(mc, &fun->object);
if (!ok) {
obj->clazz = &mocha_ObjectClass;
MOCHA_DropObject(mc, obj);
goto out;
}
/* Don't reset taint accumulator on return from function. */
taint = mc->taintInfo->accum;
/* Pop the return value, taking care not to drop prematurely. */
rval = Pop(mc, MOCHA_FALSE);
if (rval.tag == MOCHA_OBJECT && rval.u.obj != obj) {
obj->clazz = &mocha_ObjectClass;
MOCHA_DropObject(mc, obj);
obj = MOCHA_HoldObject(mc, rval.u.obj);
}
mocha_DropRef(mc, &rval);
/* Then push the newly constructed object. */
PushObject(mc, obj);
/* Finally, drop obj -- it may have been the return value(!). */
MOCHA_DropObject(mc, obj);
break;
case MOP_TYPEOF:
lval = Pop(mc, MOCHA_FALSE);
ok = mocha_TypeOfDatum(mc, lval, &atom);
mocha_DropRef(mc, &lval);
if (!ok) goto out;
PushString(mc, atom ? atom : MOCHA_empty.u.atom);
break;
case MOP_VOID:
(void) Pop(mc, MOCHA_TRUE);
Push(mc, MOCHA_void);
break;
case MOP_INC:
case MOP_DEC:
/* The operand must contain a number. */
aval = lval = Pop(mc, MOCHA_FALSE);
ok = mocha_DatumToNumber(mc, lval, &fval);
if (!ok) {
mocha_DropRef(mc, &aval);
goto out;
}
/* Push the post- or pre-incremented value. */
if (op == MOP_INC)
PushNumber(mc, pc[1] ? fval++ : ++fval);
else
PushNumber(mc, pc[1] ? fval-- : --fval);
/* XXX Need two stack slots to call Assign(). */
ok = (sp->ptr + 2 < sp->limit);
if (!ok) {
ReportStackOverflow(mc);
mocha_DropRef(mc, &aval);
goto out;
}
/* Assign the resulting number to lval. */
Push(mc, lval);
PushNumber(mc, fval);
mocha_DropRef(mc, &aval);
#ifdef DEBUG_brendan
mc->pc = pc;
#endif
ok = Assign(mc, &taint);
if (!ok)
goto out;
(void) Pop(mc, MOCHA_TRUE);
break;
case MOP_MEMBER:
case MOP_LMEMBER:
/* Pop an atom (held by an atom map) naming the member. */
rval = Pop(mc, MOCHA_TRUE);
PR_ASSERT(rval.tag == MOCHA_ATOM);
/* Pop the left part and resolve it to an object. */
lval = Pop(mc, MOCHA_FALSE);
ok = mocha_DatumToObject(mc, lval, &obj);
mocha_DropRef(mc, &lval);
if (!ok) goto out;
if (!obj) {
if (mocha_RawDatumToString(mc, lval, &atom)) {
MOCHA_ReportError(mc, "%s has no property named '%s'",
atom_name(atom), atom_name(rval.u.atom));
mocha_DropAtom(mc, atom);
}
ok = MOCHA_FALSE;
goto out;
}
/* Lookup atom in object scope, push undef symbol if not found. */
atom = rval.u.atom;
sym = 0;
if (op == MOP_LMEMBER)
ok = mocha_GetMutableScope(mc, obj);
if (ok) {
ok = mocha_LookupSymbol(mc, obj->scope, atom,
(op == MOP_LMEMBER) ? MLF_SET : MLF_GET,
&sym);
if (ok && !sym &&
(op == MOP_LMEMBER ||
(ok = mocha_GetMutableScope(mc, obj)))) { /* XXXhertme! */
/* Create a new undefined symbol in a mutable scope. */
sym = mocha_DefineSymbol(mc, obj->scope, atom,
SYM_UNDEF, 0);
ok = (sym != 0);
}
}
if (sym)
PushSymbol(mc, obj, sym);
MOCHA_DropObject(mc, obj);
if (!ok) goto out;
break;
case MOP_INDEX:
case MOP_LINDEX:
/* Pop the index (without dropping it!) and resolve it. */
aval = rval = Pop(mc, MOCHA_FALSE);
ok = mocha_ResolveValue(mc, &rval) &&
mocha_RawDatumToString(mc, rval, &atom);
mocha_DropRef(mc, &aval);
if (!ok)
goto out;
/* Pop the array and resolve it to an object. */
lval = Pop(mc, MOCHA_FALSE);
ok = mocha_DatumToObject(mc, lval, &obj);
mocha_DropRef(mc, &lval);
if (!ok || !obj) {
if (ok && mocha_RawDatumToString(mc, lval, &atom2)) {
MOCHA_ReportError(mc,
"%s has no property indexed by '%s'",
atom_name(atom2), atom_name(atom));
mocha_DropAtom(mc, atom2);
}
mocha_DropAtom(mc, atom);
ok = MOCHA_FALSE;
goto out;
}
/* If rval is a nonnegative integer, treat it as a slot number. */
slot = -1;
if (mocha_RawDatumToNumber(mc, rval, &fval)) {
ival = (MochaInt)fval;
if (ival >= 0 && (MochaFloat)ival == fval)
slot = ival;
}
/* Lookup the indexed symbol, defining a new one if not found. */
sym = 0;
if (op == MOP_LINDEX)
ok = mocha_GetMutableScope(mc, obj);
if (ok) {
ok = mocha_LookupSymbol(mc, obj->scope, atom,
(op == MOP_LINDEX) ? MLF_SET : MLF_GET,
&sym);
if (ok && !sym &&
(op == MOP_LINDEX ||
(ok = mocha_GetMutableScope(mc, obj)))) { /* XXXhertme! */
/*
** Create a new undefined symbol in a mutable scope.
** XXX want a way to distinguish o[0x10] from o["16"]
*/
sym = (slot < 0)
? mocha_DefineSymbol(mc, obj->scope, atom, SYM_UNDEF, 0)
: mocha_SetProperty(mc, obj->scope, atom, slot,
MOCHA_null);
ok = (sym != 0);
}
}
mocha_DropAtom(mc, atom);
if (sym)
PushSymbol(mc, obj, sym);
MOCHA_DropObject(mc, obj);
if (!ok) goto out;
break;
case MOP_CALL:
CHECK_BRANCH();
/* Resolve *vp to a function and call it. */
argc = pc[1];
#ifdef DEBUG_brendan
mc->pc = pc;
#endif
ok = Call(mc, argc);
if (!ok)
goto out;
/* Don't reset taint accumulator on return from function. */
taint = mc->taintInfo->accum;
break;
case MOP_NAME:
MOCHA_INIT_FULL_DATUM(mc, &lval, MOCHA_ATOM,
0, MOCHA_TAINT_IDENTITY,
u.atom, GET_CONST_ATOM(mc, script, pc));
Push(mc, lval);
break;
case MOP_NUMBER:
atom = GET_CONST_ATOM(mc, script, pc);
PushNumber(mc, atom->fval);
break;
case MOP_STRING:
atom = GET_CONST_ATOM(mc, script, pc);
PushString(mc, atom);
break;
case MOP_ZERO:
PushNumber(mc, 0);
break;
case MOP_ONE:
PushNumber(mc, 1);
break;
case MOP_NULL:
PushObject(mc, 0);
break;
case MOP_THIS:
PushObject(mc, sp->frame ? sp->frame->thisp : slink);
break;
case MOP_FALSE:
case MOP_TRUE:
PushBoolean(mc, (op == MOP_TRUE) ? MOCHA_TRUE : MOCHA_FALSE);
break;
#ifdef MOCHA_HAS_DELETE_OPERATOR
case MOP_DELETE:
/* Delete the operand, which must be an object but may be null. */
lval = rval = Pop(mc, MOCHA_FALSE);
ok = mocha_DatumToObject(mc, rval, &obj);
mocha_DropRef(mc, &rval);
if (!ok) goto out;
ok = mocha_ResolveSymbol(mc, &lval, MLF_SET);
if (!ok) goto out;
if (lval.tag != MOCHA_SYMBOL) {
if (mocha_RawDatumToString(mc, rval, &atom)) {
MOCHA_ReportError(mc, "%s can't be deleted",
atom_name(atom));
mocha_DropAtom(mc, atom);
}
goto out;
}
Push(mc, lval);
PushObject(mc, 0);
#ifdef DEBUG_brendan
mc->pc = pc;
#endif
ok = Assign(mc, &taint);
if (!ok)
goto out;
break;
#endif /* MOCHA_HAS_DELETE_OPERATOR */
default:
MOCHA_ReportError(mc, "unimplemented Mocha bytecode %d", op);
break;
}
pc += len;
mc->taintInfo->accum = taint;
#ifdef DEBUG
if (mc->tracefp) {
int ndefs, n;
MochaDatum d;
ndefs = cs->ndefs;
if (ndefs) {
for (n = ndefs; n > 0; n--) {
d = sp->ptr[-n];
if (mocha_RawDatumToString(mc, d, &atom)) {
fprintf(mc->tracefp, "%s %s",
(n == ndefs) ? " output:" : ",",
atom_name(atom));
mocha_DropAtom(mc, atom);
}
}
putc('\n', mc->tracefp);
}
}
#endif
}
out:
/*
** Pop anything left by an exception on the stack, taking care not to pop
** new variables created by eval("var x = ...").
*/
if (sp->frame) {
bottom = sp->frame->vars + sp->frame->nvars;
if (oldtos < bottom)
oldtos = bottom;
}
while (sp->ptr > oldtos)
Pop(mc, MOCHA_TRUE);
/*
** Restore the previous frame's execution state.
*/
mc->staticLink = oldslink;
mc->pc = oldpc;
mc->script = oldscript;
ALLOCA_GC();
/*
** Drop result if there was an error.
*/
if (!ok) {
mocha_DropRef(mc, result);
*result = MOCHA_void;
}
return ok;
}