summaryrefslogtreecommitdiff
path: root/appl/lib/ecmascript/exec.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/ecmascript/exec.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/lib/ecmascript/exec.b')
-rw-r--r--appl/lib/ecmascript/exec.b863
1 files changed, 863 insertions, 0 deletions
diff --git a/appl/lib/ecmascript/exec.b b/appl/lib/ecmascript/exec.b
new file mode 100644
index 00000000..d182df82
--- /dev/null
+++ b/appl/lib/ecmascript/exec.b
@@ -0,0 +1,863 @@
+exec(ex: ref Exec, code: ref Code): Completion
+{
+ ssp := ex.sp;
+
+ r := estmt(ex, code, 0, code.npc);
+
+ if(r.kind == CThrow)
+ ex.sp = ssp;
+
+ if(ssp != ex.sp)
+ runtime(ex, InternalError, "internal error: exec stack not balanced");
+
+ if(r.lab != nil)
+ runtime(ex, InternalError, "internal error: label out of stack");
+ return r;
+}
+
+estmt(ex: ref Exec, code: ref Code, pc, epc: int): Completion
+{
+ e: ref Ref;
+ ev: ref Val;
+ k, apc, pc2, apc2, pc3, apc3, c: int;
+ lab: string;
+ labs: list of string;
+
+ osp := ex.sp;
+
+{
+ v : ref Val = nil;
+ k1 := CNormal;
+ while(pc < epc){
+ v1 : ref Val = nil;
+
+ labs = nil;
+ op := int code.ops[pc++];
+ while(op == Llabel){
+ (pc, c) = getconst(code.ops, pc);
+ labs = code.strs[c] :: labs;
+ op = int code.ops[pc++];
+ }
+ if(debug['e'] > 1)
+ print("estmt(pc %d, sp %d) %s\n", pc-1, ex.sp, tokname(op));
+ case op {
+ Lbreak =>
+ return (CBreak, v, nil);
+ Lcontinue =>
+ return (CContinue, v, nil);
+ Lbreaklab =>
+ (pc, c) = getconst(code.ops, pc);
+ return (CBreak, v, code.strs[c]);
+ Lcontinuelab =>
+ (pc, c) = getconst(code.ops, pc);
+ return (CContinue, v, code.strs[c]);
+ Lreturn =>
+ (pc, v) = eexpval(ex, code, pc, code.npc);
+ return (CReturn, v, nil);
+ '{' =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (k1, v1, lab) = estmt(ex, code, pc, apc);
+ pc = apc;
+ Lif =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, ev) = eexpval(ex, code, pc, apc);
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc2, apc2) = getjmp(code.ops, apc);
+ if(toBoolean(ex, ev) != false)
+ (k1, v1, lab) = estmt(ex, code, pc, apc);
+ else if(pc2 != apc2)
+ (k1, v1, lab) = estmt(ex, code, pc2, apc2);
+ pc = apc2;
+ Lwhile =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc2, apc2) = getjmp(code.ops, apc);
+ for(;;){
+ (nil, ev) = eexpval(ex, code, pc, apc);
+ if(toBoolean(ex, ev) == false)
+ break;
+ (k, v1, lab) = estmt(ex, code, pc2, apc2);
+ if(v1 != nil)
+ v = v1;
+ if(k == CBreak || k == CContinue){
+ if(initlabs(lab, labs)){
+ if(k == CBreak)
+ break;
+ else
+ continue;
+ }
+ else
+ return (k, v1, lab);
+ }
+ if(k == CReturn || k == CThrow)
+ return (k, v1, nil);
+ }
+ pc = apc2;
+ Ldo =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc2, apc2) = getjmp(code.ops, apc);
+ for(;;){
+ (k, v1, lab) = estmt(ex, code, pc, apc);
+ if(v1 != nil)
+ v = v1;
+ if(k == CBreak || k == CContinue){
+ if(initlabs(lab, labs)){
+ if(k == CBreak)
+ break;
+ else
+ continue;
+ }
+ else
+ return (k, v1, lab);
+ }
+ if(k == CReturn || k == CThrow)
+ return (k, v1, nil);
+ (nil, ev) = eexpval(ex, code, pc2, apc2);
+ if(toBoolean(ex, ev) == false)
+ break;
+ }
+ pc = apc2;
+ Lfor or
+ Lforvar =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, nil) = eexpval(ex, code, pc, apc);
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc2, apc2) = getjmp(code.ops, apc);
+ (pc3, apc3) = getjmp(code.ops, apc2);
+ for(;;){
+ (nil, e) = eexp(ex, code, pc, apc);
+ if(e != nil && toBoolean(ex, getValue(ex, e)) == false)
+ break;
+ (k, v1, lab) = estmt(ex, code, pc3, apc3);
+ if(v1 != nil)
+ v = v1;
+ if(k == CBreak || k == CContinue){
+ if(initlabs(lab, labs)){
+ if(k == CBreak)
+ break;
+ else
+ continue;
+ }
+ else
+ return (k, v1, lab);
+ }
+ if(k == CReturn || k == CThrow)
+ return (k, v1, nil);
+ eexpval(ex, code, pc2, apc2);
+ }
+ pc = apc3;
+ Lforin or
+ Lforvarin =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc2, apc2) = getjmp(code.ops, apc);
+ (pc3, apc3) = getjmp(code.ops, apc2);
+ if(op == Lforvarin){
+ (nil, nil) = eexp(ex, code, pc, apc);
+ # during for only evaluate the id, not the initializer
+ apc = pc + 1;
+ }
+ (nil, ev) = eexpval(ex, code, pc2, apc2);
+ bo := toObject(ex, ev);
+
+ #
+ # note this won't enumerate host properties
+ #
+ enum:
+ for(o := bo; o != nil; o = o.prototype){
+ if(o.host != nil && o.host != me)
+ continue;
+ for(i := 0; i < len o.props; i++){
+ if(o.props[i] == nil
+ || (o.props[i].attr & DontEnum)
+ || propshadowed(bo, o, o.props[i].name))
+ continue;
+ (nil, e) = eexp(ex, code, pc, apc);
+ putValue(ex, e, strval(o.props[i].name));
+ (k, v1, lab) = estmt(ex, code, pc3, apc3);
+ if(v1 != nil)
+ v = v1;
+ if(k == CBreak || k == CContinue){
+ if(initlabs(lab, labs)){
+ if(k == CBreak)
+ break enum;
+ else
+ continue enum;
+ }
+ else
+ return (k, v1, lab);
+ }
+ if(k == CReturn || k == CThrow)
+ return (k, v1, nil);
+ }
+ }
+ pc = apc3;
+ Lwith =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, ev) = eexpval(ex, code, pc, apc);
+ pushscope(ex, toObject(ex, ev));
+ (pc, apc) = getjmp(code.ops, pc);
+ (k1, v1, lab) = estmt(ex, code, pc, apc);
+ popscope(ex);
+ pc = apc;
+ ';' =>
+ ;
+ Lvar =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, nil) = eexp(ex, code, pc, apc);
+ Lswitch =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (pc, ev) = eexpval(ex, code, pc, apc);
+ (pc, apc) = getjmp(code.ops, pc);
+ (k1, v1, lab) = ecaseblk(ex, code, ev, pc, apc, labs);
+ pc = apc;
+ Lthrow =>
+ (pc, v) = eexpval(ex, code, pc, code.npc);
+ ex.error = toString(ex, v);
+ return (CThrow, v, nil);
+ Lprint =>
+ (pc, v1) = eexpval(ex, code, pc, code.npc);
+ print("%s\n", toString(ex, v1));
+ Ltry =>
+ (pc, apc) = getjmp(code.ops, pc);
+ (k1, v1, lab) = estmt(ex, code, pc, apc);
+ (kc, vc) := (k1, v1);
+ (pc, apc) = getjmp(code.ops, apc);
+ if(pc != apc){
+ (pc, c) = getconst(code.ops, ++pc);
+ if(k1 == CThrow){
+ o := mkobj(ex.objproto, "Object");
+ valinstant(o, DontDelete, code.strs[c], v1);
+ pushscope(ex, o);
+ (k1, v1, lab) = estmt(ex, code, pc, apc);
+ popscope(ex);
+ if(k1 != CNormal)
+ (kc, vc) = (k1, v1);
+ }
+ }
+ (pc, apc) = getjmp(code.ops, apc);
+ if(pc != apc){
+ (k, v, lab) = estmt(ex, code, pc, apc);
+ if(k == CNormal)
+ (k1, v1) = (kc, vc);
+ else
+ (k1, v1) = (k, v);
+ }
+ pc = apc;
+ * =>
+ (pc, e) = eexp(ex, code, pc-1, code.npc);
+ if(e != nil)
+ v1 = getValue(ex, e);
+ if(debug['v'])
+ print("%s\n", toString(ex, v1));
+ }
+
+ if(v1 != nil)
+ v = v1;
+ if(k1 == CBreak && lab != nil && inlabs(lab, labs))
+ (k1, lab) = (CNormal, nil);
+ if(k1 != CNormal)
+ return (k1, v, lab);
+ }
+ return (CNormal, v, nil);
+}
+exception{
+ "throw" =>
+ ex.sp = osp;
+ return (CThrow, ex.errval, nil);
+}
+}
+
+ecaseblk(ex : ref Exec, code : ref Code, sv : ref Val, pc, epc : int, labs: list of string) : Completion
+{ defpc, nextpc, clausepc, apc : int;
+ ev : ref Val;
+ lab: string;
+
+ k := CNormal;
+ v := undefined;
+ matched := 0;
+
+ (pc, defpc) = getjmp(code.ops, pc);
+ clausepc = pc;
+ (pc, nextpc) = getjmp(code.ops, pc);
+ for (; pc <= epc; (clausepc, (pc, nextpc)) = (nextpc, getjmp(code.ops, nextpc))) {
+ if (nextpc == epc) {
+ if (matched || defpc == epc)
+ break;
+ # do the default
+ matched = 1;
+ nextpc = defpc;
+ continue;
+ }
+ if (!matched && clausepc == defpc)
+ # skip default case - still scanning guards
+ continue;
+ if (clausepc != defpc) {
+ # only case clauses have guard exprs
+ (pc, apc) = getjmp(code.ops, pc);
+ if (matched)
+ pc = apc;
+ else {
+ (pc, ev) = eexpval(ex, code, pc, apc);
+ if (identical(sv, ev))
+ matched = 1;
+ else
+ continue;
+ }
+ }
+ (k, v, lab) = estmt(ex, code, pc, nextpc);
+ if(k == CBreak && initlabs(lab, labs))
+ return (CNormal, v, nil);
+ if(k == CBreak || k == CContinue || k == CReturn || k == CThrow)
+ return (k, v, lab);
+ }
+ return (k, v, lab);
+}
+
+identical(v1, v2 : ref Val) : int
+{
+ if (v1.ty != v2.ty)
+ return 0;
+ ret := 0;
+ case v1.ty{
+ TUndef or
+ TNull =>
+ ret = 1;
+ TNum =>
+ if(v1.num == v2.num)
+ ret = 1;
+ TBool =>
+ if(v1 == v2)
+ ret = 1;
+ TStr =>
+ if(v1.str == v2.str)
+ ret = 1;
+ TObj =>
+ if(v1.obj == v2.obj)
+ ret = 1;
+ TRegExp =>
+ if(v1.rev == v2.rev)
+ ret = 1;
+ }
+ return ret;
+}
+
+eexpval(ex: ref Exec, code: ref Code, pc, epc: int): (int, ref Val)
+{
+ e: ref Ref;
+
+ (pc, e) = eexp(ex, code, pc, epc);
+ if(e == nil)
+ v := undefined;
+ else
+ v = getValue(ex, e);
+ return (pc, v);
+}
+
+eexp(ex: ref Exec, code: ref Code, pc, epc: int): (int, ref Ref)
+{
+ o, th: ref Obj;
+ a1: ref Ref;
+ v, v1, v2: ref Val;
+ s: string;
+ r1, r2: real;
+ c, apc, i1, i2: int;
+
+ savesp := ex.sp;
+out: while(pc < epc){
+ op := int code.ops[pc++];
+ if(debug['e'] > 1){
+ case op{
+ Lid or
+ Lstr or
+ Lregexp =>
+ (nil, c) = getconst(code.ops, pc);
+ print("eexp(pc %d, sp %d) %s '%s'\n", pc-1, ex.sp, tokname(op), code.strs[c]);
+ Lnum =>
+ (nil, c) = getconst(code.ops, pc);
+ print("eexp(pc %d, sp %d) %s '%g'\n", pc-1, ex.sp, tokname(op), code.nums[c]);
+ * =>
+ print("eexp(pc %d, sp %d) %s\n", pc-1, ex.sp, tokname(op));
+ }
+ }
+ case op{
+ Lthis =>
+ v1 = objval(ex.this);
+ Lnum =>
+ (pc, c) = getconst(code.ops, pc);
+ v1 = numval(code.nums[c]);
+ Lstr =>
+ (pc, c) = getconst(code.ops, pc);
+ v1 = strval(code.strs[c]);
+ Lregexp =>
+ (pc, c) = getconst(code.ops, pc);
+ (p, f) := rsplit(code.strs[c]);
+ o = nregexp(ex, nil, array[] of { strval(p), strval(f) });
+ v1 = objval(o);
+ # v1 = regexpval(p, f, 0);
+ Lid =>
+ (pc, c) = getconst(code.ops, pc);
+ epush(ex, esprimid(ex, code.strs[c]));
+ continue;
+ Lnoval =>
+ v1 = undefined;
+ '.' =>
+ a1 = epop(ex);
+ v1 = epopval(ex);
+ epush(ex, ref Ref(1, nil, toObject(ex, v1), a1.name));
+ continue;
+ '[' =>
+ v2 = epopval(ex);
+ v1 = epopval(ex);
+ epush(ex, ref Ref(1, nil, toObject(ex, v1), toString(ex, v2)));
+ continue;
+ Lpostinc or
+ Lpostdec =>
+ a1 = epop(ex);
+ r1 = toNumber(ex, getValue(ex, a1));
+ v1 = numval(r1);
+ if(op == Lpostinc)
+ r1++;
+ else
+ r1--;
+ putValue(ex, a1, numval(r1));
+ Linc or
+ Ldec or
+ Lpreadd or
+ Lpresub =>
+ a1 = epop(ex);
+ r1 = toNumber(ex, getValue(ex, a1));
+ case op{
+ Linc =>
+ r1++;
+ Ldec =>
+ r1--;
+ Lpresub =>
+ r1 = -r1;
+ }
+ v1 = numval(r1);
+ if(op == Linc || op == Ldec)
+ putValue(ex, a1, v1);
+ '~' =>
+ v = epopval(ex);
+ i1 = toInt32(ex, v);
+ i1 = ~i1;
+ v1 = numval(real i1);
+ '!' =>
+ v = epopval(ex);
+ v1 = toBoolean(ex, v);
+ if(v1 == true)
+ v1 = false;
+ else
+ v1 = true;
+ Ltypeof =>
+ a1 = epop(ex);
+ if(a1.isref && getBase(ex, a1) == nil)
+ s = "undefined";
+ else case (v1 = getValue(ex, a1)).ty{
+ TUndef =>
+ s = "undefined";
+ TNull =>
+ s = "object";
+ TBool =>
+ s = "boolean";
+ TNum =>
+ s = "number";
+ TStr =>
+ s = "string";
+ TObj =>
+ if(v1.obj.call != nil)
+ s = "function";
+ else
+ s = "object";
+ TRegExp =>
+ s = "regexp";
+ }
+ v1 = strval(s);
+ Ldelete =>
+ a1 = epop(ex);
+ o = getBase(ex, a1);
+ s = getPropertyName(ex, a1);
+ if(o != nil)
+ esdelete(ex, o, s, 0);
+ v1 = undefined;
+ Lvoid =>
+ epopval(ex);
+ v = undefined;
+ '*' or
+ '/' or
+ '%' or
+ '-' =>
+ v2 = epopval(ex);
+ a1 = epop(ex);
+ r1 = toNumber(ex, getValue(ex, a1));
+ r2 = toNumber(ex, v2);
+ case op{
+ '*' =>
+ r1 = r1 * r2;
+ '/' =>
+ r1 = r1 / r2;
+ '%' =>
+ r1 = fmod(r1, r2);
+ '-' =>
+ r1 = r1 - r2;
+ }
+ v1 = numval(r1);
+ '+' =>
+ v2 = epopval(ex);
+ a1 = epop(ex);
+ v1 = toPrimitive(ex, getValue(ex, a1), NoHint);
+ v2 = toPrimitive(ex, v2, NoHint);
+ if(v1.ty == TStr || v2.ty == TStr)
+ v1 = strval(toString(ex, v1)+toString(ex, v2));
+ else
+ v1 = numval(toNumber(ex, v1)+toNumber(ex, v2));
+ Llsh or
+ Lrsh or
+ Lrshu or
+ '&' or
+ '^' or
+ '|' =>
+ v2 = epopval(ex);
+ a1 = epop(ex);
+ i1 = toInt32(ex, getValue(ex, a1));
+ i2 = toInt32(ex, v2);
+ case op{
+ Llsh =>
+ i1 <<= i2 & 16r1f;
+ Lrsh =>
+ i1 >>= i2 & 16r1f;
+ Lrshu =>
+ i1 = int (((big i1) & 16rffffffff) >> (i2 & 16r1f));
+ '&' =>
+ i1 &= i2;
+ '|' =>
+ i1 |= i2;
+ '^' =>
+ i1 ^= i2;
+ }
+ v1 = numval(real i1);
+ '=' or
+ Las =>
+ v1 = epopval(ex);
+ a1 = epop(ex);
+ putValue(ex, a1, v1);
+ '<' or
+ '>' or
+ Lleq or
+ Lgeq =>
+ v2 = epopval(ex);
+ v1 = epopval(ex);
+ if(op == '>' || op == Lleq){
+ v = v1;
+ v1 = v2;
+ v2 = v;
+ }
+ v1 = toPrimitive(ex, v1, TNum);
+ v2 = toPrimitive(ex, v2, TNum);
+ if(v1.ty == TStr && v2.ty == TStr){
+ if(v1.str < v2.str)
+ v1 = true;
+ else
+ v1 = false;
+ }else{
+ r1 = toNumber(ex, v1);
+ r2 = toNumber(ex, v2);
+ if(isnan(r1) || isnan(r2))
+ v1 = undefined;
+ else if(r1 < r2)
+ v1 = true;
+ else
+ v1 = false;
+ }
+ if(op == Lgeq || op == Lleq){
+ if(v1 == false)
+ v1 = true;
+ else
+ v1 = false;
+ }
+ Lin =>
+ v2 = epopval(ex);
+ v1 = epopval(ex);
+ if(v2.ty != TObj)
+ runtime(ex, TypeError, "rhs of 'in' not an object");
+ s = toString(ex, v1);
+ v1 = eshasproperty(ex, v2.obj, s, 0);
+ Linstanceof =>
+ v2 = epopval(ex);
+ v1 = epopval(ex);
+ if(v2.ty != TObj)
+ runtime(ex, TypeError, "rhs of 'instanceof' not an object");
+ if(!isfuncobj(v2.obj))
+ runtime(ex, TypeError, "rhs of 'instanceof' not a function");
+ if(v1.ty != TObj)
+ v1 = false;
+ else{
+ v2 = esget(ex, v2.obj, "prototype", 0);
+ if(v2.ty != TObj)
+ runtime(ex, TypeError, "prototype value not an object");
+ o = v2.obj;
+ for(p := v1.obj.prototype; p != nil; p = p.prototype){
+ if(p == o){
+ v1 = true;
+ break;
+ }
+ }
+ if(p == nil)
+ v1 = false;
+ }
+ Leq or
+ Lneq or
+ Lseq or
+ Lsne =>
+ strict := op == Lseq || op == Lsne;
+ v2 = epopval(ex);
+ v1 = epopval(ex);
+ v = false;
+ while(v1.ty != v2.ty){
+ if(strict)
+ break;
+ if(isnull(v1) && v2 == undefined
+ || v1 == undefined && isnull(v2))
+ v1 = v2;
+ else if(v1.ty == TNum && v2.ty == TStr)
+ v2 = numval(toNumber(ex, v2));
+ else if(v1.ty == TStr && v2.ty == TNum)
+ v1 = numval(toNumber(ex, v1));
+ else if(v1.ty == TBool)
+ v1 = numval(toNumber(ex, v1));
+ else if(v2.ty == TBool)
+ v2 = numval(toNumber(ex, v2));
+ else if(v2.ty == TObj && (v1.ty == TStr || v1.ty == TNum))
+ v2 = toPrimitive(ex, v2, NoHint);
+ else if(v1.ty == TObj && (v2.ty == TStr || v2.ty == TNum))
+ v1 = toPrimitive(ex, v1, NoHint);
+ else{
+ v1 = true;
+ v2 = false;
+ }
+ }
+ if(v1.ty != v2.ty)
+ v = false;
+ else{
+ case v1.ty{
+ TUndef or
+ TNull =>
+ v = true;
+ TNum =>
+ if(v1.num == v2.num)
+ v = true;
+ TBool =>
+ if(v1 == v2)
+ v = true;
+ TStr =>
+ if(v1.str == v2.str)
+ v = true;
+ TObj =>
+ if(v1.obj == v2.obj)
+ v = true;
+ TRegExp =>
+ if(v1.rev.p == v2.rev.p && v1.rev.f == v2.rev.f)
+ v = true;
+ }
+ }
+ if(op == Lneq || op == Lsne){
+ if(v == false)
+ v = true;
+ else
+ v = false;
+ }
+ v1 = v;
+ Landand =>
+ v1 = epopval(ex);
+ (pc, apc) = getjmp(code.ops, pc);
+ if(toBoolean(ex, v1) != false){
+ (pc, a1) = eexp(ex, code, pc, apc);
+ v1 = getValue(ex, a1);
+ }
+ pc = apc;
+ Loror =>
+ v1 = epopval(ex);
+ (pc, apc) = getjmp(code.ops, pc);
+ if(toBoolean(ex, v1) != true){
+ (pc, a1) = eexp(ex, code, pc, apc);
+ v1 = getValue(ex, a1);
+ }
+ pc = apc;
+ '?' =>
+ v1 = epopval(ex);
+ (pc, apc) = getjmp(code.ops, pc);
+ v1 = toBoolean(ex, v1);
+ if(v1 == true)
+ (pc, a1) = eexp(ex, code, pc, apc);
+ pc = apc;
+ (pc, apc) = getjmp(code.ops, pc);
+ if(v1 != true)
+ (pc, a1) = eexp(ex, code, pc, apc);
+ pc = apc;
+ v1 = getValue(ex, a1);
+ Lasop =>
+ a1 = epop(ex);
+ epush(ex, a1);
+ v1 = getValue(ex, a1);
+ Lgetval =>
+ v1 = epopval(ex);
+ ',' =>
+ v1 = epopval(ex);
+ epop(ex);
+ # a1's value already gotten by Lgetval
+ '(' or
+ ')' =>
+ continue;
+ Larrinit =>
+ o = narray(ex, nil, nil);
+ (pc, c) = getconst(code.ops, pc);
+ esput(ex, o, "length", numval(real c), 0);
+ c = ex.sp-c;
+ for(sp := c; sp < ex.sp; sp++){
+ v = getValue(ex, ex.stack[sp]);
+ if(v != undefined)
+ esput(ex, o, string (sp-c), v, 0);
+ }
+ ex.sp = c;
+ v1 = objval(o);
+ Lobjinit =>
+ o = nobj(ex, nil, nil);
+ (pc, c) = getconst(code.ops, pc);
+ c = ex.sp-2*c;
+ for(sp := c; sp < ex.sp; sp += 2){
+ v = getValue(ex, ex.stack[sp]);
+ if(isnum(v) || isstr(v))
+ p := toString(ex, v);
+ else
+ p = ex.stack[sp].name;
+ v = getValue(ex, ex.stack[sp+1]);
+ esput(ex, o, p, v, 0);
+ }
+ ex.sp = c;
+ v1 = objval(o);
+ Lcall or
+ Lnewcall =>
+ (pc, c) = getconst(code.ops, pc);
+ args := array[c] of ref Val;
+ c = ex.sp - c;
+ for(sp := c; sp < ex.sp; sp++)
+ args[sp-c] = getValue(ex, ex.stack[sp]);
+ ex.sp = c;
+ a1 = epop(ex);
+ v = getValue(ex, a1);
+ o = getobj(v);
+ if(op == Lcall){
+ if(o == nil || o.call == nil)
+ runtime(ex, TypeError, "can only call function objects ("+a1.name+")");
+ th = nil;
+ if(a1.isref){
+ th = getBase(ex, a1);
+ if(th != nil && isactobj(th))
+ th = nil;
+ }
+
+ # have to execute functions in the same context as they
+ # were defined, but need to use current stack.
+ if (o.call.ex == nil)
+ a1 = escall(ex, v.obj, th, args, 0);
+ else {
+ fnex := ref *o.call.ex;
+ fnex.stack = ex.stack;
+ fnex.sp = ex.sp;
+ fnex.scopechain = fnex.global :: nil;
+ # drop ref to stack to avoid array duplication should stack grow
+ ex.stack = nil;
+ osp := ex.sp;
+ # can get an exception here that corrupts ex etc.
+#aardvark:=99;
+#test:=99;
+# zebra:=99;
+ {
+ a1 = escall(fnex, v.obj, th, args, 0);
+ }
+ exception e{
+ "throw" =>
+ # copy up error so as it gets reported properly
+ ex.error = fnex.error;
+ ex.errval = fnex.errval;
+ ex.stack = fnex.stack;
+ ex.sp = osp;
+# raise e;
+ raise "throw";
+ }
+ # restore stack, sp is OK as escall() ensures that stack is balanced
+ ex.stack = fnex.stack;
+ }
+ }else{
+ if(o == nil || o.construct == nil)
+ runtime(ex, TypeError, "new must be given a constructor object");
+ a1 = valref(objval(esconstruct(ex, o, args)));
+ }
+ epush(ex, a1);
+ args = nil;
+ continue;
+ Lnew =>
+ v = epopval(ex);
+ o = getobj(v);
+ if(o == nil || o.construct == nil)
+ runtime(ex, TypeError, "new must be given a constructor object");
+ v1 = objval(esconstruct(ex, o, nil));
+ Lfunction =>
+ (pc, c) = getconst(code.ops, pc);
+ v1 = objval(code.fexps[c]);
+ ';' =>
+ break out;
+ * =>
+ fatal(ex, sprint("eexp: unknown op %s\n", tokname(op)));
+ }
+ epushval(ex, v1);
+ }
+
+ if(savesp == ex.sp)
+ return (pc, nil);
+
+ if(savesp != ex.sp-1)
+ print("unbalanced stack in eexp: %d %d\n", savesp, ex.sp);
+ return (pc, epop(ex));
+}
+
+epushval(ex: ref Exec, v: ref Val)
+{
+ epush(ex, valref(v));
+}
+
+epush(ex: ref Exec, r: ref Ref)
+{
+ if(ex.sp >= len ex.stack){
+ st := array[2 * len ex.stack] of ref Ref;
+ st[:] = ex.stack;
+ ex.stack = st;
+ }
+ ex.stack[ex.sp++] = r;
+}
+
+epop(ex: ref Exec): ref Ref
+{
+ if(ex.sp == 0)
+ fatal(ex, "popping too far off the estack\n");
+ return ex.stack[--ex.sp];
+}
+
+epopval(ex: ref Exec): ref Val
+{
+ if(ex.sp == 0)
+ fatal(ex, "popping too far off the estack\n");
+ return getValue(ex, ex.stack[--ex.sp]);
+}
+
+inlabs(lab: string, labs: list of string): int
+{
+ for(l := labs; l != nil; l = tl l)
+ if(hd l == lab)
+ return 1;
+ return 0;
+}
+
+initlabs(lab: string, labs: list of string): int
+{
+ return lab == nil || inlabs(lab, labs);
+}