/*
* Copyright 2010 Jon S Akhtar (Sylvanaar)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sylvanaar.idea.Lua.lang.parser.kahlua;
import com.intellij.openapi.diagnostic.Logger;
import com.sylvanaar.idea.Lua.lang.parser.LuaElementTypes;
import se.krka.kahlua.vm.Prototype;
import java.util.Hashtable;
public class FuncState {
private static final Logger log = Logger.getInstance("Lua.Parser.FuncState");
private static final Object NULL_OBJECT = new Object();
/* information about local variables */
public String[] locvars;
/* upvalue names */
public String[] upvalues;
public int linedefined;
public int lastlinedefined;
public int isVararg;
Prototype f; /* current function header */
// LTable h; /* table to find (and reuse) elements in `k' */
Hashtable htable; /* table to find (and reuse) elements in `k' */
FuncState prev; /* enclosing function */
KahluaParser ls; /* lexical state */
BlockCnt bl; /* chain of current blocks */
int pc; /* next position to code (equivalent to `ncode') */
int lasttarget; /* `pc' of last `jump target' */
int jpc; /* list of pending jumps to `pc' */
int freereg; /* first free register */
int nk; /* number of elements in `k' */
int np; /* number of elements in `p' */
int nlocvars; /* number of elements in `locvars' */
int nactvar; /* number of active local variables */
int[] upvalues_k = new int[LUAI_MAXUPVALUES]; /* upvalues */
int[] upvalues_info = new int[LUAI_MAXUPVALUES]; /* upvalues */
short actvar[] = new short[LUAI_MAXVARS]; /* declared-variable stack */
FuncState(KahluaParser lexState) {
Prototype f = new Prototype();
if ( lexState.fs!=null )
f.name = lexState.fs.f.name;
this.f = f;
this.prev = lexState.fs; /* linked list of funcstates */
this.ls = lexState;
lexState.fs = this;
this.pc = 0;
this.lasttarget = -1;
this.jpc = KahluaParser.NO_JUMP;
this.freereg = 0;
this.nk = 0;
this.np = 0;
this.nlocvars = 0;
this.nactvar = 0;
this.bl = null;
f.maxStacksize = 2; /* registers 0/1 are always valid */
//fs.h = new LTable();
this.htable = new Hashtable();
}
// =============================================================
// from lcode.h
// =============================================================
InstructionPtr getcodePtr(ExpDesc e) {
return new InstructionPtr( f.code, e.info );
}
int getcode(ExpDesc e) {
return f.code[e.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
// =============================================================
String getlocvar(int i) {
if (i < LUAI_MAXVARS)
return locvars[actvar[i]];
else
log.warn("getlocvar attpempting to get out of bounds index");
return null;
}
boolean checklimit(int v, int l, String msg) {
if ( v > l )
return errorlimit( l, msg );
return true;
}
boolean errorlimit (int limit, String what) {
String msg = (linedefined == 0) ?
"main function has more than "+limit+" "+what :
"function at line "+linedefined+" has more than "+limit+" "+what;
ls.lexerror(msg, LuaElementTypes.EMPTY_INPUT);
return false;
}
int indexupvalue(String name, ExpDesc v) {
/* new one */
if (!checklimit(f.numUpvalues + 1, LUAI_MAXUPVALUES, "upvalues"))
return -1;
int i;
for (i = 0; i < f.numUpvalues; i++) {
if (upvalues_k[i] == v.k && upvalues_info[i] == v.info) {
_assert(upvalues[i].equals(name));
return i;
}
}
if ( upvalues == null || f.numUpvalues + 1 > upvalues.length)
upvalues = realloc( upvalues, f.numUpvalues*2+1 );
upvalues[f.numUpvalues] = name;
_assert (v.k == KahluaParser.VLOCAL || v.k == KahluaParser.VUPVAL);
int numUpvalues = f.numUpvalues;
f.numUpvalues++;
upvalues_k[numUpvalues] = (v.k);
upvalues_info[numUpvalues] = (v.info);
return numUpvalues;
}
int searchvar(String n) {
int i;
for (i = nactvar - 1; i >= 0; i--) {
if (n != null && n.equals(getlocvar(i)))
return i;
}
return -1; /* not found */
}
void markupval(int level) {
BlockCnt bl = this.bl;
while (bl != null && bl.nactvar > level)
bl = bl.previous;
if (bl != null)
bl.upval = true;
}
int singlevaraux(String n, ExpDesc var, int base) {
int v = searchvar(n); /* look up at current level */
if (v >= 0) {
var.init(KahluaParser.VLOCAL, v);
if (base == 0)
markupval(v); /* local will be used as an upval */
return KahluaParser.VLOCAL;
} else { /* not found at current level; try upper one */
if (prev == null) { /* no more levels? */
/* default is global variable */
var.init(KahluaParser.VGLOBAL, NO_REG);
return KahluaParser.VGLOBAL;
}
if (prev.singlevaraux(n, var, 0) == KahluaParser.VGLOBAL)
return KahluaParser.VGLOBAL;
var.info = indexupvalue(n, var); /* else was LOCAL or UPVAL */
var.k = KahluaParser.VUPVAL; /* upvalue in this level */
return KahluaParser.VUPVAL;
}
}
void enterblock (BlockCnt bl, boolean isbreakable) {
bl.breaklist = KahluaParser.NO_JUMP;
bl.isbreakable = isbreakable;
bl.nactvar = this.nactvar;
bl.upval = false;
bl.previous = this.bl;
this.bl = bl;
_assert(this.freereg == this.nactvar);
}
//
// void leaveblock (FuncState *fs) {
// BlockCnt *bl = this.bl;
// this.bl = bl.previous;
// removevars(this.ls, bl.nactvar);
// if (bl.upval)
// this.codeABC(OP_CLOSE, bl.nactvar, 0, 0);
// /* a block either controls scope or breaks (never both) */
// assert(!bl.isbreakable || !bl.upval);
// assert(bl.nactvar == this.nactvar);
// this.freereg = this.nactvar; /* free registers */
// this.patchtohere(bl.breaklist);
// }
void leaveblock() {
BlockCnt bl = this.bl;
this.bl = bl.previous;
ls.removevars(bl.nactvar);
if (bl.upval)
this.codeABC(OP_CLOSE, bl.nactvar, 0, 0);
/* a block either controls scope or breaks (never both) */
_assert (!bl.isbreakable || !bl.upval);
_assert (bl.nactvar == this.nactvar);
this.freereg = this.nactvar; /* free registers */
this.patchtohere(bl.breaklist);
}
void closelistfield(ConsControl cc) {
if (cc.v.k == KahluaParser.VVOID)
return; /* there is no list item */
this.exp2nextreg(cc.v);
cc.v.k = KahluaParser.VVOID;
if (cc.tostore == LFIELDS_PER_FLUSH) {
this.setlist(cc.t.info, cc.na, cc.tostore); /* flush */
cc.tostore = 0; /* no more items pending */
}
}
boolean hasmultret(int k) {
return ((k) == KahluaParser.VCALL || (k) == KahluaParser.VVARARG);
}
void lastlistfield (ConsControl cc) {
if (cc.tostore == 0) return;
if (hasmultret(cc.v.k)) {
this.setmultret(cc.v);
this.setlist(cc.t.info, cc.na, LUA_MULTRET);
cc.na--; /** do not count last expression (unknown number of elements) */
}
else {
if (cc.v.k != KahluaParser.VVOID)
this.exp2nextreg(cc.v);
this.setlist(cc.t.info, cc.na, cc.tostore);
}
}
// =============================================================
// from lcode.c
// =============================================================
void nil(int from, int n) {
InstructionPtr previous;
if (this.pc > this.lasttarget) { /* no jumps to current position? */
if (this.pc == 0) { /* function start? */
if (from >= this.nactvar)
return; /* positions are already clean */
} else {
previous = new InstructionPtr(this.f.code, this.pc - 1);
if (GET_OPCODE(previous.get()) == OP_LOADNIL) {
int pfrom = GETARG_A(previous.get());
int pto = GETARG_B(previous.get());
if (pfrom <= from && from <= pto + 1) { /* can connect both? */
if (from + n - 1 > pto)
SETARG_B(previous, from + n - 1);
return;
}
}
}
}
/* else no optimization */
this.codeABC(OP_LOADNIL, from, from + n - 1, 0);
}
int jump() {
int jpc = this.jpc; /* save list of jumps to here */
this.jpc = KahluaParser.NO_JUMP;
int j = this.codeAsBx(OP_JMP, 0, KahluaParser.NO_JUMP);
j = this.concat(j, jpc); /* keep them on hold */
return j;
}
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 != KahluaParser.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) {
try {
int offset = GETARG_sBx(this.f.code[pc]);
/* point to itself represents end of list */
if (offset == KahluaParser.NO_JUMP)
/* end of list */
return KahluaParser.NO_JUMP;
else
/* turn offset into absolute position */
return (pc + 1) + offset;
} catch (Exception e) {
return KahluaParser.NO_JUMP;
}
}
InstructionPtr getjumpcontrol(int pc) {
InstructionPtr pi = new InstructionPtr(this.f.code, pc);
// if (pi.code.length == pi.idx)
// log.warn("Jump control will attempt out of bounds index");
if (pc >= 1 && pi.code.length < pi.idx && 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 != KahluaParser.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, GETARG_C(i.get())));
return true;
}
void removevalues(int list) {
for (; list != KahluaParser.NO_JUMP; list = this.getjump(list))
this.patchtestreg(list, NO_REG);
}
void patchlistaux(int list, int vtarget, int reg, int dtarget) {
while (list != KahluaParser.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, this.pc, NO_REG, this.pc);
this.jpc = KahluaParser.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 patchtohere(int list) {
this.getlabel();
this.jpc = this.concat(this.jpc, list);
}
int concat(int l1, int l2) {
if (l2 == KahluaParser.NO_JUMP)
return l1;
if (l1 == KahluaParser.NO_JUMP)
l1 = l2;
else {
int list = l1;
int next;
while ((next = this.getjump(list)) != KahluaParser.NO_JUMP)
/* find last element */
list = next;
this.fixjump(list, l2);
}
return l1;
}
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 == KahluaParser.VNONRELOC)
this.freereg(e.info);
}
int addk(Object v) {
int idx;
if (v == null) return 0;
if (this.htable.containsKey(v)) {
idx = ((Integer) htable.get(v)).intValue();
} else {
idx = this.nk;
this.htable.put(v, new Integer(idx));
final Prototype f = this.f;
if (f.constants == null || nk + 1 >= f.constants.length)
f.constants = realloc( f.constants, nk*2 + 1 );
if (v == NULL_OBJECT) {
v = null;
}
f.constants[this.nk++] = v;
}
return idx;
}
int stringK(String s) {
return this.addk(s);
}
int numberK(double r) {
return this.addk(new Double(r));
}
int boolK(boolean b) {
return this.addk((b ? Boolean.TRUE : Boolean.FALSE));
}
int nilK() {
return this.addk(NULL_OBJECT);
}
void setreturns(ExpDesc e, int nresults) {
if (e.k == KahluaParser.VCALL) { /* expression is an open function call? */
SETARG_C(this.getcodePtr(e), nresults + 1);
} else if (e.k == KahluaParser.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 == KahluaParser.VCALL) { /* expression is an open function call? */
e.k = KahluaParser.VNONRELOC;
e.info = GETARG_A(this.getcode(e));
} else if (e.k == KahluaParser.VVARARG) {
SETARG_B(this.getcodePtr(e), 2);
e.k = KahluaParser.VRELOCABLE; /* can relocate its simple result */
}
}
void dischargevars(ExpDesc e) {
switch (e.k) {
case KahluaParser.VLOCAL: {
e.k = KahluaParser.VNONRELOC;
break;
}
case KahluaParser.VUPVAL: {
e.info = this.codeABC(OP_GETUPVAL, 0, e.info, 0);
e.k = KahluaParser.VRELOCABLE;
break;
}
case KahluaParser.VGLOBAL: {
e.info = this.codeABx(OP_GETGLOBAL, 0, e.info);
e.k = KahluaParser.VRELOCABLE;
break;
}
case KahluaParser.VINDEXED: {
this.freereg(e.aux);
this.freereg(e.info);
e.info = this
.codeABC(OP_GETTABLE, 0, e.info, e.aux);
e.k = KahluaParser.VRELOCABLE;
break;
}
case KahluaParser.VVARARG:
case KahluaParser.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 KahluaParser.VNIL: {
this.nil(reg, 1);
break;
}
case KahluaParser.VFALSE:
case KahluaParser.VTRUE: {
this.codeABC(OP_LOADBOOL, reg, (e.k == KahluaParser.VTRUE ? 1 : 0),
0);
break;
}
case KahluaParser.VK: {
this.codeABx(OP_LOADK, reg, e.info);
break;
}
case KahluaParser.VKNUM: {
this.codeABx(OP_LOADK, reg, this.numberK(e.nval()));
break;
}
case KahluaParser.VRELOCABLE: {
InstructionPtr pc = this.getcodePtr(e);
SETARG_A(pc, reg);
break;
}
case KahluaParser.VNONRELOC: {
if (reg != e.info)
this.codeABC(OP_MOVE, reg, e.info, 0);
break;
}
default: {
_assert (e.k == KahluaParser.VVOID || e.k == KahluaParser.VJMP);
return; /* nothing to do... */
}
}
e.info = reg;
e.k = KahluaParser.VNONRELOC;
}
void discharge2anyreg(ExpDesc e) {
if (e.k != KahluaParser.VNONRELOC) {
this.reserveregs(1);
this.discharge2reg(e, this.freereg - 1);
}
}
void exp2reg(ExpDesc e, int reg) {
this.discharge2reg(e, reg);
if (e.k == KahluaParser.VJMP)
e.t = this.concat(e.t, e.info); /* put this jump in `t' list */
if (e.hasjumps()) {
int _final; /* position after whole expression */
int p_f = KahluaParser.NO_JUMP; /* position of an eventual LOAD false */
int p_t = KahluaParser.NO_JUMP; /* position of an eventual LOAD true */
if (this.need_value(e.t) || this.need_value(e.f)) {
int fj = (e.k == KahluaParser.VJMP) ? KahluaParser.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, _final, reg, p_f);
this.patchlistaux(e.t, _final, reg, p_t);
}
e.f = e.t = KahluaParser.NO_JUMP;
e.info = reg;
e.k = KahluaParser.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 == KahluaParser.VNONRELOC) {
if (!e.hasjumps())
return e.info; /* exp is already in a register */
if (e.info >= this.nactvar) { /* reg. is not a local? */
this.exp2reg(e, e.info); /* put value on it */
return e.info;
}
}
this.exp2nextreg(e); /* default */
return e.info;
}
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 KahluaParser.VKNUM:
case KahluaParser.VTRUE:
case KahluaParser.VFALSE:
case KahluaParser.VNIL: {
if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */
e.info = (e.k == KahluaParser.VNIL) ? this.nilK()
: (e.k == KahluaParser.VKNUM) ? this.numberK(e.nval())
: this.boolK((e.k == KahluaParser.VTRUE));
e.k = KahluaParser.VK;
return RKASK(e.info);
} else
break;
}
case KahluaParser.VK: {
if (e.info <= MAXINDEXRK) /* constant fit in argC? */
return RKASK(e.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 KahluaParser.VLOCAL: {
this.freeexp(ex);
this.exp2reg(ex, var.info);
return;
}
case KahluaParser.VUPVAL: {
int e = this.exp2anyreg(ex);
this.codeABC(OP_SETUPVAL, e, var.info, 0);
break;
}
case KahluaParser.VGLOBAL: {
int e = this.exp2anyreg(ex);
this.codeABx(OP_SETGLOBAL, e, var.info);
break;
}
case KahluaParser.VINDEXED: {
int e = this.exp2RK(ex);
this.codeABC(OP_SETTABLE, var.info, var.aux, 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.info, this.exp2RK(key));
this.freeexp(key);
e.info = func;
e.k = KahluaParser.VNONRELOC;
}
void invertjump(ExpDesc e) {
InstructionPtr pc = this.getjumpcontrol(e.info);
_assert (testTMode(GET_OPCODE(pc.get()))
&& GET_OPCODE(pc.get()) != OP_TESTSET && 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 == KahluaParser.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.info, cond);
}
void goiftrue(ExpDesc e) {
int pc; /* pc of last jump */
this.dischargevars(e);
switch (e.k) {
case KahluaParser.VK:
case KahluaParser.VKNUM:
case KahluaParser.VTRUE: {
pc = KahluaParser.NO_JUMP; /* always true; do nothing */
break;
}
case KahluaParser.VFALSE: {
pc = this.jump(); /* always jump */
break;
}
case KahluaParser.VJMP: {
this.invertjump(e);
pc = e.info;
break;
}
default: {
pc = this.jumponcond(e, 0);
break;
}
}
e.f = this.concat(e.f, pc); /* insert last jump in `f' list */
this.patchtohere(e.t);
e.t = KahluaParser.NO_JUMP;
}
void goiffalse(ExpDesc e) {
int pc; /* pc of last jump */
this.dischargevars(e);
switch (e.k) {
case KahluaParser.VNIL:
case KahluaParser.VFALSE: {
pc = KahluaParser.NO_JUMP; /* always false; do nothing */
break;
}
case KahluaParser.VTRUE: {
pc = this.jump(); /* always jump */
break;
}
case KahluaParser.VJMP: {
pc = e.info;
break;
}
default: {
pc = this.jumponcond(e, 1);
break;
}
}
e.t = this.concat(e.t, pc); /* insert last jump in `t' list */
this.patchtohere(e.f);
e.f = KahluaParser.NO_JUMP;
}
void codenot(ExpDesc e) {
this.dischargevars(e);
switch (e.k) {
case KahluaParser.VNIL:
case KahluaParser.VFALSE: {
e.k = KahluaParser.VTRUE;
break;
}
case KahluaParser.VK:
case KahluaParser.VKNUM:
case KahluaParser.VTRUE: {
e.k = KahluaParser.VFALSE;
break;
}
case KahluaParser.VJMP: {
this.invertjump(e);
break;
}
case KahluaParser.VRELOCABLE:
case KahluaParser.VNONRELOC: {
this.discharge2anyreg(e);
this.freeexp(e);
e.info = this.codeABC(OP_NOT, 0, e.info, 0);
e.k = KahluaParser.VRELOCABLE;
break;
}
default: {
_assert (false); /* cannot happen */
break;
}
}
/* interchange true and false lists */
{
int temp = e.f;
e.f = e.t;
e.t = temp;
}
this.removevalues(e.f);
this.removevalues(e.t);
}
void indexed(ExpDesc t, ExpDesc k) {
t.aux = this.exp2RK(k);
t.k = KahluaParser.VINDEXED;
}
boolean constfolding(int op, ExpDesc e1, ExpDesc e2) {
if (!e1.isnumeral() || !e2.isnumeral())
return false;
double v1, v2, r;
v1 = e1.nval();
v2 = e2.nval();
switch (op) {
case OP_ADD:
r = v1 + v2;
break;
case OP_SUB:
r = v1 - v2;
break;
case OP_MUL:
r = v1 * v2;
break;
case OP_DIV:
r = v1 / v2;
break;
case OP_MOD:
r = v1 % v2;
break;
case OP_POW:
return false;
case OP_UNM:
r = -v1;
break;
case OP_LEN:
return false; /* no constant folding for 'len' */
default:
_assert (false);
return false;
}
if (Double.isNaN(r) || Double.isInfinite(r))
return false; /* do not attempt to produce NaN */
e1.setNval( r);
return true;
}
void codearith(int op, ExpDesc e1, ExpDesc e2) {
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.info = this.codeABC(op, 0, o1, o2);
e1.k = KahluaParser.VRELOCABLE;
}
}
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.info = this.condjump(op, cond, o1, o2);
e1.k = KahluaParser.VJMP;
}
void prefix(int /* UnOpr */op, ExpDesc e) {
ExpDesc e2 = new ExpDesc();
e2.init(KahluaParser.VKNUM, 0);
switch (op) {
case KahluaParser.OPR_MINUS: {
if (e.k == KahluaParser.VK)
this.exp2anyreg(e); /* cannot operate on non-numeric constants */
this.codearith(OP_UNM, e, e2);
break;
}
case KahluaParser.OPR_NOT:
this.codenot(e);
break;
case KahluaParser.OPR_LEN: {
this.exp2anyreg(e); /* cannot operate on constants */
this.codearith(OP_LEN, e, e2);
break;
}
default:
_assert (false);
}
}
void infix(int /* BinOpr */op, ExpDesc v) {
switch (op) {
case KahluaParser.OPR_AND: {
this.goiftrue(v);
break;
}
case KahluaParser.OPR_OR: {
this.goiffalse(v);
break;
}
case KahluaParser.OPR_CONCAT: {
this.exp2nextreg(v); /* operand must be on the `stack' */
break;
}
case KahluaParser.OPR_ADD:
case KahluaParser.OPR_SUB:
case KahluaParser.OPR_MUL:
case KahluaParser.OPR_DIV:
case KahluaParser.OPR_MOD:
case KahluaParser.OPR_POW: {
if (!v.isnumeral())
this.exp2RK(v);
break;
}
default: {
this.exp2RK(v);
break;
}
}
}
void posfix(int op, ExpDesc e1, ExpDesc e2) {
switch (op) {
case KahluaParser.OPR_AND: {
_assert (e1.t == KahluaParser.NO_JUMP); /* list must be closed */
this.dischargevars(e2);
e2.f = this.concat(e2.f, e1.f);
// *e1 = *e2;
e1.setvalue(e2);
break;
}
case KahluaParser.OPR_OR: {
_assert (e1.f == KahluaParser.NO_JUMP); /* list must be closed */
this.dischargevars(e2);
e2.t = this.concat(e2.t, e1.t);
// *e1 = *e2;
e1.setvalue(e2);
break;
}
case KahluaParser.OPR_CONCAT: {
this.exp2val(e2);
if (e2.k == KahluaParser.VRELOCABLE
&& GET_OPCODE(this.getcode(e2)) == OP_CONCAT) {
_assert (e1.info == GETARG_B(this.getcode(e2)) - 1);
this.freeexp(e1);
SETARG_B(this.getcodePtr(e2), e1.info);
e1.k = KahluaParser.VRELOCABLE;
e1.info = e2.info;
} else {
this.exp2nextreg(e2); /* operand must be on the 'stack' */
this.codearith(OP_CONCAT, e1, e2);
}
break;
}
case KahluaParser.OPR_ADD:
this.codearith(OP_ADD, e1, e2);
break;
case KahluaParser.OPR_SUB:
this.codearith(OP_SUB, e1, e2);
break;
case KahluaParser.OPR_MUL:
this.codearith(OP_MUL, e1, e2);
break;
case KahluaParser.OPR_DIV:
this.codearith(OP_DIV, e1, e2);
break;
case KahluaParser.OPR_MOD:
this.codearith(OP_MOD, e1, e2);
break;
case KahluaParser.OPR_POW:
this.codearith(OP_POW, e1, e2);
break;
case KahluaParser.OPR_EQ:
this.codecomp(OP_EQ, 1, e1, e2);
break;
case KahluaParser.OPR_NE:
this.codecomp(OP_EQ, 0, e1, e2);
break;
case KahluaParser.OPR_LT:
this.codecomp(OP_LT, 1, e1, e2);
break;
case KahluaParser.OPR_LE:
this.codecomp(OP_LE, 1, e1, e2);
break;
case KahluaParser.OPR_GT:
this.codecomp(OP_LT, 0, e1, e2);
break;
case KahluaParser.OPR_GE:
this.codecomp(OP_LE, 0, e1, e2);
break;
default:
_assert (false);
}
}
void fixline(int line) {
this.f.lines[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 = realloc(f.code, this.pc * 2 + 1);
f.code[this.pc] = instruction;
/* save corresponding line information */
if (f.lines == null || this.pc + 1 > f.lines.length)
f.lines = realloc(f.lines,
this.pc * 2 + 1);
f.lines[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);
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 = base + 1; /* free registers with list values */
}
protected static void _assert(boolean b) {
// if (!b)
// throw new KahluaException("compiler assert failed");
}
public static final int MAXSTACK = 250;
static final int LUAI_MAXUPVALUES = 60;
static final int LUAI_MAXVARS = 200;
/* OpArgMask */
static final int
OpArgN = 0, /* argument is not used */
OpArgU = 1, /* argument is used */
OpArgR = 2, /* argument is a register or a jump offset */
OpArgK = 3; /* argument is a constant or register/constant */
static void SET_OPCODE(InstructionPtr i,int o) {
i.set( ( i.get() & (MASK_NOT_OP)) | ((o << POS_OP) & MASK_OP) );
}
static void SETARG_A(InstructionPtr i,int u) {
i.set( ( i.get() & (MASK_NOT_A)) | ((u << POS_A) & MASK_A) );
}
static void SETARG_B(InstructionPtr i,int u) {
i.set( ( i.get() & (MASK_NOT_B)) | ((u << POS_B) & MASK_B) );
}
static void SETARG_C(InstructionPtr i,int u) {
i.set( ( i.get() & (MASK_NOT_C)) | ((u << POS_C) & MASK_C) );
}
static void SETARG_Bx(InstructionPtr i,int u) {
i.set( ( i.get() & (MASK_NOT_Bx)) | ((u << POS_Bx) & MASK_Bx) );
}
static void SETARG_sBx(InstructionPtr i,int u) {
SETARG_Bx( i, u + MAXARG_sBx );
}
static int CREATE_ABC(int o, int a, int b, int c) {
return ((o << POS_OP) & MASK_OP) |
((a << POS_A) & MASK_A) |
((b << POS_B) & MASK_B) |
((c << POS_C) & MASK_C) ;
}
static int CREATE_ABx(int o, int a, int bc) {
return ((o << POS_OP) & MASK_OP) |
((a << POS_A) & MASK_A) |
((bc << POS_Bx) & MASK_Bx) ;
}
// vector reallocation
static Object[] realloc(Object[] v, int n) {
Object[] a = new Object[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
return a;
}
static String[] realloc(String[] v, int n) {
String[] a = new String[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
return a;
}
static Prototype[] realloc(Prototype[] v, int n) {
Prototype[] a = new Prototype[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
return a;
}
static int[] realloc(int[] v, int n) {
int[] a = new int[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
return a;
}
static byte[] realloc(byte[] v, int n) {
byte[] a = new byte[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
return a;
}
/** use return values from previous op */
public static final int LUA_MULTRET = -1;
/** masks for new-style vararg */
public static final int VARARG_HASARG = 1;
public static final int VARARG_ISVARARG = 2;
public static final int VARARG_NEEDSARG = 4;
// from lopcodes.h
/*===========================================================================
We assume that instructions are unsigned numbers.
All instructions have an opcode in the first 6 bits.
Instructions can have the following fields:
`A' : 8 bits
`B' : 9 bits
`C' : 9 bits
`Bx' : 18 bits (`B' and `C' together)
`sBx' : signed Bx
A signed argument is represented in excess K; that is, the number
value is the unsigned value minus K. K is exactly the maximum value
for that argument (so that -max is represented by 0, and +max is
represented by 2*max), which is half the maximum for the corresponding
unsigned argument.
===========================================================================*/
/* basic instruction format */
public static final int iABC = 0;
public static final int iABx = 1;
public static final int iAsBx = 2;
/*
** size and position of opcode arguments.
*/
public static final int SIZE_C = 9;
public static final int SIZE_B = 9;
public static final int SIZE_Bx = (SIZE_C + SIZE_B);
public static final int SIZE_A = 8;
public static final int SIZE_OP = 6;
public static final int POS_OP = 0;
public static final int POS_A = (POS_OP + SIZE_OP);
public static final int POS_C = (POS_A + SIZE_A);
public static final int POS_B = (POS_C + SIZE_C);
public static final int POS_Bx = POS_C;
public static final int MAX_OP = ((1<<SIZE_OP)-1);
public static final int MAXARG_A = ((1<<SIZE_A)-1);
public static final int MAXARG_B = ((1<<SIZE_B)-1);
public static final int MAXARG_C = ((1<<SIZE_C)-1);
public static final int MAXARG_Bx = ((1<<SIZE_Bx)-1);
public static final int MAXARG_sBx = (MAXARG_Bx>>1); /* `sBx' is signed */
public static final int MASK_OP = ((1<<SIZE_OP)-1)<<POS_OP;
public static final int MASK_A = ((1<<SIZE_A)-1)<<POS_A;
public static final int MASK_B = ((1<<SIZE_B)-1)<<POS_B;
public static final int MASK_C = ((1<<SIZE_C)-1)<<POS_C;
public static final int MASK_Bx = ((1<<SIZE_Bx)-1)<<POS_Bx;
public static final int MASK_NOT_OP = ~MASK_OP;
public static final int MASK_NOT_A = ~MASK_A;
public static final int MASK_NOT_B = ~MASK_B;
public static final int MASK_NOT_C = ~MASK_C;
public static final int MASK_NOT_Bx = ~MASK_Bx;
/*
** the following macros help to manipulate instructions
*/
public static int GET_OPCODE(int i) {
return (i >> POS_OP) & MAX_OP;
}
public static int GETARG_A(int i) {
return (i >> POS_A) & MAXARG_A;
}
public static int GETARG_B(int i) {
return (i >> POS_B) & MAXARG_B;
}
public static int GETARG_C(int i) {
return (i >> POS_C) & MAXARG_C;
}
public static int GETARG_Bx(int i) {
return (i >> POS_Bx) & MAXARG_Bx;
}
public static int GETARG_sBx(int i) {
return ((i >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx;
}
/*
** Macros to operate RK indices
*/
/** this bit 1 means constant (0 means register) */
public static final int BITRK = (1 << (SIZE_B - 1));
/** test whether value is a constant */
public static boolean ISK(int x) {
return 0 != ((x) & BITRK);
}
/** gets the index of the constant */
public static int INDEXK(int r) {
return ((int)(r) & ~BITRK);
}
public static final int MAXINDEXRK = (BITRK - 1);
/** code a constant index as a RK value */
public static int RKASK(int x) {
return ((x) | BITRK);
}
/**
** invalid register that fits in 8 bits
*/
public static final int NO_REG = MAXARG_A;
/*
** R(x) - register
** Kst(x) - constant (in constant table)
** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
*/
/*
** grep "ORDER OP" if you change these enums
*/
/*----------------------------------------------------------------------
name args description
------------------------------------------------------------------------*/
public static final int OP_MOVE = 0;/* A B R(A) := R(B) */
public static final int OP_LOADK = 1;/* A Bx R(A) := Kst(Bx) */
public static final int OP_LOADBOOL = 2;/* A B C R(A) := (Bool)B; if (C) pc++ */
public static final int OP_LOADNIL = 3; /* A B R(A) := ... := R(B) := nil */
public static final int OP_GETUPVAL = 4; /* A B R(A) := UpValue[B] */
public static final int OP_GETGLOBAL = 5; /* A Bx R(A) := Gbl[Kst(Bx)] */
public static final int OP_GETTABLE = 6; /* A B C R(A) := R(B)[RK(C)] */
public static final int OP_SETGLOBAL = 7; /* A Bx Gbl[Kst(Bx)] := R(A) */
public static final int OP_SETUPVAL = 8; /* A B UpValue[B] := R(A) */
public static final int OP_SETTABLE = 9; /* A B C R(A)[RK(B)] := RK(C) */
public static final int OP_NEWTABLE = 10; /* A B C R(A) := {} (size = B,C) */
public static final int OP_SELF = 11; /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
public static final int OP_ADD = 12; /* A B C R(A) := RK(B) + RK(C) */
public static final int OP_SUB = 13; /* A B C R(A) := RK(B) - RK(C) */
public static final int OP_MUL = 14; /* A B C R(A) := RK(B) * RK(C) */
public static final int OP_DIV = 15; /* A B C R(A) := RK(B) / RK(C) */
public static final int OP_MOD = 16; /* A B C R(A) := RK(B) % RK(C) */
public static final int OP_POW = 17; /* A B C R(A) := RK(B) ^ RK(C) */
public static final int OP_UNM = 18; /* A B R(A) := -R(B) */
public static final int OP_NOT = 19; /* A B R(A) := not R(B) */
public static final int OP_LEN = 20; /* A B R(A) := length of R(B) */
public static final int OP_CONCAT = 21; /* A B C R(A) := R(B).. ... ..R(C) */
public static final int OP_JMP = 22; /* sBx pc+=sBx */
public static final int OP_EQ = 23; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
public static final int OP_LT = 24; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
public static final int OP_LE = 25; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
public static final int OP_TEST = 26; /* A C if not (R(A) <=> C) then pc++ */
public static final int OP_TESTSET = 27; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
public static final int OP_CALL = 28; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
public static final int OP_TAILCALL = 29; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
public static final int OP_RETURN = 30; /* A B return R(A), ... ,R(A+B-2) (see note) */
public static final int OP_FORLOOP = 31; /* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
public static final int OP_FORPREP = 32; /* A sBx R(A)-=R(A+2); pc+=sBx */
public static final int OP_TFORLOOP = 33; /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
public static final int OP_SETLIST = 34; /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
public static final int OP_CLOSE = 35; /* A close all variables in the stack up to (>=) R(A)*/
public static final int OP_CLOSURE = 36; /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
public static final int OP_VARARG = 37; /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
public static final int NUM_OPCODES = OP_VARARG + 1;
/*===========================================================================
Notes:
(*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
and can be 0: OP_CALL then sets `top' to last_result+1, so
next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
(*) In OP_VARARG, if (B == 0) then use actual number of varargs and
set top (like in OP_CALL with C == 0).
(*) In OP_RETURN, if (B == 0) then return up to `top'
(*) In OP_SETLIST, if (B == 0) then B = `top';
if (C == 0) then next `instruction' is real C
(*) For comparisons, A specifies what condition the test should accept
(true or false).
(*) All `skips' (pc++) assume that next instruction is a jump
===========================================================================*/
/*
** masks for instruction properties. The format is:
** bits 0-1: op mode
** bits 2-3: C arg mode
** bits 4-5: B arg mode
** bit 6: instruction set register A
** bit 7: operator is a test
*/
public static final int[] luaP_opmodes = {
/* T A B C mode opcode */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_MOVE */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_LOADK */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_LOADBOOL */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LOADNIL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_GETUPVAL */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_GETGLOBAL */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABLE */
(0<<7) | (0<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_SETGLOBAL */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_SETUPVAL */
(0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABLE */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_NEWTABLE */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_SELF */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_ADD */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SUB */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MUL */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_DIV */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MOD */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_POW */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_UNM */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_NOT */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LEN */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgR<<2) | (iABC), /* OP_CONCAT */
(0<<7) | (0<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_JMP */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_EQ */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LT */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LE */
(1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TEST */
(1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TESTSET */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_CALL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_TAILCALL */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_RETURN */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORLOOP */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORPREP */
(1<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TFORLOOP */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_SETLIST */
(0<<7) | (0<<6) | (OpArgN<<4) | (OpArgN<<2) | (iABC), /* OP_CLOSE */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABx), /* OP_CLOSURE */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_VARARG */
};
public static int getOpMode(int m) {
return luaP_opmodes[m] & 3;
}
public static int getBMode(int m) {
return (luaP_opmodes[m] >> 4) & 3;
}
public static int getCMode(int m) {
return (luaP_opmodes[m] >> 2) & 3;
}
public static boolean testTMode(int m) {
return 0 != (luaP_opmodes[m] & (1 << 7));
}
/* number of list items to accumulate before a SETLIST instruction */
public static final int LFIELDS_PER_FLUSH = 50;
}