/******************************************************************************* * Copyright (c) 2009 Luaj.org. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ package org.luaj.vm2.compiler; import java.util.Hashtable; import org.luaj.vm2.LocVars; import org.luaj.vm2.Lua; import org.luaj.vm2.LuaDouble; import org.luaj.vm2.LuaInteger; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaUserdata; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Prototype; import org.luaj.vm2.Upvaldesc; import org.luaj.vm2.compiler.LexState.ConsControl; import org.luaj.vm2.compiler.LexState.expdesc; public class FuncState extends LuaC { static class BlockCnt { BlockCnt previous; /* chain */ short firstlabel; /* index of first label in this block */ short firstgoto; /* index of first pending goto in this block */ short nactvar; /* # active locals outside the breakable structure */ boolean upval; /* true if some variable in the block is an upvalue */ boolean isloop; /* true if `block' is a loop */ }; Prototype f; /* current function header */ Hashtable h; /* table to find (and reuse) elements in `k' */ FuncState prev; /* enclosing function */ LexState ls; /* lexical state */ LuaC L; /* compiler being invoked */ BlockCnt bl; /* chain of current blocks */ int pc; /* next position to code (equivalent to `ncode') */ int lasttarget; /* `pc' of last `jump target' */ IntPtr jpc; /* list of pending jumps to `pc' */ int nk; /* number of elements in `k' */ int np; /* number of elements in `p' */ int firstlocal; /* index of first local var (in Dyndata array) */ short nlocvars; /* number of elements in `locvars' */ short nactvar; /* number of active local variables */ short nups; /* number of upvalues */ short freereg; /* first free register */ FuncState() { } // ============================================================= // from lcode.h // ============================================================= InstructionPtr getcodePtr(expdesc e) { return new InstructionPtr( f.code, e.u.info ); } int getcode(expdesc e) { return f.code[e.u.info]; } int codeAsBx(int o, int A, int sBx) { return codeABx(o,A,sBx+MAXARG_sBx); } void setmultret(expdesc e) { setreturns(e, LUA_MULTRET); } // ============================================================= // from lparser.c // ============================================================= /* check for repeated labels on the same block */ void checkrepeated (LexState.Labeldesc[] ll, int ll_n, LuaString label) { int i; for (i = bl.firstlabel; i < ll_n; i++) { if (label.eq_b(ll[i].name)) { String msg = ls.L.pushfstring( "label '" + label + " already defined on line " + ll[i].line); ls.semerror(msg); } } } void checklimit(int v, int l, String msg) { if ( v > l ) errorlimit( l, msg ); } void errorlimit (int limit, String what) { // TODO: report message logic. String msg = (f.linedefined == 0) ? L.pushfstring("main function has more than "+limit+" "+what) : L.pushfstring("function at line "+f.linedefined+" has more than "+limit+" "+what); ls.lexerror(msg, 0); } LocVars getlocvar(int i) { int idx = ls.dyd.actvar[firstlocal + i].idx; _assert(idx < nlocvars); return f.locvars[idx]; } void removevars (int tolevel) { ls.dyd.n_actvar -= (nactvar - tolevel); while (nactvar > tolevel) getlocvar(--nactvar).endpc = pc; } int searchupvalue (LuaString name) { int i; Upvaldesc[] up = f.upvalues; for (i = 0; i < nups; i++) if (up[i].name.eq_b(name)) return i; return -1; /* not found */ } int newupvalue (LuaString name, expdesc v) { checklimit(nups + 1, LUAI_MAXUPVAL, "upvalues"); if (f.upvalues == null || nups + 1 > f.upvalues.length) f.upvalues = realloc( f.upvalues, nups > 0 ? nups*2 : 1 ); f.upvalues[nups] = new Upvaldesc(name, v.k == LexState.VLOCAL, v.u.info); return nups++; } int searchvar(LuaString n) { int i; for (i = nactvar - 1; i >= 0; i--) { if (n.eq_b(getlocvar(i).varname)) return i; } return -1; /* not found */ } void markupval(int level) { BlockCnt bl = this.bl; while (bl.nactvar > level) bl = bl.previous; bl.upval = true; } static int singlevaraux(FuncState fs, LuaString n, expdesc var, int base) { if (fs == null) /* no more levels? */ return LexState.VVOID; /* default is global */ int v = fs.searchvar(n); /* look up at current level */ if (v >= 0) { var.init(LexState.VLOCAL, v); if (base == 0) fs.markupval(v); /* local will be used as an upval */ return LexState.VLOCAL; } else { /* not found at current level; try upvalues */ int idx = fs.searchupvalue(n); /* try existing upvalues */ if (idx < 0) { /* not found? */ if (singlevaraux(fs.prev, n, var, 0) == LexState.VVOID) /* try upper levels */ return LexState.VVOID; /* not found; is a global */ /* else was LOCAL or UPVAL */ idx = fs.newupvalue(n, var); /* will be a new upvalue */ } var.init(LexState.VUPVAL, idx); return LexState.VUPVAL; } } /* ** "export" pending gotos to outer level, to check them against ** outer labels; if the block being exited has upvalues, and ** the goto exits the scope of any variable (which can be the ** upvalue), close those variables being exited. */ void movegotosout(BlockCnt bl) { int i = bl.firstgoto; final LexState.Labeldesc[] gl = ls.dyd.gt; /* correct pending gotos to current block and try to close it with visible labels */ while (i < ls.dyd.n_gt) { LexState.Labeldesc gt = gl[i]; if (gt.nactvar > bl.nactvar) { if (bl.upval) patchclose(gt.pc, bl.nactvar); gt.nactvar = bl.nactvar; } if (!ls.findlabel(i)) i++; /* move to next one */ } } void enterblock (BlockCnt bl, boolean isloop) { bl.isloop = isloop; bl.nactvar = nactvar; bl.firstlabel = (short) ls.dyd.n_label; bl.firstgoto = (short) ls.dyd.n_gt; bl.upval = false; bl.previous = this.bl; this.bl = bl; _assert(this.freereg == this.nactvar); } void leaveblock() { BlockCnt bl = this.bl; if (bl.previous != null && bl.upval) { /* create a 'jump to here' to close upvalues */ int j = this.jump(); this.patchclose(j, bl.nactvar); this.patchtohere(j); } if (bl.isloop) ls.breaklabel(); /* close pending breaks */ this.bl = bl.previous; this.removevars(bl.nactvar); _assert(bl.nactvar == this.nactvar); this.freereg = this.nactvar; /* free registers */ ls.dyd.n_label = bl.firstlabel; /* remove local labels */ if (bl.previous != null) /* inner block? */ this.movegotosout(bl); /* update pending gotos to outer block */ else if (bl.firstgoto < ls.dyd.n_gt) /* pending gotos in outer block? */ ls.undefgoto(ls.dyd.gt[bl.firstgoto]); /* error */ } void closelistfield(ConsControl cc) { if (cc.v.k == LexState.VVOID) return; /* there is no list item */ this.exp2nextreg(cc.v); cc.v.k = LexState.VVOID; if (cc.tostore == LFIELDS_PER_FLUSH) { this.setlist(cc.t.u.info, cc.na, cc.tostore); /* flush */ cc.tostore = 0; /* no more items pending */ } } boolean hasmultret(int k) { return ((k) == LexState.VCALL || (k) == LexState.VVARARG); } void lastlistfield (ConsControl cc) { if (cc.tostore == 0) return; if (hasmultret(cc.v.k)) { this.setmultret(cc.v); this.setlist(cc.t.u.info, cc.na, LUA_MULTRET); cc.na--; /** do not count last expression (unknown number of elements) */ } else { if (cc.v.k != LexState.VVOID) this.exp2nextreg(cc.v); this.setlist(cc.t.u.info, cc.na, cc.tostore); } } // ============================================================= // from lcode.c // ============================================================= void nil(int from, int n) { int l = from + n - 1; /* last register to set nil */ if (this.pc > this.lasttarget && pc > 0) { /* no jumps to current position? */ final int previous_code = f.code[pc - 1]; if (GET_OPCODE(previous_code) == OP_LOADNIL) { int pfrom = GETARG_A(previous_code); int pl = pfrom + GETARG_B(previous_code); if ((pfrom <= from && from <= pl + 1) || (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ if (pl > l) l = pl; /* l = max(l, pl) */ InstructionPtr previous = new InstructionPtr(this.f.code, this.pc - 1); SETARG_A(previous, from); SETARG_B(previous, l - from); return; } } /* else go through */ } this.codeABC(OP_LOADNIL, from, n - 1, 0); } int jump() { int jpc = this.jpc.i; /* save list of jumps to here */ this.jpc.i = LexState.NO_JUMP; IntPtr j = new IntPtr(this.codeAsBx(OP_JMP, 0, LexState.NO_JUMP)); this.concat(j, jpc); /* keep them on hold */ return j.i; } void ret(int first, int nret) { this.codeABC(OP_RETURN, first, nret + 1, 0); } int condjump(int /* OpCode */op, int A, int B, int C) { this.codeABC(op, A, B, C); return this.jump(); } void fixjump(int pc, int dest) { InstructionPtr jmp = new InstructionPtr(this.f.code, pc); int offset = dest - (pc + 1); _assert (dest != LexState.NO_JUMP); if (Math.abs(offset) > MAXARG_sBx) ls.syntaxerror("control structure too long"); SETARG_sBx(jmp, offset); } /* * * returns current `pc' and marks it as a jump target (to avoid wrong * * optimizations with consecutive instructions not in the same basic block). */ int getlabel() { this.lasttarget = this.pc; return this.pc; } int getjump(int pc) { int offset = GETARG_sBx(this.f.code[pc]); /* point to itself represents end of list */ if (offset == LexState.NO_JUMP) /* end of list */ return LexState.NO_JUMP; else /* turn offset into absolute position */ return (pc + 1) + offset; } InstructionPtr getjumpcontrol(int pc) { InstructionPtr pi = new InstructionPtr(this.f.code, pc); if (pc >= 1 && testTMode(GET_OPCODE(pi.code[pi.idx - 1]))) return new InstructionPtr(pi.code, pi.idx - 1); else return pi; } /* * * check whether list has any jump that do not produce a value * (or * produce an inverted value) */ boolean need_value(int list) { for (; list != LexState.NO_JUMP; list = this.getjump(list)) { int i = this.getjumpcontrol(list).get(); if (GET_OPCODE(i) != OP_TESTSET) return true; } return false; /* not found */ } boolean patchtestreg(int node, int reg) { InstructionPtr i = this.getjumpcontrol(node); if (GET_OPCODE(i.get()) != OP_TESTSET) /* cannot patch other instructions */ return false; if (reg != NO_REG && reg != GETARG_B(i.get())) SETARG_A(i, reg); else /* no register to put value or register already has the value */ i.set(CREATE_ABC(OP_TEST, GETARG_B(i.get()), 0, Lua.GETARG_C(i.get()))); return true; } void removevalues(int list) { for (; list != LexState.NO_JUMP; list = this.getjump(list)) this.patchtestreg(list, NO_REG); } void patchlistaux(int list, int vtarget, int reg, int dtarget) { while (list != LexState.NO_JUMP) { int next = this.getjump(list); if (this.patchtestreg(list, reg)) this.fixjump(list, vtarget); else this.fixjump(list, dtarget); /* jump to default target */ list = next; } } void dischargejpc() { this.patchlistaux(this.jpc.i, this.pc, NO_REG, this.pc); this.jpc.i = LexState.NO_JUMP; } void patchlist(int list, int target) { if (target == this.pc) this.patchtohere(list); else { _assert (target < this.pc); this.patchlistaux(list, target, NO_REG, target); } } void patchclose(int list, int level) { level++; /* argument is +1 to reserve 0 as non-op */ while (list != LexState.NO_JUMP) { int next = getjump(list); _assert(GET_OPCODE(f.code[list]) == OP_JMP && (GETARG_A(f.code[list]) == 0 || GETARG_A(f.code[list]) >= level)); SETARG_A(f.code, list, level); list = next; } } void patchtohere(int list) { this.getlabel(); this.concat(this.jpc, list); } void concat(IntPtr l1, int l2) { if (l2 == LexState.NO_JUMP) return; if (l1.i == LexState.NO_JUMP) l1.i = l2; else { int list = l1.i; int next; while ((next = this.getjump(list)) != LexState.NO_JUMP) /* find last element */ list = next; this.fixjump(list, l2); } } void checkstack(int n) { int newstack = this.freereg + n; if (newstack > this.f.maxstacksize) { if (newstack >= MAXSTACK) ls.syntaxerror("function or expression too complex"); this.f.maxstacksize = newstack; } } void reserveregs(int n) { this.checkstack(n); this.freereg += n; } void freereg(int reg) { if (!ISK(reg) && reg >= this.nactvar) { this.freereg--; _assert (reg == this.freereg); } } void freeexp(expdesc e) { if (e.k == LexState.VNONRELOC) this.freereg(e.u.info); } int addk(LuaValue v) { if (this.h == null) { this.h = new Hashtable(); } else if (this.h.containsKey(v)) { return ((Integer) h.get(v)).intValue(); } final int idx = this.nk; this.h.put(v, new Integer(idx)); final Prototype f = this.f; if (f.k == null || nk + 1 >= f.k.length) f.k = realloc( f.k, nk*2 + 1 ); f.k[this.nk++] = v; return idx; } int stringK(LuaString s) { return this.addk(s); } int numberK(LuaValue r) { if ( r instanceof LuaDouble ) { double d = r.todouble(); int i = (int) d; if ( d == (double) i ) r = LuaInteger.valueOf(i); } return this.addk(r); } int boolK(boolean b) { return this.addk((b ? LuaValue.TRUE : LuaValue.FALSE)); } int nilK() { return this.addk(LuaValue.NIL); } void setreturns(expdesc e, int nresults) { if (e.k == LexState.VCALL) { /* expression is an open function call? */ SETARG_C(this.getcodePtr(e), nresults + 1); } else if (e.k == LexState.VVARARG) { SETARG_B(this.getcodePtr(e), nresults + 1); SETARG_A(this.getcodePtr(e), this.freereg); this.reserveregs(1); } } void setoneret(expdesc e) { if (e.k == LexState.VCALL) { /* expression is an open function call? */ e.k = LexState.VNONRELOC; e.u.info = GETARG_A(this.getcode(e)); } else if (e.k == LexState.VVARARG) { SETARG_B(this.getcodePtr(e), 2); e.k = LexState.VRELOCABLE; /* can relocate its simple result */ } } void dischargevars(expdesc e) { switch (e.k) { case LexState.VLOCAL: { e.k = LexState.VNONRELOC; break; } case LexState.VUPVAL: { e.u.info = this.codeABC(OP_GETUPVAL, 0, e.u.info, 0); e.k = LexState.VRELOCABLE; break; } case LexState.VINDEXED: { int op = OP_GETTABUP; /* assume 't' is in an upvalue */ this.freereg(e.u.ind_idx); if (e.u.ind_vt == LexState.VLOCAL) { /* 't' is in a register? */ this.freereg(e.u.ind_t); op = OP_GETTABLE; } e.u.info = this.codeABC(op, 0, e.u.ind_t, e.u.ind_idx); e.k = LexState.VRELOCABLE; break; } case LexState.VVARARG: case LexState.VCALL: { this.setoneret(e); break; } default: break; /* there is one value available (somewhere) */ } } int code_label(int A, int b, int jump) { this.getlabel(); /* those instructions may be jump targets */ return this.codeABC(OP_LOADBOOL, A, b, jump); } void discharge2reg(expdesc e, int reg) { this.dischargevars(e); switch (e.k) { case LexState.VNIL: { this.nil(reg, 1); break; } case LexState.VFALSE: case LexState.VTRUE: { this.codeABC(OP_LOADBOOL, reg, (e.k == LexState.VTRUE ? 1 : 0), 0); break; } case LexState.VK: { this.codeABx(OP_LOADK, reg, e.u.info); break; } case LexState.VKNUM: { this.codeABx(OP_LOADK, reg, this.numberK(e.u.nval())); break; } case LexState.VRELOCABLE: { InstructionPtr pc = this.getcodePtr(e); SETARG_A(pc, reg); break; } case LexState.VNONRELOC: { if (reg != e.u.info) this.codeABC(OP_MOVE, reg, e.u.info, 0); break; } default: { _assert (e.k == LexState.VVOID || e.k == LexState.VJMP); return; /* nothing to do... */ } } e.u.info = reg; e.k = LexState.VNONRELOC; } void discharge2anyreg(expdesc e) { if (e.k != LexState.VNONRELOC) { this.reserveregs(1); this.discharge2reg(e, this.freereg - 1); } } void exp2reg(expdesc e, int reg) { this.discharge2reg(e, reg); if (e.k == LexState.VJMP) this.concat(e.t, e.u.info); /* put this jump in `t' list */ if (e.hasjumps()) { int _final; /* position after whole expression */ int p_f = LexState.NO_JUMP; /* position of an eventual LOAD false */ int p_t = LexState.NO_JUMP; /* position of an eventual LOAD true */ if (this.need_value(e.t.i) || this.need_value(e.f.i)) { int fj = (e.k == LexState.VJMP) ? LexState.NO_JUMP : this .jump(); p_f = this.code_label(reg, 0, 1); p_t = this.code_label(reg, 1, 0); this.patchtohere(fj); } _final = this.getlabel(); this.patchlistaux(e.f.i, _final, reg, p_f); this.patchlistaux(e.t.i, _final, reg, p_t); } e.f.i = e.t.i = LexState.NO_JUMP; e.u.info = reg; e.k = LexState.VNONRELOC; } void exp2nextreg(expdesc e) { this.dischargevars(e); this.freeexp(e); this.reserveregs(1); this.exp2reg(e, this.freereg - 1); } int exp2anyreg(expdesc e) { this.dischargevars(e); if (e.k == LexState.VNONRELOC) { if (!e.hasjumps()) return e.u.info; /* exp is already in a register */ if (e.u.info >= this.nactvar) { /* reg. is not a local? */ this.exp2reg(e, e.u.info); /* put value on it */ return e.u.info; } } this.exp2nextreg(e); /* default */ return e.u.info; } void exp2anyregup (expdesc e) { if (e.k != LexState.VUPVAL || e.hasjumps()) exp2anyreg(e); } void exp2val(expdesc e) { if (e.hasjumps()) this.exp2anyreg(e); else this.dischargevars(e); } int exp2RK(expdesc e) { this.exp2val(e); switch (e.k) { case LexState.VTRUE: case LexState.VFALSE: case LexState.VNIL: { if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */ e.u.info = (e.k == LexState.VNIL) ? this.nilK() : this.boolK((e.k == LexState.VTRUE)); e.k = LexState.VK; return RKASK(e.u.info); } else break; } case LexState.VKNUM: { e.u.info = this.numberK(e.u.nval()); e.k = LexState.VK; /* go through */ } case LexState.VK: { if (e.u.info <= MAXINDEXRK) /* constant fit in argC? */ return RKASK(e.u.info); else break; } default: break; } /* not a constant in the right range: put it in a register */ return this.exp2anyreg(e); } void storevar(expdesc var, expdesc ex) { switch (var.k) { case LexState.VLOCAL: { this.freeexp(ex); this.exp2reg(ex, var.u.info); return; } case LexState.VUPVAL: { int e = this.exp2anyreg(ex); this.codeABC(OP_SETUPVAL, e, var.u.info, 0); break; } case LexState.VINDEXED: { int op = (var.u.ind_vt == LexState.VLOCAL) ? OP_SETTABLE : OP_SETTABUP; int e = this.exp2RK(ex); this.codeABC(op, var.u.ind_t, var.u.ind_idx, e); break; } default: { _assert (false); /* invalid var kind to store */ break; } } this.freeexp(ex); } void self(expdesc e, expdesc key) { int func; this.exp2anyreg(e); this.freeexp(e); func = this.freereg; this.reserveregs(2); this.codeABC(OP_SELF, func, e.u.info, this.exp2RK(key)); this.freeexp(key); e.u.info = func; e.k = LexState.VNONRELOC; } void invertjump(expdesc e) { InstructionPtr pc = this.getjumpcontrol(e.u.info); _assert (testTMode(GET_OPCODE(pc.get())) && GET_OPCODE(pc.get()) != OP_TESTSET && Lua .GET_OPCODE(pc.get()) != OP_TEST); // SETARG_A(pc, !(GETARG_A(pc.get()))); int a = GETARG_A(pc.get()); int nota = (a!=0? 0: 1); SETARG_A(pc, nota); } int jumponcond(expdesc e, int cond) { if (e.k == LexState.VRELOCABLE) { int ie = this.getcode(e); if (GET_OPCODE(ie) == OP_NOT) { this.pc--; /* remove previous OP_NOT */ return this.condjump(OP_TEST, GETARG_B(ie), 0, (cond!=0? 0: 1)); } /* else go through */ } this.discharge2anyreg(e); this.freeexp(e); return this.condjump(OP_TESTSET, NO_REG, e.u.info, cond); } void goiftrue(expdesc e) { int pc; /* pc of last jump */ this.dischargevars(e); switch (e.k) { case LexState.VJMP: { this.invertjump(e); pc = e.u.info; break; } case LexState.VK: case LexState.VKNUM: case LexState.VTRUE: { pc = LexState.NO_JUMP; /* always true; do nothing */ break; } default: { pc = this.jumponcond(e, 0); break; } } this.concat(e.f, pc); /* insert last jump in `f' list */ this.patchtohere(e.t.i); e.t.i = LexState.NO_JUMP; } void goiffalse(expdesc e) { int pc; /* pc of last jump */ this.dischargevars(e); switch (e.k) { case LexState.VJMP: { pc = e.u.info; break; } case LexState.VNIL: case LexState.VFALSE: { pc = LexState.NO_JUMP; /* always false; do nothing */ break; } default: { pc = this.jumponcond(e, 1); break; } } this.concat(e.t, pc); /* insert last jump in `t' list */ this.patchtohere(e.f.i); e.f.i = LexState.NO_JUMP; } void codenot(expdesc e) { this.dischargevars(e); switch (e.k) { case LexState.VNIL: case LexState.VFALSE: { e.k = LexState.VTRUE; break; } case LexState.VK: case LexState.VKNUM: case LexState.VTRUE: { e.k = LexState.VFALSE; break; } case LexState.VJMP: { this.invertjump(e); break; } case LexState.VRELOCABLE: case LexState.VNONRELOC: { this.discharge2anyreg(e); this.freeexp(e); e.u.info = this.codeABC(OP_NOT, 0, e.u.info, 0); e.k = LexState.VRELOCABLE; break; } default: { _assert (false); /* cannot happen */ break; } } /* interchange true and false lists */ { int temp = e.f.i; e.f.i = e.t.i; e.t.i = temp; } this.removevalues(e.f.i); this.removevalues(e.t.i); } static boolean vkisinreg(int k) { return ((k) == LexState.VNONRELOC || (k) == LexState.VLOCAL); } void indexed(expdesc t, expdesc k) { t.u.ind_t = (short) t.u.info; t.u.ind_idx = (short) this.exp2RK(k); LuaC._assert(t.k == LexState.VUPVAL || vkisinreg(t.k)); t.u.ind_vt = (short) ((t.k == LexState.VUPVAL) ? LexState.VUPVAL : LexState.VLOCAL); t.k = LexState.VINDEXED; } boolean constfolding(int op, expdesc e1, expdesc e2) { LuaValue v1, v2, r; if (!e1.isnumeral() || !e2.isnumeral()) return false; if ((op == OP_DIV || op == OP_MOD) && e2.u.nval().eq_b(LuaValue.ZERO)) return false; /* do not attempt to divide by 0 */ v1 = e1.u.nval(); v2 = e2.u.nval(); switch (op) { case OP_ADD: r = v1.add(v2); break; case OP_SUB: r = v1.sub(v2); break; case OP_MUL: r = v1.mul(v2); break; case OP_DIV: r = v1.div(v2); break; case OP_MOD: r = v1.mod(v2); break; case OP_POW: r = v1.pow(v2); break; case OP_UNM: r = v1.neg(); break; case OP_LEN: // r = v1.len(); // break; return false; /* no constant folding for 'len' */ default: _assert (false); r = null; break; } if ( Double.isNaN(r.todouble()) ) return false; /* do not attempt to produce NaN */ e1.u.setNval( r ); return true; } void codearith(int op, expdesc e1, expdesc e2, int line) { if (constfolding(op, e1, e2)) return; else { int o2 = (op != OP_UNM && op != OP_LEN) ? this.exp2RK(e2) : 0; int o1 = this.exp2RK(e1); if (o1 > o2) { this.freeexp(e1); this.freeexp(e2); } else { this.freeexp(e2); this.freeexp(e1); } e1.u.info = this.codeABC(op, 0, o1, o2); e1.k = LexState.VRELOCABLE; fixline(line); } } void codecomp(int /* OpCode */op, int cond, expdesc e1, expdesc e2) { int o1 = this.exp2RK(e1); int o2 = this.exp2RK(e2); this.freeexp(e2); this.freeexp(e1); if (cond == 0 && op != OP_EQ) { int temp; /* exchange args to replace by `<' or `<=' */ temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */ cond = 1; } e1.u.info = this.condjump(op, cond, o1, o2); e1.k = LexState.VJMP; } void prefix(int /* UnOpr */op, expdesc e, int line) { expdesc e2 = new expdesc(); e2.init(LexState.VKNUM, 0); switch (op) { case LexState.OPR_MINUS: { if (e.isnumeral()) /* minus constant? */ e.u.setNval(e.u.nval().neg()); /* fold it */ else { this.exp2anyreg(e); this.codearith(OP_UNM, e, e2, line); } break; } case LexState.OPR_NOT: this.codenot(e); break; case LexState.OPR_LEN: { this.exp2anyreg(e); /* cannot operate on constants */ this.codearith(OP_LEN, e, e2, line); break; } default: _assert (false); } } void infix(int /* BinOpr */op, expdesc v) { switch (op) { case LexState.OPR_AND: { this.goiftrue(v); break; } case LexState.OPR_OR: { this.goiffalse(v); break; } case LexState.OPR_CONCAT: { this.exp2nextreg(v); /* operand must be on the `stack' */ break; } case LexState.OPR_ADD: case LexState.OPR_SUB: case LexState.OPR_MUL: case LexState.OPR_DIV: case LexState.OPR_MOD: case LexState.OPR_POW: { if (!v.isnumeral()) this.exp2RK(v); break; } default: { this.exp2RK(v); break; } } } void posfix(int op, expdesc e1, expdesc e2, int line) { switch (op) { case LexState.OPR_AND: { _assert (e1.t.i == LexState.NO_JUMP); /* list must be closed */ this.dischargevars(e2); this.concat(e2.f, e1.f.i); // *e1 = *e2; e1.setvalue(e2); break; } case LexState.OPR_OR: { _assert (e1.f.i == LexState.NO_JUMP); /* list must be closed */ this.dischargevars(e2); this.concat(e2.t, e1.t.i); // *e1 = *e2; e1.setvalue(e2); break; } case LexState.OPR_CONCAT: { this.exp2val(e2); if (e2.k == LexState.VRELOCABLE && GET_OPCODE(this.getcode(e2)) == OP_CONCAT) { _assert (e1.u.info == GETARG_B(this.getcode(e2)) - 1); this.freeexp(e1); SETARG_B(this.getcodePtr(e2), e1.u.info); e1.k = LexState.VRELOCABLE; e1.u.info = e2.u.info; } else { this.exp2nextreg(e2); /* operand must be on the 'stack' */ this.codearith(OP_CONCAT, e1, e2, line); } break; } case LexState.OPR_ADD: this.codearith(OP_ADD, e1, e2, line); break; case LexState.OPR_SUB: this.codearith(OP_SUB, e1, e2, line); break; case LexState.OPR_MUL: this.codearith(OP_MUL, e1, e2, line); break; case LexState.OPR_DIV: this.codearith(OP_DIV, e1, e2, line); break; case LexState.OPR_MOD: this.codearith(OP_MOD, e1, e2, line); break; case LexState.OPR_POW: this.codearith(OP_POW, e1, e2, line); break; case LexState.OPR_EQ: this.codecomp(OP_EQ, 1, e1, e2); break; case LexState.OPR_NE: this.codecomp(OP_EQ, 0, e1, e2); break; case LexState.OPR_LT: this.codecomp(OP_LT, 1, e1, e2); break; case LexState.OPR_LE: this.codecomp(OP_LE, 1, e1, e2); break; case LexState.OPR_GT: this.codecomp(OP_LT, 0, e1, e2); break; case LexState.OPR_GE: this.codecomp(OP_LE, 0, e1, e2); break; default: _assert (false); } } void fixline(int line) { this.f.lineinfo[this.pc - 1] = line; } int code(int instruction, int line) { Prototype f = this.f; this.dischargejpc(); /* `pc' will change */ /* put new instruction in code array */ if (f.code == null || this.pc + 1 > f.code.length) f.code = LuaC.realloc(f.code, this.pc * 2 + 1); f.code[this.pc] = instruction; /* save corresponding line information */ if (f.lineinfo == null || this.pc + 1 > f.lineinfo.length) f.lineinfo = LuaC.realloc(f.lineinfo, this.pc * 2 + 1); f.lineinfo[this.pc] = line; return this.pc++; } int codeABC(int o, int a, int b, int c) { _assert (getOpMode(o) == iABC); _assert (getBMode(o) != OpArgN || b == 0); _assert (getCMode(o) != OpArgN || c == 0); return this.code(CREATE_ABC(o, a, b, c), this.ls.lastline); } int codeABx(int o, int a, int bc) { _assert (getOpMode(o) == iABx || getOpMode(o) == iAsBx); _assert (getCMode(o) == OpArgN); _assert (bc >= 0 && bc <= Lua.MAXARG_Bx); return this.code(CREATE_ABx(o, a, bc), this.ls.lastline); } void setlist(int base, int nelems, int tostore) { int c = (nelems - 1) / LFIELDS_PER_FLUSH + 1; int b = (tostore == LUA_MULTRET) ? 0 : tostore; _assert (tostore != 0); if (c <= MAXARG_C) this.codeABC(OP_SETLIST, base, b, c); else { this.codeABC(OP_SETLIST, base, b, 0); this.code(c, this.ls.lastline); } this.freereg = (short) (base + 1); /* free registers with list values */ } }