|  | /******************************************************************************* | 
|  | * 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.vm3.compiler; | 
|  |  | 
|  | import java.util.Hashtable; | 
|  |  | 
|  | import org.luaj.vm3.LocVars; | 
|  | import org.luaj.vm3.Lua; | 
|  | import org.luaj.vm3.LuaDouble; | 
|  | import org.luaj.vm3.LuaInteger; | 
|  | import org.luaj.vm3.LuaString; | 
|  | import org.luaj.vm3.LuaValue; | 
|  | import org.luaj.vm3.Prototype; | 
|  | import org.luaj.vm3.Upvaldesc; | 
|  | import org.luaj.vm3.compiler.LexState.ConsControl; | 
|  | import org.luaj.vm3.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 */ | 
|  | } | 
|  |  | 
|  | } |