Mercurial > projects > dmdscript-tango
view dmdscript_tango/opcodes.d @ 3:8363a4bf6a8f
rename package: dmdscript to dmdscript_tango
author | saaadel |
---|---|
date | Sun, 24 Jan 2010 18:33:05 +0200 |
parents | 55c2951c07be |
children |
line wrap: on
line source
/* Digital Mars DMDScript source code. * Copyright (c) 2000-2002 by Chromium Communications * D version Copyright (c) 2004-2006 by Digital Mars * All Rights Reserved * written by Walter Bright * www.digitalmars.com * Use at your own risk. There is no warranty, express or implied. * License for redistribution is by the GNU General Public License in gpl.txt. * * A binary, non-exclusive license for commercial use can be * purchased from www.digitalmars.com/dscript/buy.html. * * DMDScript is implemented in the D Programming Language, * www.digitalmars.com/d/ * * For a C++ implementation of DMDScript, including COM support, * see www.digitalmars.com/dscript/cppscript.html. */ module dmdscript_tango.opcodes; import std.stdio; import std.string; import dmdscript_tango.script; import dmdscript_tango.dobject; import dmdscript_tango.statement; import dmdscript_tango.functiondefinition; import dmdscript_tango.value; import dmdscript_tango.iterator; import dmdscript_tango.scopex; import dmdscript_tango.identifier; import dmdscript_tango.ir; import dmdscript_tango.textgen.errmsgs; import dmdscript_tango.property; import dmdscript_tango.ddeclaredfunction; import dmdscript_tango.dfunction; //debug=VERIFY; // verify integrity of code version = SCOPECACHING; // turn scope caching on //version = SCOPECACHE_LOG; // log statistics on it // Catch & Finally are "fake" Dobjects that sit in the scope // chain to implement our exception handling context. class Catch : Dobject { // This is so scope_get() will skip over these objects Value* Get(d_string PropertyName) { return null; } Value* Get(d_string PropertyName, uint hash) { return null; } // This is so we can distinguish between a real Dobject // and these fakers d_string getTypeof() { return null; } uint offset; // offset of CatchBlock d_string name; // catch identifier this(uint offset, d_string name) { super(null); this.offset = offset; this.name = name; } int isCatch() { return true; } } class Finally : Dobject { Value* Get(d_string PropertyName) { return null; } Value* Get(d_string PropertyName, uint hash) { return null; } d_string getTypeof() { return null; } IR *finallyblock; // code for FinallyBlock this(IR *finallyblock) { super(null); this.finallyblock = finallyblock; } int isFinally() { return true; } } /************************ * Look for identifier in scope. */ Value* scope_get(Dobject[] scopex, Identifier* id, Dobject *pthis) { uint d; Dobject o; Value* v; //writef("scope_get: scope = %p, scope.data = %p\n", scopex, scopex.data); //writefln("scope_get: scopex = %x, length = %d, id = %s", cast(uint)scopex.ptr, scopex.length, id.toString()); d = scopex.length; for (;;) { if (!d) { v = null; *pthis = null; break; } d--; o = scopex[d]; //writef("o = %x, hash = x%x, s = '%s'\n", o, hash, s); v = o.Get(id); if (v) { *pthis = o; break; } } return v; } Value* scope_get_lambda(Dobject[] scopex, Identifier* id, Dobject *pthis) { uint d; Dobject o; Value* v; //writefln("scope_get_lambda: scope = %x, length = %d, id = %s", cast(uint)scopex.ptr, scopex.length, id.toString()); d = scopex.length; for (;;) { if (!d) { v = null; *pthis = null; break; } d--; o = scopex[d]; //printf("o = %p ", o); //writefln("o = %s", o); //printf("o = %x, hash = x%x, s = '%.*s'\n", o, hash, s); //v = o.GetLambda(s, hash); v = o.Get(id); if (v) { *pthis = o; break; } } //writefln("v = %x", cast(uint)cast(void*)v); return v; } Value* scope_get(Dobject[] scopex, Identifier* id) { uint d; Dobject o; Value* v; //writefln("scope_get: scopex = %x, length = %d, id = %s", cast(uint)scopex.ptr, scopex.length, id.toString()); d = scopex.length; // 1 is most common case for d if (d == 1) { return scopex[0].Get(id); } for (;;) { if (!d) { v = null; break; } d--; o = scopex[d]; //writefln("\to = %s", o); v = o.Get(id); if (v) break; //writefln("\tnot found"); } return v; } /************************************ * Find last object in scopex, null if none. */ Dobject scope_tos(Dobject[] scopex) { uint d; Dobject o; for (d = scopex.length; d;) { d--; o = scopex[d]; if (o.getTypeof() != null) // if not a Finally or a Catch return o; } return null; } /***************************************** */ void PutValue(CallContext *cc, d_string s, Value* a) { // ECMA v3 8.7.2 // Look for the object o in the scope chain. // If we find it, put its value. // If we don't find it, put it into the global object uint d; uint hash; Value* v; Dobject o; d = cc.scopex.length; if (d == cc.globalroot) { o = scope_tos(cc.scopex); o.Put(s, a, 0); return; } hash = Value.calcHash(s); for (;; d--) { assert(d > 0); o = cc.scopex[d - 1]; if (d == cc.globalroot) { o.Put(s, a, 0); return; } v = o.Get(s, hash); if (v) { // Overwrite existing property with new one o.Put(s, a, 0); break; } } } void PutValue(CallContext *cc, Identifier* id, Value* a) { // ECMA v3 8.7.2 // Look for the object o in the scope chain. // If we find it, put its value. // If we don't find it, put it into the global object uint d; Value* v; Dobject o; d = cc.scopex.length; if (d == cc.globalroot) { o = scope_tos(cc.scopex); } else { for (;; d--) { assert(d > 0); o = cc.scopex[d - 1]; if (d == cc.globalroot) break; v = o.Get(id); if (v) { // Overwrite existing property with new one break; } } } o.Put(id, a, 0); } /***************************************** * Helper function for Values that cannot be converted to Objects. */ Value* cannotConvert(Value* b, int linnum) { ErrInfo errinfo; errinfo.linnum = linnum; if (b.isUndefinedOrNull()) { b = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_CANNOT_CONVERT_TO_OBJECT4], b.getType()); } else { b = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_CANNOT_CONVERT_TO_OBJECT2], b.getType(), b.toString()); } return b; } const uint INDEX_FACTOR = 16; // or 1 struct IR { union { struct { version (LittleEndian) { ubyte opcode; ubyte padding; ushort linnum; } else { ushort linnum; ubyte padding; ubyte opcode; } } IR* code; Value* value; uint index; // index into local variable table uint hash; // cached hash value int offset; Identifier* id; d_boolean boolean; Statement target; // used for backpatch fixups Dobject object; void* ptr; } /**************************** * This is the main interpreter loop. */ static void *call(CallContext *cc, Dobject othis, IR *code, Value* ret, Value* locals) { Value* a; Value* b; Value* c; Value* v; Iterator *iter; Identifier *id; d_string s; d_string s2; d_number n; d_boolean bo; d_int32 i32; d_uint32 u32; d_boolean res; tchar[] tx; tchar[] ty; Dobject o; Dobject[] scopex; uint dimsave; uint offset; Catch ca; Finally f; IR* codestart = code; d_number inc; /*************************************** * Cache for getscope's */ version (SCOPECACHING) { struct ScopeCache { d_string s; Value* v; // never null, and never from a Dcomobject } int si; ScopeCache zero; ScopeCache[16] scopecache; version (SCOPECACHE_LOG) int scopecache_cnt = 0; uint SCOPECACHE_SI(tchar* s) { return (cast(uint)(s)) & 15; } void SCOPECACHE_CLEAR() { scopecache[] = zero; } } else { uint SCOPECACHE_SI(d_string s) { return 0; } void SCOPECACHE_CLEAR() { } } version (all) { // Eliminate the scale factor of Value.sizeof by computing it at compile time Value* GETa(IR* code) { return cast(Value*)(cast(void*)locals + (code + 1).index * (16 / INDEX_FACTOR)); } Value* GETb(IR* code) { return cast(Value*)(cast(void*)locals + (code + 2).index * (16 / INDEX_FACTOR)); } Value* GETc(IR* code) { return cast(Value*)(cast(void*)locals + (code + 3).index * (16 / INDEX_FACTOR)); } Value* GETd(IR* code) { return cast(Value*)(cast(void*)locals + (code + 4).index * (16 / INDEX_FACTOR)); } Value* GETe(IR* code) { return cast(Value*)(cast(void*)locals + (code + 5).index * (16 / INDEX_FACTOR)); } } else { Value* GETa(IR* code) { return &locals[(code + 1).index]; } Value* GETb(IR* code) { return &locals[(code + 2).index]; } Value* GETc(IR* code) { return &locals[(code + 3).index]; } Value* GETd(IR* code) { return &locals[(code + 4).index]; } Value* GETe(IR* code) { return &locals[(code + 5).index]; } } uint GETlinnum(IR* code) { return code.linnum; } debug (VERIFY) uint checksum = IR.verify(__LINE__, code); version (none) { writef("+printfunc\n"); printfunc(code); writef("-printfunc\n"); } scopex = cc.scopex; //printf("call: scope = %p, length = %d\n", scopex.ptr, scopex.length); dimsave = scopex.length; //if (logflag) // writef("IR.call(othis = %p, code = %p, locals = %p)\n",othis,code,locals); debug { uint debug_scoperoot = cc.scoperoot; uint debug_globalroot = cc.globalroot; uint debug_scopedim = scopex.length; uint debug_scopeallocdim = scopex.allocdim; Dobject debug_global = cc.global; Dobject debug_variable = cc.variable; void** debug_pscoperootdata = cast(void**)mem.malloc((void*).sizeof*debug_scoperoot); void** debug_pglobalrootdata = cast(void**)mem.malloc((void*).sizeof*debug_globalroot); memcpy(debug_pscoperootdata, scopex.data, (void*).sizeof*debug_scoperoot); memcpy(debug_pglobalrootdata, scopex.data, (void*).sizeof*debug_globalroot); } assert(code); //Value.copy(ret, &vundefined); assert(othis); Lnext: //writef("cc = %x, interrupt = %d\n", cc, cc.Interrupt); if (cc.Interrupt) // see if script was interrupted goto Linterrupt; for (;;) { version (none) { writef("%2d:", code - codestart); print(code - codestart, code); } debug { assert(scopex == cc.scopex); assert(debug_scoperoot == cc.scoperoot); assert(debug_globalroot == cc.globalroot); assert(debug_global == cc.global); assert(debug_variable == cc.variable); assert(scopex.length >= debug_scoperoot); assert(scopex.length >= debug_globalroot); assert(scopex.length >= debug_scopedim); assert(scopex.allocdim >= debug_scopeallocdim); assert(0 == memcmp(debug_pscoperootdata, scopex.data, (void*).sizeof*debug_scoperoot)); assert(0 == memcmp(debug_pglobalrootdata, scopex.data, (void*).sizeof*debug_globalroot)); assert(scopex); } /+ v = &vundefined; tx = v.getType(); assert(tx == TypeUndefined); +/ //writef("\tIR%d:\n", code.opcode); switch (code.opcode) { case IRerror: assert(0); break; case IRnop: code++; break; case IRget: // a = b.c a = GETa(code); b = GETb(code); o = b.toObject(); if (!o) { a = cannotConvert(b, GETlinnum(code)); goto Lthrow; } c = GETc(code); if (c.vtype == V_NUMBER && (i32 = cast(d_int32)c.number) == c.number && i32 >= 0) { //writef("IRget %d\n", i32); v = o.Get(cast(d_uint32)i32, c); } else { s = c.toString(); v = o.Get(s); } if (!v) v = &vundefined; Value.copy(a,v); code += 4; break; case IRput: // b.c = a a = GETa(code); b = GETb(code); c = GETc(code); if (c.vtype == V_NUMBER && (i32 = cast(d_int32)c.number) == c.number && i32 >= 0) { //writef("IRput %d\n", i32); if (b.vtype == V_OBJECT) a = b.object.Put(cast(d_uint32)i32, c, a, 0); else a = b.Put(cast(d_uint32)i32, c, a); } else { s = c.toString(); a = b.Put(s, a); } if (a) goto Lthrow; code += 4; break; case IRgets: // a = b.s a = GETa(code); b = GETb(code); s = (code + 3).id.value.string; o = b.toObject(); if (!o) { //writef("%s %s.%s cannot convert to Object", b.getType(), b.toString(), s); ErrInfo errinfo; a = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_CANNOT_CONVERT_TO_OBJECT3], b.getType(), b.toString(), s); goto Lthrow; } v = o.Get(s); if (!v) { //writef("IRgets: %s.%s is undefined\n", b.getType(), d_string_ptr(s)); v = &vundefined; } Value.copy(a,v); code += 4; goto Lnext; case IRgetscope: // a = s a = GETa(code); id = (code + 2).id; s = id.value.string; version (SCOPECACHING) { si = SCOPECACHE_SI(s.ptr); if (s is scopecache[si].s) { version (SCOPECACHE_LOG) scopecache_cnt++; Value.copy(a, scopecache[si].v); code += 3; break; } //writefln("miss %s, was %s, s.ptr = %x, cache.ptr = %x", s, scopecache[si].s, cast(uint)s.ptr, cast(uint)scopecache[si].s.ptr); } version (all) { // Inline scope_get() for speed { uint d; d = scopex.length; // 1 is most common case for d if (d == 1) { o = scopex[0]; v = o.Get(id); } else { o = null; for (;;) { if (!d) { v = null; break; } d--; o = scopex[d]; v = o.Get(id); //writef("o = %x, v = %x\n", o, v); if (v) break; } } } if (!v) v = &vundefined; else { version (SCOPECACHING) { if (1) //!o.isDcomobject()) { scopecache[si].s = s; scopecache[si].v = v; } } } } else { v = scope_get(scopex, id); if (!v) v = &vundefined; } //writef("v = %p\n", v); //writef("v = %g\n", v.toNumber()); //writef("v = %s\n", d_string_ptr(v.toString())); Value.copy(a, v); code += 3; break; case IRaddass: // a = (b.c += a) c = GETc(code); s = c.toString(); goto Laddass; case IRaddasss: // a = (b.s += a) s = (code + 3).id.value.string; Laddass: b = GETb(code); v = b.Get(s); goto Laddass2; case IRaddassscope: // a = (s += a) b = null; // Needed for the b.Put() below to shutup a compiler use-without-init warning id = (code + 2).id; s = id.value.string; version (SCOPECACHING) { si = SCOPECACHE_SI(s.ptr); if (s is scopecache[si].s) v = scopecache[si].v; else v = scope_get(scopex, id); } else { v = scope_get(scopex, id); } Laddass2: a = GETa(code); if (!v) { v = &vundefined; a.putVundefined(); /+ if (b) { a = b.Put(s, v); //if (a) goto Lthrow; } else { PutValue(cc, s, v); } +/ } else if (a.vtype == V_NUMBER && v.vtype == V_NUMBER) { a.number += v.number; v.number = a.number; } else { v.toPrimitive(v, null); a.toPrimitive(a, null); if (v.isString()) { s2 = v.toString() ~ a.toString(); a.putVstring(s2); Value.copy(v, a); } else if (a.isString()) { s2 = v.toString() ~ a.toString(); a.putVstring(s2); Value.copy(v, a); } else { a.putVnumber(a.toNumber() + v.toNumber()); v.number = a.number; } } code += 4; break; case IRputs: // b.s = a a = GETa(code); b = GETb(code); o = b.toObject(); if (!o) { a = cannotConvert(b, GETlinnum(code)); goto Lthrow; } a = o.Put((code + 3).id.value.string, a, 0); if (a) goto Lthrow; code += 4; goto Lnext; case IRputscope: // s = a PutValue(cc, (code + 2).id, GETa(code)); code += 3; break; case IRputdefault: // b = a a = GETa(code); b = GETb(code); o = b.toObject(); if (!o) { ErrInfo errinfo; a = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_CANNOT_ASSIGN], a.getType(), b.getType()); goto Lthrow; } a = o.PutDefault(a); if (a) goto Lthrow; code += 3; break; case IRputthis: // s = a a = cc.variable.Put((code + 2).id.value.string, GETa(code), 0); //if (a) goto Lthrow; code += 3; break; case IRmov: // a = b Value.copy(GETa(code), GETb(code)); code += 3; break; case IRstring: // a = "string" GETa(code).putVstring((code + 2).id.value.string); code += 3; break; case IRobject: // a = object { FunctionDefinition fd; fd = cast(FunctionDefinition)(code + 2).ptr; Dfunction fobject = new DdeclaredFunction(fd); fobject.scopex = scopex; GETa(code).putVobject(fobject); code += 3; break; } case IRthis: // a = this GETa(code).putVobject(othis); //writef("IRthis: %s, othis = %x\n", GETa(code).getType(), othis); code += 2; break; case IRnumber: // a = number GETa(code).putVnumber(*cast(d_number *)(code + 2)); code += 4; break; case IRboolean: // a = boolean GETa(code).putVboolean((code + 2).boolean); code += 3; break; case IRnull: // a = null GETa(code).putVnull(); code += 2; break; case IRundefined: // a = undefined GETa(code).putVundefined(); code += 2; break; case IRthisget: // a = othis.ident a = GETa(code); v = othis.Get((code + 2).id.value.string); if (!v) v = &vundefined; Value.copy(a, v); code += 3; break; case IRneg: // a = -a a = GETa(code); n = a.toNumber(); a.putVnumber(-n); code += 2; break; case IRpos: // a = a a = GETa(code); n = a.toNumber(); a.putVnumber(n); code += 2; break; case IRcom: // a = ~a a = GETa(code); i32 = a.toInt32(); a.putVnumber(~i32); code += 2; break; case IRnot: // a = !a a = GETa(code); a.putVboolean(!a.toBoolean()); code += 2; break; case IRtypeof: // a = typeof a // ECMA 11.4.3 says that if the result of (a) // is a Reference and GetBase(a) is null, // then the result is "undefined". I don't know // what kind of script syntax will generate this. a = GETa(code); a.putVstring(a.getTypeof()); code += 2; break; case IRinstance: // a = b instanceof c { Dobject co; // ECMA v3 11.8.6 b = GETb(code); o = b.toObject(); c = GETc(code); if (c.isPrimitive()) { ErrInfo errinfo; a = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_RHS_MUST_BE_OBJECT], "instanceof", c.getType()); goto Lthrow; } co = c.toObject(); a = GETa(code); v = cast(Value*)co.HasInstance(a, b); if (v) { a = v; goto Lthrow; } code += 4; break; } case IRadd: // a = b + c a = GETa(code); b = GETb(code); c = GETc(code); if (b.vtype == V_NUMBER && c.vtype == V_NUMBER) { a.putVnumber(b.number + c.number); } else { char vtmpb[Value.sizeof]; Value* vb = cast(Value*)vtmpb; char vtmpc[Value.sizeof]; Value* vc = cast(Value*)vtmpc; v = cast(Value*)b.toPrimitive(vb, null); if (v) { a = v; goto Lthrow; } v = cast(Value*)c.toPrimitive(vc, null); if (v) { a = v; goto Lthrow; } if (vb.isString() || vc.isString()) { s = vb.toString() ~ vc.toString(); a.putVstring(s); } else { a.putVnumber(vb.toNumber() + vc.toNumber()); } } code += 4; break; case IRsub: // a = b - c a = GETa(code); b = GETb(code); c = GETc(code); a.putVnumber(b.toNumber() - c.toNumber()); code += 4; break; case IRmul: // a = b * c a = GETa(code); b = GETb(code); c = GETc(code); a.putVnumber(b.toNumber() * c.toNumber()); code += 4; break; case IRdiv: // a = b / c a = GETa(code); b = GETb(code); c = GETc(code); //writef("%g / %g = %g\n", b.toNumber() , c.toNumber(), b.toNumber() / c.toNumber()); a.putVnumber(b.toNumber() / c.toNumber()); code += 4; break; case IRmod: // a = b % c a = GETa(code); b = GETb(code); c = GETc(code); a.putVnumber(b.toNumber() % c.toNumber()); code += 4; break; case IRshl: // a = b << c a = GETa(code); b = GETb(code); c = GETc(code); i32 = b.toInt32(); u32 = c.toUint32() & 0x1F; i32 <<= u32; a.putVnumber(i32); code += 4; break; case IRshr: // a = b >> c a = GETa(code); b = GETb(code); c = GETc(code); i32 = b.toInt32(); u32 = c.toUint32() & 0x1F; i32 >>= cast(d_int32) u32; a.putVnumber(i32); code += 4; break; case IRushr: // a = b >>> c a = GETa(code); b = GETb(code); c = GETc(code); i32 = b.toUint32(); u32 = c.toUint32() & 0x1F; u32 = (cast(d_uint32) i32) >> u32; a.putVnumber(u32); code += 4; break; case IRand: // a = b & c a = GETa(code); b = GETb(code); c = GETc(code); a.putVnumber(b.toInt32() & c.toInt32()); code += 4; break; case IRor: // a = b | c a = GETa(code); b = GETb(code); c = GETc(code); a.putVnumber(b.toInt32() | c.toInt32()); code += 4; break; case IRxor: // a = b ^ c a = GETa(code); b = GETb(code); c = GETc(code); a.putVnumber(b.toInt32() ^ c.toInt32()); code += 4; break; /********************/ case IRpreinc: // a = ++b.c c = GETc(code); s = c.toString(); goto Lpreinc; case IRpreincs: // a = ++b.s s = (code + 3).id.value.string; Lpreinc: inc = 1; Lpre: a = GETa(code); b = GETb(code); v = b.Get(s); if (!v) v = &vundefined; n = v.toNumber(); a.putVnumber(n + inc); b.Put(s, a); code += 4; break; case IRpreincscope: // a = ++s inc = 1; Lprescope: a = GETa(code); id = (code + 2).id; s = id.value.string; version (SCOPECACHING) { si = SCOPECACHE_SI(s.ptr); if (s is scopecache[si].s) { v = scopecache[si].v; n = v.toNumber() + inc; v.putVnumber(n); a.putVnumber(n); } else { v = scope_get(scopex, id, &o); if (v) { n = v.toNumber() + inc; v.putVnumber(n); a.putVnumber(n); } else a.putVundefined(); } } else { v = scope_get(scopex, id, &o); if (v) { n = v.toNumber(); v.putVnumber(n + inc); Value.copy(a, v); } else a.putVundefined(); } code += 4; break; case IRpredec: // a = --b.c c = GETc(code); s = c.toString(); goto Lpredec; case IRpredecs: // a = --b.s s = (code + 3).id.value.string; Lpredec: inc = -1; goto Lpre; case IRpredecscope: // a = --s inc = -1; goto Lprescope; /********************/ case IRpostinc: // a = b.c++ c = GETc(code); s = c.toString(); goto Lpostinc; case IRpostincs: // a = b.s++ s = (code + 3).id.value.string; Lpostinc: a = GETa(code); b = GETb(code); v = b.Get(s); if (!v) v = &vundefined; n = v.toNumber(); a.putVnumber(n + 1); b.Put(s, a); a.putVnumber(n); code += 4; break; case IRpostincscope: // a = s++ id = (code + 2).id; v = scope_get(scopex, id, &o); if (v && v != &vundefined) { n = v.toNumber(); a = GETa(code); v.putVnumber(n + 1); a.putVnumber(n); } else GETa(code).putVundefined(); code += 3; break; case IRpostdec: // a = b.c-- c = GETc(code); s = c.toString(); goto Lpostdec; case IRpostdecs: // a = b.s-- s = (code + 3).id.value.string; Lpostdec: a = GETa(code); b = GETb(code); v = b.Get(s); if (!v) v = &vundefined; n = v.toNumber(); a.putVnumber(n - 1); b.Put(s, a); a.putVnumber(n); code += 4; break; case IRpostdecscope: // a = s-- id = (code + 2).id; v = scope_get(scopex, id, &o); if (v && v != &vundefined) { n = v.toNumber(); a = GETa(code); v.putVnumber(n - 1); a.putVnumber(n); } else GETa(code).putVundefined(); code += 3; break; case IRdel: // a = delete b.c case IRdels: // a = delete b.s b = GETb(code); if (b.isPrimitive()) bo = true; else { o = b.toObject(); if (!o) { a = cannotConvert(b, GETlinnum(code)); goto Lthrow; } s = (code.opcode == IRdel) ? GETc(code).toString() : (code + 3).id.value.string; if (o.implementsDelete()) bo = o.Delete(s); else bo = !o.HasProperty(s); } GETa(code).putVboolean(bo); code += 4; break; case IRdelscope: // a = delete s id = (code + 2).id; s = id.value.string; //o = scope_tos(scopex); // broken way if (!scope_get(scopex, id, &o)) bo = true; else if (o.implementsDelete()) bo = o.Delete(s); else bo = !o.HasProperty(s); GETa(code).putVboolean(bo); code += 3; break; /* ECMA requires that if one of the numeric operands is NAN, * then the result of the comparison is false. D generates a * correct test for NAN operands. */ case IRclt: // a = (b < c) a = GETa(code); b = GETb(code); c = GETc(code); if (b.vtype == V_NUMBER && c.vtype == V_NUMBER) res = (b.number < c.number); else { b.toPrimitive(b, TypeNumber); c.toPrimitive(c, TypeNumber); if (b.isString() && c.isString()) { d_string x = b.toString(); d_string y = c.toString(); res = std.string.cmp(x, y) < 0; } else res = b.toNumber() < c.toNumber(); } a.putVboolean(res); code += 4; break; case IRcle: // a = (b <= c) a = GETa(code); b = GETb(code); c = GETc(code); if (b.vtype == V_NUMBER && c.vtype == V_NUMBER) res = (b.number <= c.number); else { b.toPrimitive(b, TypeNumber); c.toPrimitive(c, TypeNumber); if (b.isString() && c.isString()) { d_string x = b.toString(); d_string y = c.toString(); res = std.string.cmp(x, y) <= 0; } else res = b.toNumber() <= c.toNumber(); } a.putVboolean(res); code += 4; break; case IRcgt: // a = (b > c) a = GETa(code); b = GETb(code); c = GETc(code); if (b.vtype == V_NUMBER && c.vtype == V_NUMBER) res = (b.number > c.number); else { b.toPrimitive(b, TypeNumber); c.toPrimitive(c, TypeNumber); if (b.isString() && c.isString()) { d_string x = b.toString(); d_string y = c.toString(); res = std.string.cmp(x, y) > 0; } else res = b.toNumber() > c.toNumber(); } a.putVboolean(res); code += 4; break; case IRcge: // a = (b >= c) a = GETa(code); b = GETb(code); c = GETc(code); if (b.vtype == V_NUMBER && c.vtype == V_NUMBER) res = (b.number >= c.number); else { b.toPrimitive(b, TypeNumber); c.toPrimitive(c, TypeNumber); if (b.isString() && c.isString()) { d_string x = b.toString(); d_string y = c.toString(); res = std.string.cmp(x, y) >= 0; } else res = b.toNumber() >= c.toNumber(); } a.putVboolean(res); code += 4; break; case IRceq: // a = (b == c) case IRcne: // a = (b != c) a = GETa(code); b = GETb(code); c = GETc(code); Lagain: tx = b.getType(); ty = c.getType(); if (logflag) writef("tx('%s', '%s')\n", tx, ty); if (tx == ty) { if (tx == TypeUndefined || tx == TypeNull) res = true; else if (tx == TypeNumber) { d_number x = b.number; d_number y = c.number; res = (x == y); //writef("x = %g, y = %g, res = %d\n", x, y, res); } else if (tx == TypeString) { if (logflag) { writef("b = %x, c = %x\n", b, c); writef("cmp('%s', '%s')\n", b.string, c.string); writef("cmp(%d, %d)\n", b.string.length, c.string.length); } res = (b.string == c.string); } else if (tx == TypeBoolean) res = (b.dbool == c.dbool); else // TypeObject { res = b.object == c.object; } } else if (tx == TypeNull && ty == TypeUndefined) res = true; else if (tx == TypeUndefined && ty == TypeNull) res = true; else if (tx == TypeNumber && ty == TypeString) { c.putVnumber(c.toNumber()); goto Lagain; } else if (tx == TypeString && ty == TypeNumber) { b.putVnumber(b.toNumber()); goto Lagain; } else if (tx == TypeBoolean) { b.putVnumber(b.toNumber()); goto Lagain; } else if (ty == TypeBoolean) { c.putVnumber(c.toNumber()); goto Lagain; } else if (ty == TypeObject) { v = cast(Value*)c.toPrimitive(c, null); if (v) { a = v; goto Lthrow; } goto Lagain; } else if (tx == TypeObject) { v = cast(Value*)b.toPrimitive(b, null); if (v) { a = v; goto Lthrow; } goto Lagain; } else { res = false; } res ^= (code.opcode == IRcne); //Lceq: a.putVboolean(res); code += 4; break; case IRcid: // a = (b === c) case IRcnid: // a = (b !== c) a = GETa(code); b = GETb(code); c = GETc(code); tx = b.getType(); ty = c.getType(); if (tx == ty) { if (tx == TypeUndefined || tx == TypeNull) res = true; else if (tx == TypeNumber) { d_number x = b.number; d_number y = c.number; // Ensure that a NAN operand produces false if (code.opcode == IRcid) res = (x == y); else res = (x <> y); goto Lcid; } else if (tx == TypeString) res = (b.string == c.string); else if (tx == TypeBoolean) res = (b.dbool == c.dbool); else // TypeObject { res = b.object == c.object; } } else { res = false; } res ^= (code.opcode == IRcnid); Lcid: a.putVboolean(res); code += 4; break; case IRjt: // if (b) goto t b = GETb(code); if (b.toBoolean()) code += (code + 1).offset; else code += 3; break; case IRjf: // if (!b) goto t b = GETb(code); if (!b.toBoolean()) code += (code + 1).offset; else code += 3; break; case IRjtb: // if (b) goto t b = GETb(code); if (b.dbool) code += (code + 1).offset; else code += 3; break; case IRjfb: // if (!b) goto t b = GETb(code); if (!b.dbool) code += (code + 1).offset; else code += 3; break; case IRjmp: code += (code + 1).offset; break; case IRjlt: // if (b < c) goto c b = GETb(code); c = GETc(code); if (b.vtype == V_NUMBER && c.vtype == V_NUMBER) { if (b.number < c.number) code += 4; else code += (code + 1).offset; break; } else { b.toPrimitive(b, TypeNumber); c.toPrimitive(c, TypeNumber); if (b.isString() && c.isString()) { d_string x = b.toString(); d_string y = c.toString(); res = std.string.cmp(x, y) < 0; } else res = b.toNumber() < c.toNumber(); } if (!res) code += (code + 1).offset; else code += 4; break; case IRjle: // if (b <= c) goto c b = GETb(code); c = GETc(code); if (b.vtype == V_NUMBER && c.vtype == V_NUMBER) { if (b.number <= c.number) code += 4; else code += (code + 1).offset; break; } else { b.toPrimitive(b, TypeNumber); c.toPrimitive(c, TypeNumber); if (b.isString() && c.isString()) { d_string x = b.toString(); d_string y = c.toString(); res = std.string.cmp(x, y) <= 0; } else res = b.toNumber() <= c.toNumber(); } if (!res) code += (code + 1).offset; else code += 4; break; case IRjltc: // if (b < constant) goto c b = GETb(code); res = (b.toNumber() < *cast(d_number *)(code + 3)); if (!res) code += (code + 1).offset; else code += 5; break; case IRjlec: // if (b <= constant) goto c b = GETb(code); res = (b.toNumber() <= *cast(d_number *)(code + 3)); if (!res) code += (code + 1).offset; else code += 5; break; case IRiter: // a = iter(b) a = GETa(code); b = GETb(code); o = b.toObject(); if (!o) { a = cannotConvert(b, GETlinnum(code)); goto Lthrow; } a = o.putIterator(a); if (a) goto Lthrow; code += 3; break; case IRnext: // a, b.c, iter // if (!(b.c = iter)) goto a; iter = iter.next s = GETc(code).toString(); goto case_next; case IRnexts: // a, b.s, iter s = (code + 3).id.value.string; case_next: iter = GETd(code).iter; v = iter.next(); if (!v) code += (code + 1).offset; else { b = GETb(code); b.Put(s, v); code += 5; } break; case IRnextscope: // a, s, iter s = (code + 2).id.value.string; iter = GETc(code).iter; v = iter.next(); if (!v) code += (code + 1).offset; else { o = scope_tos(scopex); o.Put(s, v, 0); code += 4; } break; case IRcall: // a = b.c(argc, argv) s = GETc(code).toString(); goto case_call; case IRcalls: // a = b.s(argc, argv) s = (code + 3).id.value.string; goto case_call; case_call: a = GETa(code); b = GETb(code); o = b.toObject(); if (!o) { goto Lcallerror; } { //writef("v.call\n"); v = o.Get(s); if (!v) goto Lcallerror; //writef("calling... '%s'\n", v.toString()); cc.callerothis = othis; a.putVundefined(); a = cast(Value*)v.Call(cc, o, a, GETe(code)[0 .. (code + 4).index]); //writef("regular call, a = %x\n", a); } debug (VERIFY) assert(checksum == IR.verify(__LINE__, codestart)); if (a) goto Lthrow; code += 6; goto Lnext; Lcallerror: { //writef("%s %s.%s is undefined and has no Call method\n", b.getType(), b.toString(), s); ErrInfo errinfo; a = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_UNDEFINED_NO_CALL3], b.getType(), b.toString(), s); goto Lthrow; } case IRcallscope: // a = s(argc, argv) id = (code + 2).id; s = id.value.string; a = GETa(code); v = scope_get_lambda(scopex, id, &o); //writefln("v.toString() = '%s'", v.toString()); if (!v) { ErrInfo errinfo; a = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_UNDEFINED_NO_CALL2], "property", s); goto Lthrow; } // Should we pass othis or o? I think othis. cc.callerothis = othis; // pass othis to eval() a.putVundefined(); a = cast(Value*)v.Call(cc, o, a, GETd(code)[0 .. (code + 3).index] ); //writef("callscope result = %x\n", a); debug (VERIFY) assert(checksum == IR.verify(__LINE__, codestart)); if (a) goto Lthrow; code += 5; goto Lnext; case IRcallv: // v(argc, argv) = a a = GETa(code); b = GETb(code); o = b.toObject(); if (!o) { //writef("%s %s is undefined and has no Call method\n", b.getType(), b.toString()); ErrInfo errinfo; a = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_UNDEFINED_NO_CALL2], b.getType(), b.toString()); goto Lthrow; } cc.callerothis = othis; // pass othis to eval() a.putVundefined(); a = cast(Value*)o.Call(cc, o, a, GETd(code)[0 .. (code + 3).index]); if (a) goto Lthrow; code += 5; goto Lnext; case IRputcall: // b.c(argc, argv) = a s = GETc(code).toString(); goto case_putcall; case IRputcalls: // b.s(argc, argv) = a s = (code + 3).id.value.string; goto case_putcall; case_putcall: a = GETa(code); b = GETb(code); o = b.toObject(); if (!o) goto Lcallerror; //v = o.GetLambda(s, Value.calcHash(s)); v = o.Get(s, Value.calcHash(s)); if (!v) goto Lcallerror; //writef("calling... '%s'\n", v.toString()); o = v.toObject(); if (!o) { ErrInfo errinfo; a = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_CANNOT_ASSIGN_TO2], b.getType(), s); goto Lthrow; } a = cast(Value*)o.put_Value(a, GETe(code)[0 .. (code + 4).index] ); if (a) goto Lthrow; code += 6; goto Lnext; case IRputcallscope: // a = s(argc, argv) id = (code + 2).id; s = id.value.string; v = scope_get_lambda(scopex, id, &o); if (!v) { ErrInfo errinfo; a = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_UNDEFINED_NO_CALL2], "property", s); goto Lthrow; } o = v.toObject(); if (!o) { ErrInfo errinfo; a = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_CANNOT_ASSIGN_TO], s); goto Lthrow; } a = cast(Value*)o.put_Value(GETa(code), GETd(code)[0 .. (code + 3).index] ); if (a) goto Lthrow; code += 5; goto Lnext; case IRputcallv: // v(argc, argv) = a b = GETb(code); o = b.toObject(); if (!o) { //writef("%s %s is undefined and has no Call method\n", b.getType(), b.toString()); ErrInfo errinfo; a = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_UNDEFINED_NO_CALL2], b.getType(), b.toString()); goto Lthrow; } a = cast(Value*)o.put_Value(GETa(code), GETd(code)[0 .. (code + 3).index] ); if (a) goto Lthrow; code += 5; goto Lnext; case IRnew: // a = new b(argc, argv) a = GETa(code); b = GETb(code); a.putVundefined(); a = cast(Value*)b.Construct(cc, a, GETd(code)[0 .. (code + 3).index] ); debug (VERIFY) assert(checksum == IR.verify(__LINE__, codestart)); if (a) goto Lthrow; code += 5; goto Lnext; case IRpush: SCOPECACHE_CLEAR(); a = GETa(code); o = a.toObject(); if (!o) { a = cannotConvert(a, GETlinnum(code)); goto Lthrow; } scopex ~= o; // push entry onto scope chain cc.scopex = scopex; code += 2; break; case IRpop: SCOPECACHE_CLEAR(); o = scopex[length - 1]; scopex = scopex[0 .. length - 1]; // pop entry off scope chain // If it's a Finally, we need to execute // the finally block code += 1; if (o.isFinally()) // test could be eliminated with virtual func { f = cast(Finally)o; cc.finallyret = 0; a = cast(Value*)call(cc, othis, f.finallyblock, ret, locals); debug (VERIFY) assert(checksum == IR.verify(__LINE__, codestart)); if (a) goto Lthrow; if (cc.finallyret) cc.finallyret = 0; else { // The rest of any unwinding is already done return null; } } goto Lnext; case IRfinallyret: cc.finallyret = 1; case IRret: version (SCOPECACHE_LOG) printf("scopecache_cnt = %d\n", scopecache_cnt); return null; case IRretexp: a = GETa(code); Value.copy(ret, a); //writef("returns: %s\n", ret.toString()); return null; case IRimpret: a = GETa(code); Value.copy(ret, a); //writef("implicit return: %s\n", ret.toString()); code += 2; goto Lnext; case IRthrow: a = GETa(code); cc.linnum = GETlinnum(code); Lthrow: //writef("Lthrow: linnum = %d\n", GETlinnum(code)); a.getErrInfo(null, GETlinnum(code)); SCOPECACHE_CLEAR(); for (;;) { if (scopex.length <= dimsave) { ret.putVundefined(); // 'a' may be pointing into the stack, which means // it gets scrambled on return. Therefore, we copy // its contents into a safe area in CallContext. assert(cc.value.sizeof == Value.sizeof); Value.copy(cast(Value*)cc.value, a); return cast(Value*)cc.value; } o = scopex[length - 1]; scopex = scopex[0 .. length - 1]; // pop entry off scope chain if (o.isCatch()) { ca = cast(Catch)o; //writef("catch('%s')\n", ca.name); o = new Dobject(Dobject.getPrototype()); version (JSCRIPT_CATCH_BUG) { PutValue(cc, ca.name, a); } else { o.Put(ca.name, a, DontDelete); } scopex ~= o; cc.scopex = scopex; code = codestart + ca.offset; break; } else { if (o.isFinally()) { f = cast(Finally)o; v = cast(Value*)call(cc, othis, f.finallyblock, ret, locals); if (v) { a = v; //writef("changing a\n"); } } } } goto Lnext; case IRtrycatch: SCOPECACHE_CLEAR(); offset = (code - codestart) + (code + 1).offset; s = (code + 2).id.value.string; ca = new Catch(offset, s); scopex ~= ca; cc.scopex = scopex; code += 3; break; case IRtryfinally: SCOPECACHE_CLEAR(); f = new Finally(code + (code + 1).offset); scopex ~= f; cc.scopex = scopex; code += 2; break; case IRassert: { ErrInfo errinfo; errinfo.linnum = (code + 1).index; version (all) // Not supported under some com servers { a = Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_ASSERT], (code + 1).index); goto Lthrow; } else { RuntimeErrorx(ERR_ASSERT, (code + 1).index); } code += 2; break; } default: //writef("1: Unrecognized IR instruction %d\n", code.opcode); assert(0); // unrecognized IR instruction } } Linterrupt: ret.putVundefined(); return null; } /******************************************* * This is a 'disassembler' for our interpreted code. * Useful for debugging. */ static void print(uint address, IR *code) { switch (code.opcode) { case IRerror: writef("\tIRerror\n"); break; case IRnop: writef("\tIRnop\n"); break; case IRend: writef("\tIRend\n"); break; case IRget: // a = b.c writef("\tIRget %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRput: // b.c = a writef("\tIRput %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRgets: // a = b.s writef("\tIRgets %d, %d, '%s'\n",(code + 1).index, (code + 2).index, (code + 3).id.value.string); break; case IRgetscope: // a = othis.ident writef("\tIRgetscope %d, '%s', hash=%d\n",(code + 1).index,(code + 2).id.value.string,(code + 2).id.value.hash); break; case IRaddass: // b.c += a writef("\tIRaddass %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRaddasss: // b.s += a writef("\tIRaddasss %d, %d, '%s'\n",(code + 1).index, (code + 2).index, (code + 3).id.value.string); break; case IRaddassscope: // othis.ident += a writef("\tIRaddassscope %d, '%s', hash=%d\n",(code + 1).index,(code + 2).id.value.string,(code + 3).index); break; case IRputs: // b.s = a writef("\tIRputs %d, %d, '%s'\n",(code + 1).index, (code + 2).index, (code + 3).id.value.string); break; case IRputscope: // s = a writef("\tIRputscope %d, '%s'\n",(code + 1).index, (code + 2).id.value.string); break; case IRputdefault: // b = a writef("\tIRputdefault %d, %d\n",(code + 1).index, (code + 2).index); break; case IRputthis: // b = s writef("\tIRputthis '%s', %d\n",(code + 2).id.value.string,(code + 1).index); break; case IRmov: // a = b writef("\tIRmov %d, %d\n", (code + 1).index, (code + 2).index); break; case IRstring: // a = "string" writef("\tIRstring %d, '%s'\n",(code + 1).index,(code + 2).id.value.string); break; case IRobject: // a = object writef("\tIRobject %d, %x\n",(code + 1).index,cast(void*)(code + 2).object); break; case IRthis: // a = this writef("\tIRthis %d\n",(code + 1).index); break; case IRnumber: // a = number writef("\tIRnumber %d, %g\n",(code + 1).index,*cast(d_number *)(code + 2)); break; case IRboolean: // a = boolean writef("\tIRboolean %d, %d\n",(code + 1).index, (code + 2).boolean); break; case IRnull: // a = null writef("\tIRnull %d\n",(code + 1).index); break; case IRundefined: // a = undefined writef("\tIRundefined %d\n",(code + 1).index); break; case IRthisget: // a = othis.ident writef("\tIRthisget %d, '%s'\n",(code + 1).index,(code + 2).id.value.string); break; case IRneg: // a = -a writef("\tIRneg %d\n",(code + 1).index); break; case IRpos: // a = a writef("\tIRpos %d\n",(code + 1).index); break; case IRcom: // a = ~a writef("\tIRcom %d\n",(code + 1).index); break; case IRnot: // a = !a writef("\tIRnot %d\n",(code + 1).index); break; case IRtypeof: // a = typeof a writef("\tIRtypeof %d\n", (code + 1).index); break; case IRinstance: // a = b instanceof c writef("\tIRinstance %d, %d, %d\n", (code + 1).index, (code + 2).index, (code + 3).index); break; case IRadd: // a = b + c writef("\tIRadd %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRsub: // a = b - c writef("\tIRsub %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRmul: // a = b * c writef("\tIRmul %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRdiv: // a = b / c writef("\tIRdiv %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRmod: // a = b % c writef("\tIRmod %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRshl: // a = b << c writef("\tIRshl %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRshr: // a = b >> c writef("\tIRshr %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRushr: // a = b >>> c writef("\tIRushr %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRand: // a = b & c writef("\tIRand %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRor: // a = b | c writef("\tIRor %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRxor: // a = b ^ c writef("\tIRxor %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRpreinc: // a = ++b.c writef("\tIRpreinc %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRpreincs: // a = ++b.s writef("\tIRpreincs %d, %d, %s\n",(code + 1).index, (code + 2).index, (code + 3).id.value.string); break; case IRpreincscope: // a = ++s writef("\tIRpreincscope %d, '%s', hash=%d\n",(code + 1).index, (code + 2).id.value.string, (code + 3).hash); break; case IRpredec: // a = --b.c writef("\tIRpredec %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRpredecs: // a = --b.s writef("\tIRpredecs %d, %d, %s\n",(code + 1).index, (code + 2).index, (code + 3).id.value.string); break; case IRpredecscope: // a = --s writef("\tIRpredecscope %d, '%s', hash=%d\n",(code + 1).index, (code + 2).id.value.string, (code + 3).hash); break; case IRpostinc: // a = b.c++ writef("\tIRpostinc %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRpostincs: // a = b.s++ writef("\tIRpostincs %d, %d, %s\n",(code + 1).index, (code + 2).index, (code + 3).id.value.string); break; case IRpostincscope: // a = s++ writef("\tIRpostincscope %d, %s\n",(code + 1).index, (code + 2).id.value.string); break; case IRpostdec: // a = b.c-- writef("\tIRpostdec %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRpostdecs: // a = b.s-- writef("\tIRpostdecs %d, %d, %s\n",(code + 1).index, (code + 2).index, (code + 3).id.value.string); break; case IRpostdecscope: // a = s-- writef("\tIRpostdecscope %d, %s\n",(code + 1).index, (code + 2).id.value.string); break; case IRdel: // a = delete b.c writef("\tIRdel %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRdels: // a = delete b.s writef("\tIRdels %d, %d, '%s'\n",(code + 1).index, (code + 2).index, (code + 3).id.value.string); break; case IRdelscope: // a = delete s writef("\tIRdelscope %d, '%s'\n",(code + 1).index, (code + 2).id.value.string); break; case IRclt: // a = (b < c) writef("\tIRclt %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRcle: // a = (b <= c) writef("\tIRcle %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRcgt: // a = (b > c) writef("\tIRcgt %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRcge: // a = (b >= c) writef("\tIRcge %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRceq: // a = (b == c) writef("\tIRceq %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRcne: // a = (b != c) writef("\tIRcne %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRcid: // a = (b === c) writef("\tIRcid %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRcnid: // a = (b !== c) writef("\tIRcnid %d, %d, %d\n",(code + 1).index, (code + 2).index, (code + 3).index); break; case IRjt: // if (b) goto t writef("\tIRjt %d, %d\n", (code + 1).index + address, (code + 2).index); break; case IRjf: // if (!b) goto t writef("\tIRjf %d, %d\n", (code + 1).index + address, (code + 2).index); break; case IRjtb: // if (b) goto t writef("\tIRjtb %d, %d\n", (code + 1).index + address, (code + 2).index); break; case IRjfb: // if (!b) goto t writef("\tIRjfb %d, %d\n", (code + 1).index + address, (code + 2).index); break; case IRjmp: writef("\tIRjmp %d\n", (code + 1).offset + address); break; case IRjlt: // if (b < c) goto t writef("\tIRjlt %d, %d, %d\n",(code + 1).index + address, (code + 2).index, (code + 3).index); break; case IRjle: // if (b <= c) goto t writef("\tIRjle %d, %d, %d\n",(code + 1).index + address, (code + 2).index, (code + 3).index); break; case IRjltc: // if (b < constant) goto t writef("\tIRjltc %d, %d, %g\n",(code + 1).index + address, (code + 2).index, *cast(d_number *)(code + 3)); break; case IRjlec: // if (b <= constant) goto t writef("\tIRjlec %d, %d, %g\n",(code + 1).index + address, (code + 2).index, *cast(d_number *)(code + 3)); break; case IRiter: // a = iter(b) writef("\tIRiter %d, %d\n",(code + 1).index, (code + 2).index); break; case IRnext: // a, b.c, iter writef("\tIRnext %d, %d, %d, %d\n", (code + 1).index, (code + 2).index, (code + 3).index, (code + 4).index); break; case IRnexts: // a, b.s, iter writef("\tIRnexts %d, %d, '%s', %d\n", (code + 1).index, (code + 2).index, (code + 3).id.value.string, (code + 4).index); break; case IRnextscope: // a, s, iter writef ("\tIRnextscope %d, '%s', %d\n", (code + 1).index, (code + 2).id.value.string, (code + 3).index); break; case IRcall: // a = b.c(argc, argv) writef("\tIRcall %d,%d,%d, argc=%d, argv=%d \n", (code + 1).index, (code + 2).index, (code + 3).index, (code + 4).index, (code + 5).index); break; case IRcalls: // a = b.s(argc, argv) writef ("\tIRcalls %d,%d,'%s', argc=%d, argv=%d \n", (code + 1).index, (code + 2).index, (code + 3).id.value.string, (code + 4).index, (code + 5).index); break; case IRcallscope: // a = s(argc, argv) writef ("\tIRcallscope %d,'%s', argc=%d, argv=%d \n", (code + 1).index, (code + 2).id.value.string, (code + 3).index, (code + 4).index); break; case IRputcall: // a = b.c(argc, argv) writef("\tIRputcall %d,%d,%d, argc=%d, argv=%d \n", (code + 1).index, (code + 2).index, (code + 3).index, (code + 4).index, (code + 5).index); break; case IRputcalls: // a = b.s(argc, argv) writef ("\tIRputcalls %d,%d,'%s', argc=%d, argv=%d \n", (code + 1).index, (code + 2).index, (code + 3).id.value.string, (code + 4).index, (code + 5).index); break; case IRputcallscope: // a = s(argc, argv) writef ("\tIRputcallscope %d,'%s', argc=%d, argv=%d \n", (code + 1).index, (code + 2).id.value.string, (code + 3).index, (code + 4).index); break; case IRcallv: // a = v(argc, argv) writef("\tIRcallv %d, %d(argc=%d, argv=%d)\n", (code + 1).index, (code + 2).index, (code + 3).index, (code + 4).index); break; case IRputcallv: // a = v(argc, argv) writef("\tIRputcallv %d, %d(argc=%d, argv=%d)\n", (code + 1).index, (code + 2).index, (code + 3).index, (code + 4).index); break; case IRnew: // a = new b(argc, argv) writef("\tIRnew %d,%d, argc=%d, argv=%d \n", (code + 1).index, (code + 2).index, (code + 3).index, (code + 4).index); break; case IRpush: writef("\tIRpush %d\n",(code + 1).index); break; case IRpop: writef("\tIRpop\n"); break; case IRret: writef("\tIRret\n"); return; case IRretexp: writef("\tIRretexp %d\n",(code + 1).index); return; case IRimpret: writef("\tIRimpret %d\n",(code + 1).index); return; case IRthrow: writef("\tIRthrow %d\n",(code + 1).index); break; case IRassert: writef("\tIRassert %d\n",(code + 1).index); break; case IRtrycatch: writef("\tIRtrycatch %d, '%s'\n", (code + 1).offset + address, (code + 2).id.value.string); break; case IRtryfinally: writef("\tIRtryfinally %d\n", (code + 1).offset + address); break; case IRfinallyret: writef("\tIRfinallyret\n"); break; default: writef("2: Unrecognized IR instruction %d\n", code.opcode); assert(0); // unrecognized IR instruction } } /********************************* * Give size of opcode. */ static uint size(uint opcode) { uint sz = 9999; switch (opcode) { case IRerror: case IRnop: case IRend: sz = 1; break; case IRget: // a = b.c case IRaddass: sz = 4; break; case IRput: // b.c = a sz = 4; break; case IRgets: // a = b.s case IRaddasss: sz = 4; break; case IRgetscope: // a = s sz = 3; break; case IRaddassscope: sz = 4; break; case IRputs: // b.s = a sz = 4; break; case IRputscope: // s = a case IRputdefault: // b = a sz = 3; break; case IRputthis: // a = s sz = 3; break; case IRmov: // a = b sz = 3; break; case IRstring: // a = "string" sz = 3; break; case IRobject: // a = object sz = 3; break; case IRthis: // a = this sz = 2; break; case IRnumber: // a = number sz = 4; break; case IRboolean: // a = boolean sz = 3; break; case IRnull: // a = null sz = 2; break; case IRundefined: // a = undefined sz = 2; break; case IRthisget: // a = othis.ident sz = 3; break; case IRneg: // a = -a case IRpos: // a = a case IRcom: // a = ~a case IRnot: // a = !a case IRtypeof: // a = typeof a sz = 2; break; case IRinstance: // a = b instanceof c case IRadd: // a = b + c case IRsub: // a = b - c case IRmul: // a = b * c case IRdiv: // a = b / c case IRmod: // a = b % c case IRshl: // a = b << c case IRshr: // a = b >> c case IRushr: // a = b >>> c case IRand: // a = b & c case IRor: // a = b | c case IRxor: // a = b ^ c sz = 4; break; case IRpreinc: // a = ++b.c case IRpreincs: // a = ++b.s case IRpredec: // a = --b.c case IRpredecs: // a = --b.s case IRpostinc: // a = b.c++ case IRpostincs: // a = b.s++ case IRpostdec: // a = b.c-- case IRpostdecs: // a = b.s-- sz = 4; break; case IRpostincscope: // a = s++ case IRpostdecscope: // a = s-- sz = 3; break; case IRpreincscope: // a = ++s case IRpredecscope: // a = --s sz = 4; break; case IRdel: // a = delete b.c case IRdels: // a = delete b.s sz = 4; break; case IRdelscope: // a = delete s sz = 3; break; case IRclt: // a = (b < c) case IRcle: // a = (b <= c) case IRcgt: // a = (b > c) case IRcge: // a = (b >= c) case IRceq: // a = (b == c) case IRcne: // a = (b != c) case IRcid: // a = (b === c) case IRcnid: // a = (b !== c) case IRjlt: // if (b < c) goto t case IRjle: // if (b <= c) goto t sz = 4; break; case IRjltc: // if (b < constant) goto t case IRjlec: // if (b <= constant) goto t sz = 5; break; case IRjt: // if (b) goto t case IRjf: // if (!b) goto t case IRjtb: // if (b) goto t case IRjfb: // if (!b) goto t sz = 3; break; case IRjmp: sz = 2; break; case IRiter: // a = iter(b) sz = 3; break; case IRnext: // a, b.c, iter case IRnexts: // a, b.s, iter sz = 5; break; case IRnextscope: // a, s, iter sz = 4; break; case IRcall: // a = b.c(argc, argv) case IRcalls: // a = b.s(argc, argv) case IRputcall: // b.c(argc, argv) = a case IRputcalls: // b.s(argc, argv) = a sz = 6; break; case IRcallscope: // a = s(argc, argv) case IRputcallscope: // s(argc, argv) = a case IRcallv: case IRputcallv: sz = 5; break; case IRnew: // a = new b(argc, argv) sz = 5; break; case IRpush: sz = 2; break; case IRpop: sz = 1; break; case IRfinallyret: case IRret: sz = 1; break; case IRretexp: case IRimpret: case IRthrow: sz = 2; break; case IRtrycatch: sz = 3; break; case IRtryfinally: sz = 2; break; case IRassert: sz = 2; break; default: writef("3: Unrecognized IR instruction %d, IRMAX = %d\n", opcode, IRMAX); assert(0); // unrecognized IR instruction } assert(sz <= 6); return sz; } static void printfunc(IR *code) { IR *codestart = code; for (;;) { writef("%2d(%d):", code - codestart, code.linnum); print(code - codestart, code); if (code.opcode == IRend) return; code += size(code.opcode); } } /*************************************** * Verify that it is a correct sequence of code. * Useful for isolating memory corruption bugs. */ static uint verify(uint linnum, IR *codestart) { debug (VERIFY) { uint checksum = 0; uint sz; uint i; IR *code; // Verify code for (code = codestart;;) { switch (code.opcode) { case IRend: return checksum; case IRerror: writef("verify failure line %u\n", linnum); assert(0); break; default: if (code.opcode >= IRMAX) { writef("undefined opcode %d in code %p\n", code.opcode, codestart); assert(0); } sz = IR.size(code.opcode); for (i = 0; i < sz; i++) { checksum += code.opcode; code++; } break; } } } else return 0; } }