diff --git a/src/core/org/luaj/vm2/LoadState.java b/src/core/org/luaj/vm2/LoadState.java index 9528e315..42aa41cc 100644 --- a/src/core/org/luaj/vm2/LoadState.java +++ b/src/core/org/luaj/vm2/LoadState.java @@ -141,6 +141,7 @@ public class LoadState { private static final Prototype[] NOPROTOS = {}; private static final LocVars[] NOLOCVARS = {}; private static final LuaString[] NOSTRVALUES = {}; + private static final Upvaldesc[] NOUPVALDESCS = {}; private static final int[] NOINTS = {}; /** Read buffer */ @@ -282,12 +283,24 @@ public class LoadState { f.p = protos; } + + void loadUpvalues(Prototype f) throws IOException { + int n = loadInt(); + f.upvalues = n>0? new Upvaldesc[n]: NOUPVALDESCS; + for (int i=0; i0? new LocVars[n]: NOLOCVARS; @@ -299,10 +312,9 @@ public class LoadState { } n = loadInt(); - f.upvalues = n>0? new LuaString[n]: NOSTRVALUES; - for ( int i=0; i0? new Upvaldesc[n]: NOUPVALDESCS; + for ( int i=0; i>1); /* `sBx' is signed */ + public static final int MAXARG_Ax = ((1<> POS_A) & MAXARG_A; } + public static int GETARG_Ax(int i) { + return (i >> POS_Ax) & MAXARG_Ax; + } + public static int GETARG_B(int i) { return (i >> POS_B) & MAXARG_B; } @@ -177,59 +185,62 @@ public class Lua { ------------------------------------------------------------------------*/ 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_LOADKX = 2;/* A R(A) := Kst(extra arg) */ + public static final int OP_LOADBOOL = 3;/* A B C R(A) := (Bool)B; if (C) pc++ */ + public static final int OP_LOADNIL = 4; /* A B R(A) := ... := R(B) := nil */ + public static final int OP_GETUPVAL = 5; /* 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_GETTABUP = 6; /* A B C R(A) := UpValue[B][RK(C)] */ + public static final int OP_GETTABLE = 7; /* 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_SETTABUP = 8; /* A B C UpValue[A][RK(B)] := RK(C) */ + public static final int OP_SETUPVAL = 9; /* A B UpValue[B] := R(A) */ + public static final int OP_SETTABLE = 10; /* 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_NEWTABLE = 11; /* 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_SELF = 12; /* 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_ADD = 13; /* A B C R(A) := RK(B) + RK(C) */ + public static final int OP_SUB = 14; /* A B C R(A) := RK(B) - RK(C) */ + public static final int OP_MUL = 15; /* A B C R(A) := RK(B) * RK(C) */ + public static final int OP_DIV = 16; /* A B C R(A) := RK(B) / RK(C) */ + public static final int OP_MOD = 17; /* A B C R(A) := RK(B) % RK(C) */ + public static final int OP_POW = 18; /* A B C R(A) := RK(B) ^ RK(C) */ + public static final int OP_UNM = 19; /* A B R(A) := -R(B) */ + public static final int OP_NOT = 20; /* A B R(A) := not R(B) */ + public static final int OP_LEN = 21; /* 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_CONCAT = 22; /* A B C R(A) := R(B).. ... ..R(C) */ - public static final int OP_JMP = 22; /* sBx pc+=sBx */ + public static final int OP_JMP = 23; /* sBx pc+=sBx */ + public static final int OP_EQ = 24; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ + public static final int OP_LT = 25; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ + public static final int OP_LE = 26; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ - 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 = 27; /* A C if not (R(A) <=> C) then pc++ */ + public static final int OP_TESTSET = 28; /* A B C if (R(B) <=> C) then R(A) := R(B) else 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 = 29; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_TAILCALL = 30; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_RETURN = 31; /* A B return R(A), ... ,R(A+B-2) (see note) */ - 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); + public static final int OP_FORLOOP = 32; /* A sBx R(A)+=R(A+2); if R(A) =) 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; + public static final int OP_CLOSURE = 37; /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ + + public static final int OP_VARARG = 38; /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ + + public static final int OP_EXTRAARG = 39; /* Ax extra (larger) argument for previous opcode */ + + public static final int NUM_OPCODES = OP_EXTRAARG + 1; /* pseudo-opcodes used in parsing only. */ public static final int OP_GT = 63; // > @@ -277,12 +288,13 @@ public class Lua { /* 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) | (OpArgN<<4) | (OpArgN<<2) | (iABx), /* OP_LOADKX */ (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_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) | (OpArgU<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABUP */ (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) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABUP */ (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 */ @@ -301,18 +313,19 @@ public class Lua { (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) | (0<<6) | (OpArgN<<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) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TFORCALL */ + (1<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* 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 */ + (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iAx), /* OP_EXTRAARG */ }; public static int getOpMode(int m) { diff --git a/src/core/org/luaj/vm2/LuaClosure.java b/src/core/org/luaj/vm2/LuaClosure.java index 8deb56b4..3701be87 100644 --- a/src/core/org/luaj/vm2/LuaClosure.java +++ b/src/core/org/luaj/vm2/LuaClosure.java @@ -21,9 +21,6 @@ ******************************************************************************/ package org.luaj.vm2; -import java.io.ByteArrayInputStream; -import java.io.InputStream; - import org.luaj.vm2.LoadState.LuaCompiler; import org.luaj.vm2.compiler.LuaC; import org.luaj.vm2.lib.DebugLib; @@ -101,15 +98,17 @@ public class LuaClosure extends LuaFunction { } /** Supply the initial environment */ public LuaClosure(Prototype p, LuaValue env) { - super( env ); - this.p = p; - this.upValues = p.nups>0? new UpValue[p.nups]: NOUPVALUES; + this(p, p.upvalues.length, env); } - protected LuaClosure(int nupvalues, LuaValue env) { + protected LuaClosure(Prototype p, int nupvalues, LuaValue env) { super( env ); - this.p = null; - this.upValues = nupvalues>0? new UpValue[nupvalues]: NOUPVALUES; + this.p = p; + switch (nupvalues) { + case 0: this.upValues = NOUPVALUES; break; + case 1: this.upValues = new UpValue[] { new UpValue(new LuaValue[1], 0) }; this.upValues[0].setValue(env); break; + default: this.upValues = new UpValue[nupvalues]; break; + } } public boolean isclosure() { @@ -233,16 +232,16 @@ public class LuaClosure extends LuaFunction { stack[a] = upValues[i>>>23].getValue(); continue; - case Lua.OP_GETGLOBAL: /* A Bx R(A):= Gbl[Kst(Bx)] */ - stack[a] = env.get(k[i>>>14]); + case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */ + stack[a] = upValues[i>>>23].getValue().get((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); continue; - + case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */ stack[a] = stack[i>>>23].get((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); continue; - case Lua.OP_SETGLOBAL: /* A Bx Gbl[Kst(Bx)]:= R(A) */ - env.set(k[i>>>14], stack[a]); + case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */ + upValues[a].getValue().set(((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]), (c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); continue; case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */ @@ -422,7 +421,11 @@ public class LuaClosure extends LuaFunction { pc += (i>>>14)-0x1ffff; } continue; - + + case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */ + v = stack[a].invoke(varargsOf(stack[a+1],stack[a+2])); + continue; + case Lua.OP_TFORLOOP: /* * 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) @@ -462,21 +465,12 @@ public class LuaClosure extends LuaFunction { } continue; - case Lua.OP_CLOSE: /* A close all variables in the stack up to (>=) R(A)*/ - for ( b=openups.length; --b>=a; ) - if ( openups[b]!=null ) { - openups[b].close(); - openups[b] = null; - } - continue; - case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ { Prototype newp = p.p[i>>>14]; LuaClosure newcl = new LuaClosure(newp, env); - for ( int j=0, nup=newp.nups; j>>23; newcl.upValues[j] = (i&4) != 0? upValues[b]: @@ -496,6 +490,12 @@ public class LuaClosure extends LuaFunction { stack[a+j-1] = varargs.arg(j); } continue; + + case Lua.OP_EXTRAARG: + throw new java.lang.IllegalArgumentException("Uexecutable opcode: OP_EXTRAARG"); + + default: + throw new java.lang.IllegalArgumentException("Illegal opcode: " + (i & 0x3f)); } } } catch ( LuaError le ) { diff --git a/src/core/org/luaj/vm2/LuaValue.java b/src/core/org/luaj/vm2/LuaValue.java index 76907b75..e88fb706 100644 --- a/src/core/org/luaj/vm2/LuaValue.java +++ b/src/core/org/luaj/vm2/LuaValue.java @@ -187,7 +187,9 @@ public class LuaValue extends Varargs { /** LuaValue array constant with no values */ public static final LuaValue[] NOVALS = {}; - + + /** The variable name of the environment. */ + public static LuaString ENV = valueOf("_ENV"); /** LuaString constant with value "__index" for use as metatag */ public static final LuaString INDEX = valueOf("__index"); diff --git a/src/core/org/luaj/vm2/Print.java b/src/core/org/luaj/vm2/Print.java index 72c2f8f9..f8d9f490 100644 --- a/src/core/org/luaj/vm2/Print.java +++ b/src/core/org/luaj/vm2/Print.java @@ -136,6 +136,11 @@ public class Print extends Lua { printValue( ps, f.k[i] ); } + static void printUpvalue(PrintStream ps, Upvaldesc u) { + ps.print( u.idx + " " ); + printValue( ps, u.name ); + } + /** * Print the code in a prototype * @param f the {@link Prototype} @@ -211,12 +216,12 @@ public class Print extends Lua { case OP_SETUPVAL: ps.print(" ; "); if ( f.upvalues.length > b ) - printValue(ps, f.upvalues[b]); + printUpvalue(ps, f.upvalues[b]); else ps.print( "-" ); break; - case OP_GETGLOBAL: - case OP_SETGLOBAL: + case OP_GETTABUP: + case OP_SETTABUP: ps.print(" ; "); printConstant( ps, f, bx ); break; diff --git a/src/core/org/luaj/vm2/Prototype.java b/src/core/org/luaj/vm2/Prototype.java index 2e150cd5..8112da3e 100644 --- a/src/core/org/luaj/vm2/Prototype.java +++ b/src/core/org/luaj/vm2/Prototype.java @@ -42,10 +42,9 @@ public class Prototype { public int[] lineinfo; /* information about local variables */ public LocVars[] locvars; - /* upvalue names */ - public LuaString[] upvalues; + /* upvalue information */ + public Upvaldesc[] upvalues; public LuaString source; - public int nups; public int linedefined; public int lastlinedefined; public int numparams; diff --git a/src/core/org/luaj/vm2/Upvaldesc.java b/src/core/org/luaj/vm2/Upvaldesc.java new file mode 100644 index 00000000..839ac4c6 --- /dev/null +++ b/src/core/org/luaj/vm2/Upvaldesc.java @@ -0,0 +1,40 @@ +/******************************************************************************* +* Copyright (c) 2012 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; + +public class Upvaldesc { + + /* upvalue name (for debug information) */ + public final LuaString name; + + /* whether it is in stack */ + public final boolean instack; + + /* index of upvalue (in stack or in outer function's list) */ + public final short idx; + + public Upvaldesc(LuaString name, boolean instack, int idx) { + this.name = name; + this.instack = instack; + this.idx = (short) idx; + } +} diff --git a/src/core/org/luaj/vm2/compiler/DumpState.java b/src/core/org/luaj/vm2/compiler/DumpState.java index f02a944c..9dc3091c 100644 --- a/src/core/org/luaj/vm2/compiler/DumpState.java +++ b/src/core/org/luaj/vm2/compiler/DumpState.java @@ -177,14 +177,24 @@ public class DumpState { for (i = 0; i < n; i++) dumpFunction(f.p[i], f.source); } - + + void dumpUpvalues(final Prototype f) throws IOException { + int n = f.upvalues.length; + dumpInt(n); + for (int i = 0; i < n; i++) { + writer.writeByte(f.upvalues[i].instack ? 1 : 0); + writer.writeByte(f.upvalues[i].idx); + } + } + void dumpDebug(final Prototype f) throws IOException { int i, n; - n = (strip) ? 0 : f.lineinfo.length; + dumpString(strip ? null: f.source); + n = strip ? 0 : f.lineinfo.length; dumpInt(n); for (i = 0; i < n; i++) dumpInt(f.lineinfo[i]); - n = (strip) ? 0 : f.locvars.length; + n = strip ? 0 : f.locvars.length; dumpInt(n); for (i = 0; i < n; i++) { LocVars lvi = f.locvars[i]; @@ -192,10 +202,10 @@ public class DumpState { dumpInt(lvi.startpc); dumpInt(lvi.endpc); } - n = (strip) ? 0 : f.upvalues.length; + n = strip ? 0 : f.upvalues.length; dumpInt(n); for (i = 0; i < n; i++) - dumpString(f.upvalues[i]); + dumpString(f.upvalues[i].name); } void dumpFunction(final Prototype f, final LuaString string) throws IOException { @@ -205,12 +215,12 @@ public class DumpState { dumpString(f.source); dumpInt(f.linedefined); dumpInt(f.lastlinedefined); - dumpChar(f.nups); dumpChar(f.numparams); dumpChar(f.is_vararg); dumpChar(f.maxstacksize); dumpCode(f); dumpConstants(f); + dumpUpvalues(f); dumpDebug(f); } diff --git a/src/core/org/luaj/vm2/compiler/FuncState.java b/src/core/org/luaj/vm2/compiler/FuncState.java index 81ae24de..ad59f6b4 100644 --- a/src/core/org/luaj/vm2/compiler/FuncState.java +++ b/src/core/org/luaj/vm2/compiler/FuncState.java @@ -21,15 +21,15 @@ ******************************************************************************/ package org.luaj.vm2.compiler; -import java.util.Hashtable; - import org.luaj.vm2.LuaBoolean; import org.luaj.vm2.LuaDouble; import org.luaj.vm2.LuaInteger; import org.luaj.vm2.LocVars; import org.luaj.vm2.Lua; import org.luaj.vm2.LuaNil; +import org.luaj.vm2.LuaTable; import org.luaj.vm2.Prototype; +import org.luaj.vm2.Upvaldesc; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaValue; import org.luaj.vm2.compiler.LexState.ConsControl; @@ -37,22 +37,18 @@ import org.luaj.vm2.compiler.LexState.expdesc; public class FuncState extends LuaC { - class upvaldesc { - short k; - short info; - }; static class BlockCnt { - BlockCnt previous; /* chain */ - IntPtr breaklist = new IntPtr(); /* list of jumps out of this loop */ - short nactvar; /* # active locals outside the breakable structure */ - boolean upval; /* true if some variable in the block is an upvalue */ - boolean isbreakable; /* true if `block' is a loop */ - }; + 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 */ -// LTable h; /* table to find (and reuse) elements in `k' */ - Hashtable htable; /* table to find (and reuse) elements in `k' */ + LuaTable h; /* table to find (and reuse) elements in `k' */ FuncState prev; /* enclosing function */ LexState ls; /* lexical state */ LuaC L; /* compiler being invoked */ @@ -60,14 +56,14 @@ public class FuncState extends LuaC { 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 freereg; /* first free register */ 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 */ - upvaldesc upvalues[] = new upvaldesc[LUAI_MAXUPVALUES]; /* upvalues */ - short actvar[] = new short[LUAI_MAXVARS]; /* declared-variable stack */ - + short nups; /* number of upvalues */ + short freereg; /* first free register */ + FuncState() { } @@ -77,11 +73,11 @@ public class FuncState extends LuaC { // ============================================================= InstructionPtr getcodePtr(expdesc e) { - return new InstructionPtr( f.code, e.u.s.info ); + return new InstructionPtr( f.code, e.u.info ); } int getcode(expdesc e) { - return f.code[e.u.s.info]; + return f.code[e.u.info]; } int codeAsBx(int o, int A, int sBx) { @@ -97,47 +93,66 @@ public class FuncState extends LuaC { // from lparser.c // ============================================================= - LocVars getlocvar(int i) { - return f.locvars[actvar[i]]; + /* 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]; + } - int indexupvalue(LuaString name, expdesc v) { - int i; - for (i = 0; i < f.nups; i++) { - if (upvalues[i].k == v.k && upvalues[i].info == v.u.s.info) { - _assert(f.upvalues[i] == name); - return i; - } - } - /* new one */ - checklimit(f.nups + 1, LUAI_MAXUPVALUES, "upvalues"); - if ( f.upvalues == null || f.nups + 1 > f.upvalues.length) - f.upvalues = realloc( f.upvalues, f.nups*2+1 ); - f.upvalues[f.nups] = name; - _assert (v.k == LexState.VLOCAL || v.k == LexState.VUPVAL); - upvalues[f.nups] = new upvaldesc(); - upvalues[f.nups].k = (short) (v.k); - upvalues[f.nups].info = (short) (v.u.s.info); - return f.nups++; + 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 == getlocvar(i).varname) + if (n.eq_b(getlocvar(i).varname)) return i; } return -1; /* not found */ @@ -145,68 +160,89 @@ public class FuncState extends LuaC { void markupval(int level) { BlockCnt bl = this.bl; - while (bl != null && bl.nactvar > level) + while (bl.nactvar > level) bl = bl.previous; - if (bl != null) - bl.upval = true; + bl.upval = true; } - int singlevaraux(LuaString n, expdesc var, int base) { - int v = searchvar(n); /* look up at current level */ + 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) - markupval(v); /* local will be used as an upval */ + fs.markupval(v); /* local will be used as an upval */ return LexState.VLOCAL; - } else { /* not found at current level; try upper one */ - if (prev == null) { /* no more levels? */ - /* default is global variable */ - var.init(LexState.VGLOBAL, NO_REG); - return LexState.VGLOBAL; + } 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; + final int n_gt = ls.dyd.n_gt; + /* correct pending gotos to current block and try to close it + with visible labels */ + while (i < 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 (prev.singlevaraux(n, var, 0) == LexState.VGLOBAL) - return LexState.VGLOBAL; - var.u.s.info = indexupvalue(n, var); /* else was LOCAL or UPVAL */ - var.k = LexState.VUPVAL; /* upvalue in this level */ - return LexState.VUPVAL; + if (!ls.findlabel(i)) + i++; /* move to next one */ } } - void enterblock (BlockCnt bl, boolean isbreakable) { - bl.breaklist.i = LexState.NO_JUMP; - bl.isbreakable = isbreakable; - bl.nactvar = this.nactvar; + void enterblock (BlockCnt bl, boolean isloop) { + bl.isloop = isloop; + bl.nactvar = nactvar; + if (ls.dyd == null) + ls.dyd = new LexState.Dyndata(); + 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 (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; + 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; - 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.i); + 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) { @@ -215,7 +251,7 @@ public class FuncState extends LuaC { this.exp2nextreg(cc.v); cc.v.k = LexState.VVOID; if (cc.tostore == LFIELDS_PER_FLUSH) { - this.setlist(cc.t.u.s.info, cc.na, cc.tostore); /* flush */ + this.setlist(cc.t.u.info, cc.na, cc.tostore); /* flush */ cc.tostore = 0; /* no more items pending */ } } @@ -228,13 +264,13 @@ public class FuncState extends LuaC { if (cc.tostore == 0) return; if (hasmultret(cc.v.k)) { this.setmultret(cc.v); - this.setlist(cc.t.u.s.info, cc.na, LUA_MULTRET); + 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.s.info, cc.na, cc.tostore); + this.setlist(cc.t.u.info, cc.na, cc.tostore); } } @@ -385,6 +421,17 @@ public class FuncState extends LuaC { } } + 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); @@ -428,21 +475,23 @@ public class FuncState extends LuaC { void freeexp(expdesc e) { if (e.k == LexState.VNONRELOC) - this.freereg(e.u.s.info); + this.freereg(e.u.info); } int addk(LuaValue v) { - int idx; - if (this.htable.containsKey(v)) { - idx = ((Integer) htable.get(v)).intValue(); + if (this.h == null) { + this.h = new LuaTable(); } else { - idx = this.nk; - this.htable.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; + LuaValue idx = this.h.get(v); + if (idx.isnumber()) + return idx.toint(); } + int idx = this.nk; + this.h.set(v, LuaValue.valueOf(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; } @@ -481,7 +530,7 @@ public class FuncState extends LuaC { void setoneret(expdesc e) { if (e.k == LexState.VCALL) { /* expression is an open function call? */ e.k = LexState.VNONRELOC; - e.u.s.info = GETARG_A(this.getcode(e)); + 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 */ @@ -495,20 +544,18 @@ public class FuncState extends LuaC { break; } case LexState.VUPVAL: { - e.u.s.info = this.codeABC(OP_GETUPVAL, 0, e.u.s.info, 0); - e.k = LexState.VRELOCABLE; - break; - } - case LexState.VGLOBAL: { - e.u.s.info = this.codeABx(OP_GETGLOBAL, 0, e.u.s.info); + e.u.info = this.codeABC(OP_GETUPVAL, 0, e.u.info, 0); e.k = LexState.VRELOCABLE; break; } case LexState.VINDEXED: { - this.freereg(e.u.s.aux); - this.freereg(e.u.s.info); - e.u.s.info = this - .codeABC(OP_GETTABLE, 0, e.u.s.info, e.u.s.aux); + 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; } @@ -541,7 +588,7 @@ public class FuncState extends LuaC { break; } case LexState.VK: { - this.codeABx(OP_LOADK, reg, e.u.s.info); + this.codeABx(OP_LOADK, reg, e.u.info); break; } case LexState.VKNUM: { @@ -554,8 +601,8 @@ public class FuncState extends LuaC { break; } case LexState.VNONRELOC: { - if (reg != e.u.s.info) - this.codeABC(OP_MOVE, reg, e.u.s.info, 0); + if (reg != e.u.info) + this.codeABC(OP_MOVE, reg, e.u.info, 0); break; } default: { @@ -563,7 +610,7 @@ public class FuncState extends LuaC { return; /* nothing to do... */ } } - e.u.s.info = reg; + e.u.info = reg; e.k = LexState.VNONRELOC; } @@ -577,7 +624,7 @@ public class FuncState extends LuaC { void exp2reg(expdesc e, int reg) { this.discharge2reg(e, reg); if (e.k == LexState.VJMP) - this.concat(e.t, e.u.s.info); /* put this jump in `t' list */ + 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 */ @@ -594,7 +641,7 @@ public class FuncState extends LuaC { this.patchlistaux(e.t.i, _final, reg, p_t); } e.f.i = e.t.i = LexState.NO_JUMP; - e.u.s.info = reg; + e.u.info = reg; e.k = LexState.VNONRELOC; } @@ -609,14 +656,14 @@ public class FuncState extends LuaC { this.dischargevars(e); if (e.k == LexState.VNONRELOC) { if (!e.hasjumps()) - return e.u.s.info; /* exp is already in a register */ - if (e.u.s.info >= this.nactvar) { /* reg. is not a local? */ - this.exp2reg(e, e.u.s.info); /* put value on it */ - return e.u.s.info; + 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.s.info; + return e.u.info; } void exp2val(expdesc e) { @@ -634,17 +681,17 @@ public class FuncState extends LuaC { case LexState.VFALSE: case LexState.VNIL: { if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */ - e.u.s.info = (e.k == LexState.VNIL) ? this.nilK() + e.u.info = (e.k == LexState.VNIL) ? this.nilK() : (e.k == LexState.VKNUM) ? this.numberK(e.u.nval()) : this.boolK((e.k == LexState.VTRUE)); e.k = LexState.VK; - return RKASK(e.u.s.info); + return RKASK(e.u.info); } else break; } case LexState.VK: { - if (e.u.s.info <= MAXINDEXRK) /* constant fit in argC? */ - return RKASK(e.u.s.info); + if (e.u.info <= MAXINDEXRK) /* constant fit in argC? */ + return RKASK(e.u.info); else break; } @@ -659,22 +706,18 @@ public class FuncState extends LuaC { switch (var.k) { case LexState.VLOCAL: { this.freeexp(ex); - this.exp2reg(ex, var.u.s.info); + this.exp2reg(ex, var.u.info); return; } case LexState.VUPVAL: { int e = this.exp2anyreg(ex); - this.codeABC(OP_SETUPVAL, e, var.u.s.info, 0); - break; - } - case LexState.VGLOBAL: { - int e = this.exp2anyreg(ex); - this.codeABx(OP_SETGLOBAL, e, var.u.s.info); + 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_SETTABLE, var.u.s.info, var.u.s.aux, e); + this.codeABC(op, var.u.ind_t, var.u.ind_idx, e); break; } default: { @@ -691,14 +734,14 @@ public class FuncState extends LuaC { this.freeexp(e); func = this.freereg; this.reserveregs(2); - this.codeABC(OP_SELF, func, e.u.s.info, this.exp2RK(key)); + this.codeABC(OP_SELF, func, e.u.info, this.exp2RK(key)); this.freeexp(key); - e.u.s.info = func; + e.u.info = func; e.k = LexState.VNONRELOC; } void invertjump(expdesc e) { - InstructionPtr pc = this.getjumpcontrol(e.u.s.info); + 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); @@ -719,7 +762,7 @@ public class FuncState extends LuaC { } this.discharge2anyreg(e); this.freeexp(e); - return this.condjump(OP_TESTSET, NO_REG, e.u.s.info, cond); + return this.condjump(OP_TESTSET, NO_REG, e.u.info, cond); } void goiftrue(expdesc e) { @@ -738,7 +781,7 @@ public class FuncState extends LuaC { } case LexState.VJMP: { this.invertjump(e); - pc = e.u.s.info; + pc = e.u.info; break; } default: { @@ -765,7 +808,7 @@ public class FuncState extends LuaC { break; } case LexState.VJMP: { - pc = e.u.s.info; + pc = e.u.info; break; } default: { @@ -800,7 +843,7 @@ public class FuncState extends LuaC { case LexState.VNONRELOC: { this.discharge2anyreg(e); this.freeexp(e); - e.u.s.info = this.codeABC(OP_NOT, 0, e.u.s.info, 0); + e.u.info = this.codeABC(OP_NOT, 0, e.u.info, 0); e.k = LexState.VRELOCABLE; break; } @@ -820,7 +863,9 @@ public class FuncState extends LuaC { } void indexed(expdesc t, expdesc k) { - t.u.s.aux = this.exp2RK(k); + t.u.ind_t = (short) t.u.info; + t.u.ind_idx = (short) this.exp2RK(k); + t.u.ind_vt = (short) ((t.k == LexState.VUPVAL) ? LexState.VUPVAL : LexState.VLOCAL); t.k = LexState.VINDEXED; } @@ -881,7 +926,7 @@ public class FuncState extends LuaC { this.freeexp(e2); this.freeexp(e1); } - e1.u.s.info = this.codeABC(op, 0, o1, o2); + e1.u.info = this.codeABC(op, 0, o1, o2); e1.k = LexState.VRELOCABLE; } } @@ -898,7 +943,7 @@ public class FuncState extends LuaC { o2 = temp; /* o1 <==> o2 */ cond = 1; } - e1.u.s.info = this.condjump(op, cond, o1, o2); + e1.u.info = this.condjump(op, cond, o1, o2); e1.k = LexState.VJMP; } @@ -979,11 +1024,11 @@ public class FuncState extends LuaC { this.exp2val(e2); if (e2.k == LexState.VRELOCABLE && GET_OPCODE(this.getcode(e2)) == OP_CONCAT) { - _assert (e1.u.s.info == GETARG_B(this.getcode(e2)) - 1); + _assert (e1.u.info == GETARG_B(this.getcode(e2)) - 1); this.freeexp(e1); - SETARG_B(this.getcodePtr(e2), e1.u.s.info); + SETARG_B(this.getcodePtr(e2), e1.u.info); e1.k = LexState.VRELOCABLE; - e1.u.s.info = e2.u.s.info; + e1.u.info = e2.u.info; } else { this.exp2nextreg(e2); /* operand must be on the 'stack' */ this.codearith(OP_CONCAT, e1, e2); @@ -1078,7 +1123,7 @@ public class FuncState extends LuaC { this.codeABC(OP_SETLIST, base, b, 0); this.code(c, this.ls.lastline); } - this.freereg = base + 1; /* free registers with list values */ + this.freereg = (short) (base + 1); /* free registers with list values */ } } diff --git a/src/core/org/luaj/vm2/compiler/LexState.java b/src/core/org/luaj/vm2/compiler/LexState.java index 40247431..2d43c944 100644 --- a/src/core/org/luaj/vm2/compiler/LexState.java +++ b/src/core/org/luaj/vm2/compiler/LexState.java @@ -105,13 +105,12 @@ public class LexState { VKNUM = 5, /* nval = numerical value */ VLOCAL = 6, /* info = local register */ VUPVAL = 7, /* info = index of upvalue in `upvalues' */ - VGLOBAL = 8, /* info = index of table, aux = index of global name in `k' */ - VINDEXED = 9, /* info = table register, aux = index register (or `k') */ - VJMP = 10, /* info = instruction pc */ - VRELOCABLE = 11, /* info = instruction pc */ - VNONRELOC = 12, /* info = result register */ - VCALL = 13, /* info = instruction pc */ - VVARARG = 14; /* info = instruction pc */ + VINDEXED = 8, /* info = table register, aux = index register (or `k') */ + VJMP = 9, /* info = instruction pc */ + VRELOCABLE = 10, /* info = instruction pc */ + VNONRELOC = 11, /* info = result register */ + VCALL = 12, /* info = instruction pc */ + VVARARG = 13; /* info = instruction pc */ /* semantics information */ private static class SemInfo { @@ -139,7 +138,9 @@ public class LexState { InputStream z; /* input stream */ byte[] buff; /* buffer for tokens */ int nbuff; /* length of buffer */ + Dyndata dyd; /* dynamic structures used by the parser */ LuaString source; /* current source name */ + LuaString envn; /* environment variable name */ byte decpoint; /* locale decimal point */ /* ORDER RESERVED */ @@ -155,13 +156,13 @@ public class LexState { final static int /* terminal symbols denoted by reserved words */ TK_AND=257, TK_BREAK=258, TK_DO=259, TK_ELSE=260, TK_ELSEIF=261, - TK_END=262, TK_FALSE=263, TK_FOR=264, TK_FUNCTION=265, TK_IF=266, - TK_IN=267, TK_LOCAL=268, TK_NIL=269, TK_NOT=270, TK_OR=271, TK_REPEAT=272, - TK_RETURN=273, TK_THEN=274, TK_TRUE=275, TK_UNTIL=276, TK_WHILE=277, + TK_END=262, TK_FALSE=263, TK_FOR=264, TK_FUNCTION=265, TK_GOTO=266, TK_IF=267, + TK_IN=268, TK_LOCAL=269, TK_NIL=270, TK_NOT=271, TK_OR=272, TK_REPEAT=273, + TK_RETURN=274, TK_THEN=275, TK_TRUE=276, TK_UNTIL=277, TK_WHILE=278, /* other terminal symbols */ - TK_CONCAT=278, TK_DOTS=279, TK_EQ=280, TK_GE=281, TK_LE=282, TK_NE=283, - TK_NUMBER=284, TK_NAME=285, TK_STRING=286, TK_EOS=287; - + TK_CONCAT=279, TK_DOTS=280, TK_EQ=281, TK_GE=282, TK_LE=283, TK_NE=284, + TK_DBCOLON=285, TK_EOS=286, TK_NUMBER=287, TK_NAME=288, TK_STRING=289; + final static int FIRST_RESERVED = TK_AND; final static int NUM_RESERVED = TK_WHILE+1-FIRST_RESERVED; @@ -308,6 +309,7 @@ public class LexState { this.linenumber = 1; this.lastline = 1; this.source = source; + this.envn = LuaValue.ENV; /* environment variable name */ this.nbuff = 0; /* initialize buffer */ this.current = firstByte; /* read first char */ this.skipShebang(); @@ -690,19 +692,27 @@ public class LexState { // from lparser.c // ============================================================= + static final boolean vkisvar(final int k) { + return (VLOCAL <= (k) && (k) <= VINDEXED); + } + + static final boolean vkisinreg(final int k) { + return ((k) == VNONRELOC || (k) == VLOCAL); + } + static class expdesc { int k; // expkind, from enumerated list, above static class U { // originally a union - static class S { - int info, aux; - } - final S s = new S(); + short ind_idx; // index (R/K) + short ind_t; // table(register or upvalue) + short ind_vt; // whether 't' is register (VLOCAL) or (UPVALUE) private LuaValue _nval; + int info; public void setNval(LuaValue r) { _nval = r; } public LuaValue nval() { - return (_nval == null? LuaInteger.valueOf(s.info): _nval); + return (_nval == null? LuaInteger.valueOf(info): _nval); } }; final U u = new U(); @@ -712,7 +722,7 @@ public class LexState { this.f.i = NO_JUMP; this.t.i = NO_JUMP; this.k = k; - this.u.s.info = i; + this.u.info = i; } boolean hasjumps() { @@ -725,14 +735,51 @@ public class LexState { public void setvalue(expdesc other) { this.k = other.k; - this.u._nval = other.u._nval; - this.u.s.info = other.u.s.info; - this.u.s.aux = other.u.s.aux; + this.u.info = other.u.info; + this.u.ind_idx = other.u.ind_idx; + this.u.ind_t = other.u.ind_t; + this.u.ind_vt = other.u.ind_vt; this.t.i = other.t.i; this.f.i = other.f.i; } } - + + + /* description of active local variable */ + static class Vardesc { + final short idx; /* variable index in stack */ + Vardesc(int idx) { + this.idx = (short) idx; + } + }; + + + /* description of pending goto statements and label statements */ + static class Labeldesc { + LuaString name; /* label identifier */ + int pc; /* position in code */ + int line; /* line where it appeared */ + short nactvar; /* local level where it appears in current block */ + public Labeldesc(LuaString name, int pc, int line, short nactvar) { + this.name = name; + this.pc = pc; + this.line = line; + this.nactvar = nactvar; + } + }; + + + /* dynamic structures used by the parser */ + static class Dyndata { + Vardesc[] actvar; /* list of active local variables */ + int n_actvar = 0; + Labeldesc[] gt; /* list of pending gotos */ + int n_gt = 0; + Labeldesc[] label; /* list of active labels */ + int n_label = 0; + }; + + boolean hasmultret(int k) { return ((k) == VCALL || (k) == VVARARG); } @@ -741,9 +788,21 @@ public class LexState { name args description ------------------------------------------------------------------------*/ - /* - * * prototypes for recursive non-terminal functions - */ + void anchor_token () { + /* last token from outer function must be EOS */ + LuaC._assert(fs != null || t.token == TK_EOS); + if (t.token == TK_NAME || t.token == TK_STRING) { + LuaString ts = t.seminfo.ts; + // TODO: is this necessary? + newstring(ts.m_bytes, 0, ts.m_length); + } + } + + /* semantic error */ + void semerror (String msg) { + t.token = 0; /* remove 'near to' from final message */ + syntaxerror(msg); + } void error_expected(int token) { syntaxerror(L.pushfstring(LUA_QS(token2str(token)) + " expected")); @@ -822,9 +881,11 @@ public class LexState { } void new_localvar(LuaString name, int n) { - FuncState fs = this.fs; + int reg = registerlocalvar(name); fs.checklimit(fs.nactvar + n + 1, FuncState.LUAI_MAXVARS, "local variables"); - fs.actvar[fs.nactvar + n] = (short) registerlocalvar(name); + if (dyd.actvar == null || dyd.n_actvar + 1 > dyd.actvar.length) + dyd.actvar = LuaC.realloc(dyd.actvar, Math.max(1, dyd.n_actvar * 2)); + dyd.actvar[dyd.n_actvar] = new Vardesc(reg); } void adjustlocalvars(int nvars) { @@ -844,8 +905,13 @@ public class LexState { void singlevar(expdesc var) { LuaString varname = this.str_checkname(); FuncState fs = this.fs; - if (fs.singlevaraux(varname, var, 1) == VGLOBAL) - var.u.s.info = fs.stringK(varname); /* info points to global name */ + if (FuncState.singlevaraux(fs, varname, var, 1) == VVOID) { /* global name? */ + expdesc key = new expdesc(); + FuncState.singlevaraux(fs, this.envn, var, 1); /* get environment variable */ + LuaC._assert(var.k == VLOCAL || var.k == VUPVAL); + this.codestring(key, varname); /* key is variable name */ + fs.indexed(var, key); /* env[varname] */ + } } void adjust_assign(int nvars, int nexps, expdesc e) { @@ -880,22 +946,91 @@ public class LexState { void leavelevel() { L.nCcalls--; } - - void pushclosure(FuncState func, expdesc v) { + + void closegoto(int g, Labeldesc label) { + int i; FuncState fs = this.fs; - Prototype f = fs.f; - if (f.p == null || fs.np + 1 > f.p.length) - f.p = LuaC.realloc( f.p, fs.np*2 + 1 ); - f.p[fs.np++] = func.f; - v.init(VRELOCABLE, fs.codeABx(Lua.OP_CLOSURE, 0, fs.np - 1)); - for (int i = 0; i < func.f.nups; i++) { - int o = (func.upvalues[i].k == VLOCAL) ? Lua.OP_MOVE - : Lua.OP_GETUPVAL; - fs.codeABC(o, 0, func.upvalues[i].info, 0); + Labeldesc[] gl = this.dyd.gt; + Labeldesc gt = gl[g]; + LuaC._assert(gt.name.eq_b(label.name)); + if (gt.nactvar < label.nactvar) { + LuaString vname = fs.getlocvar(gt.nactvar).varname; + String msg = L.pushfstring(" at line " + + gt.line + " jumps into the scope of local '" + + vname.tojstring() + "'"); + semerror(msg); + } + fs.patchlist(gt.pc, label.pc); + /* remove goto from pending list */ + System.arraycopy(gl, g + 1, gl, g, this.dyd.n_gt - g - 1); + gl[--this.dyd.n_gt] = null; + } + + /* + ** try to close a goto with existing labels; this solves backward jumps + */ + boolean findlabel (int g) { + int i; + BlockCnt bl = fs.bl; + Dyndata dyd = this.dyd; + Labeldesc gt = dyd.gt[g]; + /* check labels in current block for a match */ + for (i = bl.firstlabel; i < dyd.n_label; i++) { + Labeldesc lb = dyd.label[i]; + if (lb.name.eq_b(gt.name)) { /* correct label? */ + if (gt.nactvar > lb.nactvar && + (bl.upval || dyd.n_label > bl.firstlabel)) + fs.patchclose(gt.pc, lb.nactvar); + closegoto(g, lb); /* close it */ + return true; + } + } + return false; /* label not found; cannot close goto */ + } + + /* Caller must LuaC.grow() the vector before calling this. */ + int newlabelentry(Labeldesc[] l, int index, LuaString name, int line, int pc) { + l[index] = new Labeldesc(name, pc, line, fs.nactvar); + return index; + } + + /* + ** check whether new label 'lb' matches any pending gotos in current + ** block; solves forward jumps + */ + void findgotos (Labeldesc lb) { + Labeldesc[] gl = dyd.gt; + int i = fs.bl.firstgoto; + while (i < dyd.n_gt) { + if (gl[i].name.eq_b(lb.name)) + closegoto(i, lb); + else + i++; } } - void open_func (FuncState fs) { + + /* + ** create a label named "break" to resolve break statements + */ + void breaklabel () { + LuaString n = LuaString.valueOf("break"); + int l = newlabelentry(LuaC.grow(dyd.label, dyd.n_label+1), dyd.n_label++, n, 0, fs.pc); + findgotos(dyd.label[l]); + } + + /* + ** generates an error for an undefined 'goto'; choose appropriate + ** message when label name is a reserved word (which can only be 'break') + */ + void undefgoto (Labeldesc gt) { + String msg = L.pushfstring(isReservedKeyword(gt.name.tojstring()) + ? "<"+gt.name+"> at line "+gt.line+" not inside a loop" + : "no visible label '"+gt.name+"' for at line "+gt.line); + semerror(msg); + } + + void open_func (FuncState fs, BlockCnt bl) { LuaC L = this.L; Prototype f = new Prototype(); if ( this.fs!=null ) @@ -915,14 +1050,14 @@ public class LexState { fs.nactvar = 0; fs.bl = null; f.maxstacksize = 2; /* registers 0/1 are always valid */ - //fs.h = new LTable(); - fs.htable = new Hashtable(); + fs.enterblock(bl, false); } void close_func() { FuncState fs = this.fs; Prototype f = fs.f; - this.removevars(0); + fs.ret(0, 0); + fs.leaveblock(); fs.ret(0, 0); /* final return */ f.code = LuaC.realloc(f.code, fs.pc); f.lineinfo = LuaC.realloc(f.lineinfo, fs.pc); @@ -931,7 +1066,7 @@ public class LexState { f.p = LuaC.realloc(f.p, fs.np); f.locvars = LuaC.realloc(f.locvars, fs.nlocvars); // f.sizelocvars = fs.nlocvars; - f.upvalues = LuaC.realloc(f.upvalues, f.nups); + f.upvalues = LuaC.realloc(f.upvalues, fs.nups); // LuaC._assert (CheckCode.checkcode(f)); LuaC._assert (fs.bl == null); this.fs = fs.prev; @@ -997,8 +1132,8 @@ public class LexState { this.checknext('='); rkkey = fs.exp2RK(key); this.expr(val); - fs.codeABC(Lua.OP_SETTABLE, cc.t.u.s.info, rkkey, fs.exp2RK(val)); - fs.freereg = reg; /* free registers */ + fs.codeABC(Lua.OP_SETTABLE, cc.t.u.info, rkkey, fs.exp2RK(val)); + fs.freereg = (short)reg; /* free registers */ } void listfield (ConsControl cc) { @@ -1106,7 +1241,8 @@ public class LexState { void body(expdesc e, boolean needself, int line) { /* body -> `(' parlist `)' chunk END */ FuncState new_fs = new FuncState(); - open_func(new_fs); + BlockCnt bl = new BlockCnt(); + open_func(new_fs, bl); new_fs.f.linedefined = line; this.checknext('('); if (needself) { @@ -1115,11 +1251,10 @@ public class LexState { } this.parlist(); this.checknext(')'); - this.chunk(); + this.statlist(); new_fs.f.lastlinedefined = this.linenumber; this.check_match(TK_END, TK_FUNCTION, line); this.close_func(); - this.pushclosure(new_fs, e); } int explist1(expdesc v) { @@ -1169,7 +1304,7 @@ public class LexState { } } LuaC._assert (f.k == VNONRELOC); - base = f.u.s.info; /* base register for call */ + base = f.u.info; /* base register for call */ if (hasmultret(args.k)) nparams = Lua.LUA_MULTRET; /* open call */ else { @@ -1179,7 +1314,7 @@ public class LexState { } f.init(VCALL, fs.codeABC(Lua.OP_CALL, base, nparams + 1, 2)); fs.fixline(line); - fs.freereg = base+1; /* call remove function and arguments and leaves + fs.freereg = (short)(base+1); /* call remove function and arguments and leaves * (unless changed) one result */ } @@ -1427,11 +1562,12 @@ public class LexState { */ - boolean block_follow (int token) { - switch (token) { - case TK_ELSE: case TK_ELSEIF: case TK_END: - case TK_UNTIL: case TK_EOS: + boolean block_follow (boolean withuntil) { + switch (t.token) { + case TK_ELSE: case TK_ELSEIF: case TK_END: case TK_EOS: return true; + case TK_UNTIL: + return withuntil; default: return false; } } @@ -1442,8 +1578,7 @@ public class LexState { FuncState fs = this.fs; BlockCnt bl = new BlockCnt(); fs.enterblock(bl, false); - this.chunk(); - LuaC._assert(bl.breaklist.i == NO_JUMP); + this.statlist(); fs.leaveblock(); } @@ -1467,22 +1602,25 @@ public class LexState { */ void check_conflict (LHS_assign lh, expdesc v) { FuncState fs = this.fs; - int extra = fs.freereg; /* eventual position to save local variable */ + short extra = (short) fs.freereg; /* eventual position to save local variable */ boolean conflict = false; for (; lh!=null; lh = lh.prev) { if (lh.v.k == VINDEXED) { - if (lh.v.u.s.info == v.u.s.info) { /* conflict? */ + /* table is the upvalue/local being assigned now? */ + if (lh.v.u.ind_vt == v.k && lh.v.u.ind_t == v.u.info) { conflict = true; - lh.v.u.s.info = extra; /* previous assignment will use safe copy */ + lh.v.u.ind_vt = VLOCAL; + lh.v.u.ind_t = extra; /* previous assignment will use safe copy */ } - if (lh.v.u.s.aux == v.u.s.info) { /* conflict? */ + /* index is the local being assigned? (index cannot be upvalue) */ + if (v.k == VLOCAL && lh.v.u.ind_idx == v.u.info) { conflict = true; - lh.v.u.s.aux = extra; /* previous assignment will use safe copy */ + lh.v.u.ind_idx = extra; /* previous assignment will use safe copy */ } } } if (conflict) { - fs.codeABC(Lua.OP_MOVE, fs.freereg, v.u.s.info, 0); /* make copy */ + fs.codeABC(Lua.OP_MOVE, fs.freereg, v.u.info, 0); /* make copy */ fs.reserveregs(1); } } @@ -1532,23 +1670,46 @@ public class LexState { return v.f.i; } - - void breakstat() { - FuncState fs = this.fs; - BlockCnt bl = fs.bl; - boolean upval = false; - while (bl != null && !bl.isbreakable) { - upval |= bl.upval; - bl = bl.previous; + void gotostat(int pc) { + int line = linenumber; + LuaString label; + int g; + if (testnext(TK_GOTO)) + label = str_checkname(); + else { + next(); /* skip break */ + label = LuaString.valueOf("break"); } - if (bl == null) - this.syntaxerror("no loop to break"); - if (upval) - fs.codeABC(Lua.OP_CLOSE, bl.nactvar, 0, 0); - fs.concat(bl.breaklist, fs.jump()); + g = newlabelentry(LuaC.grow(dyd.gt, dyd.n_gt+1), dyd.n_gt++, label, line, pc); + findlabel(g); /* close it if label already defined */ } + /* skip no-op statements */ + void skipnoopstat () { + while (t.token == ';' || t.token == TK_DBCOLON) + statement(); + } + + + void labelstat (LuaString label, int line) { + /* label -> '::' NAME '::' */ + FuncState fs = this.fs; + Labeldesc[] ll = dyd.label; + int l; /* index of new label being created */ + fs.checkrepeated(ll, dyd.n_label, label); /* check for repeated labels */ + checknext(TK_DBCOLON); /* skip double colon */ + /* create new entry for this label */ + l = newlabelentry(LuaC.grow(ll, dyd.n_label+1), dyd.n_label++, label, line, fs.pc); + skipnoopstat(); /* skip other no-op statements */ + if (block_follow(false)) { /* label is last no-op statement in the block? */ + /* assume that locals are already out of scope */ + ll[l].nactvar = fs.bl.nactvar; + } + findgotos(ll[l]); +} + + void whilestat (int line) { /* whilestat -> WHILE cond DO block END */ FuncState fs = this.fs; @@ -1577,18 +1738,14 @@ public class LexState { fs.enterblock(bl1, true); /* loop block */ fs.enterblock(bl2, false); /* scope block */ this.next(); /* skip REPEAT */ - this.chunk(); + this.statlist(); this.check_match(TK_UNTIL, TK_REPEAT, line); condexit = this.cond(); /* read condition (inside scope block) */ - if (!bl2.upval) { /* no upvalues? */ - fs.leaveblock(); /* finish scope */ - fs.patchlist(condexit, repeat_init); /* close the loop */ - } else { /* complete semantics when there are upvalues */ - this.breakstat(); /* if condition then break */ - fs.patchtohere(condexit); /* else... */ - fs.leaveblock(); /* finish scope... */ - fs.patchlist(fs.jump(), repeat_init); /* and repeat */ + if (bl2.upval) { /* upvalues? */ + fs.patchclose(condexit, bl2.nactvar); } + fs.leaveblock(); /* finish scope */ + fs.patchlist(condexit, repeat_init); /* close the loop */ fs.leaveblock(); /* finish loop */ } @@ -1807,7 +1964,7 @@ public class LexState { expdesc e = new expdesc(); int first, nret; /* registers with returned values */ this.next(); /* skip RETURN */ - if (block_follow(this.t.token) || this.t.token == ';') + if (block_follow(true) || this.t.token == ';') first = nret = 0; /* return no values */ else { nret = this.explist1(e); /* optional return values */ @@ -1874,9 +2031,10 @@ public class LexState { this.retstat(); return true; /* must be last statement */ } - case TK_BREAK: { /* stat -> breakstat */ + case TK_BREAK: + case TK_GOTO: { /* stat -> breakstat */ this.next(); /* skip BREAK */ - this.breakstat(); + this.gotostat(fs.jump()); return true; /* must be last statement */ } default: { @@ -1886,20 +2044,34 @@ public class LexState { } } - void chunk() { - /* chunk -> { stat [`;'] } */ - boolean islast = false; - this.enterlevel(); - while (!islast && !block_follow(this.t.token)) { - islast = this.statement(); - this.testnext(';'); - LuaC._assert (this.fs.f.maxstacksize >= this.fs.freereg - && this.fs.freereg >= this.fs.nactvar); - this.fs.freereg = this.fs.nactvar; /* free registers */ + void statlist() { + /* statlist -> { stat [`;'] } */ + while (!block_follow(true)) { + if (t.token == TK_RETURN) { + statement(); + return; /* 'return' must be last statement */ + } + statement(); } - this.leavelevel(); } + /* + ** compiles the main function, which is a regular vararg function with an + ** upvalue named LUA_ENV + */ + public void mainfunc(FuncState funcstate) { + BlockCnt bl = new BlockCnt(); + open_func(funcstate, bl); + fs.f.is_vararg = 1; /* main function is always vararg */ + expdesc v = new expdesc(); + v.init(VLOCAL, 0); /* create and... */ + fs.newupvalue(envn, v); /* ...set environment upvalue */ + next(); /* read first token */ + statlist(); /* parse main body */ + check(TK_EOS); + close_func(); + } + /* }====================================================================== */ } diff --git a/src/core/org/luaj/vm2/compiler/LuaC.java b/src/core/org/luaj/vm2/compiler/LuaC.java index f5bb61bf..71d86ed3 100644 --- a/src/core/org/luaj/vm2/compiler/LuaC.java +++ b/src/core/org/luaj/vm2/compiler/LuaC.java @@ -34,6 +34,7 @@ import org.luaj.vm2.LuaFunction; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Prototype; +import org.luaj.vm2.Upvaldesc; import org.luaj.vm2.LoadState.LuaCompiler; /** @@ -83,7 +84,7 @@ public class LuaC extends Lua implements LuaCompiler { } public static final int MAXSTACK = 250; - static final int LUAI_MAXUPVALUES = 60; + static final int LUAI_MAXUPVAL = 0xff; static final int LUAI_MAXVARS = 200; static final int NO_REG = MAXARG_A; @@ -106,6 +107,10 @@ public class LuaC extends Lua implements LuaCompiler { i.set( ( i.get() & (MASK_NOT_OP)) | ((o << POS_OP) & MASK_OP) ); } + static void SETARG_A(int[] code, int index, int u) { + code[index] = (code[index] & (MASK_NOT_A)) | ((u << POS_A) & MASK_A); + } + static void SETARG_A(InstructionPtr i,int u) { i.set( ( i.get() & (MASK_NOT_A)) | ((u << POS_A) & MASK_A) ); } @@ -169,6 +174,31 @@ public class LuaC extends Lua implements LuaCompiler { return a; } + static Upvaldesc[] realloc(Upvaldesc[] v, int n) { + Upvaldesc[] a = new Upvaldesc[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static LexState.Vardesc[] realloc(LexState.Vardesc[] v, int n) { + LexState.Vardesc[] a = new LexState.Vardesc[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static LexState.Labeldesc[] grow(LexState.Labeldesc[] v, int min_n) { + return v == null ? new LexState.Labeldesc[2] : v.length <= min_n ? v : realloc(v, v.length*2); + } + + static LexState.Labeldesc[] realloc(LexState.Labeldesc[] v, int n) { + LexState.Labeldesc[] a = new LexState.Labeldesc[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 ) @@ -206,23 +236,22 @@ public class LuaC extends Lua implements LuaCompiler { (new LuaC(new Hashtable())).luaY_parser(firstByte, stream, name); } + /** Parse the input */ private Prototype luaY_parser(int firstByte, InputStream z, String name) { LexState lexstate = new LexState(this, z); FuncState funcstate = new FuncState(); // lexstate.buff = buff; + lexstate.fs = funcstate; lexstate.setinput( this, firstByte, z, (LuaString) LuaValue.valueOf(name) ); - lexstate.open_func(funcstate); /* main func. is always vararg */ - funcstate.f.is_vararg = LuaC.VARARG_ISVARARG; + funcstate.f = new Prototype(); funcstate.f.source = (LuaString) LuaValue.valueOf(name); - lexstate.next(); /* read first token */ - lexstate.chunk(); - lexstate.check(LexState.TK_EOS); - lexstate.close_func(); + lexstate.mainfunc(funcstate); LuaC._assert (funcstate.prev == null); - LuaC._assert (funcstate.f.nups == 0); - LuaC._assert (lexstate.fs == null); + /* all scopes should be correctly finished */ + LuaC._assert (lexstate.dyd == null + || (lexstate.dyd.n_actvar == 0 && lexstate.dyd.n_gt == 0 && lexstate.dyd.n_label == 0)); return funcstate.f; } diff --git a/src/core/org/luaj/vm2/lib/DebugLib.java b/src/core/org/luaj/vm2/lib/DebugLib.java index da3e103a..8947f578 100644 --- a/src/core/org/luaj/vm2/lib/DebugLib.java +++ b/src/core/org/luaj/vm2/lib/DebugLib.java @@ -123,6 +123,7 @@ public class DebugLib extends VarArgFunction { private static final LuaString COUNT = valueOf("count"); private static final LuaString RETURN = valueOf("return"); private static final LuaString TAILRETURN = valueOf("tail return"); + private static final LuaString CONSTANT = valueOf("constant"); private static final LuaString FUNC = valueOf("func"); private static final LuaString NUPS = valueOf("nups"); @@ -219,7 +220,7 @@ public class DebugLib extends VarArgFunction { public LuaString[] getfunckind() { if ( closure == null || pc<0 ) return null; int stackpos = (closure.p.code[pc] >> 6) & 0xff; - return getobjname(this, stackpos); + return getobjname(this, pc, stackpos); } public String sourceline() { if ( closure == null ) return func.tojstring(); @@ -500,7 +501,7 @@ public class DebugLib extends VarArgFunction { break; } case 'u': { - info.set(NUPS, valueOf(c!=null? c.p.nups: 0)); + info.set(NUPS, valueOf(c!=null? c.p.upvalues.length: 0)); break; } case 'n': { @@ -604,7 +605,7 @@ public class DebugLib extends VarArgFunction { static LuaString findupvalue(LuaClosure c, int up) { if ( c.upValues != null && up > 0 && up <= c.upValues.length ) { if ( c.p.upvalues != null && up <= c.p.upvalues.length ) - return c.p.upvalues[up-1]; + return c.p.upvalues[up-1].name; else return LuaString.valueOf( "."+up ); } @@ -720,40 +721,53 @@ public class DebugLib extends VarArgFunction { // return StrValue[] { name, namewhat } if found, null if not - static LuaString[] getobjname(DebugInfo di, int stackpos) { - LuaString name; - if (di.closure != null) { /* a Lua function? */ - Prototype p = di.closure.p; - int pc = di.pc; // currentpc(L, ci); - int i;// Instruction i; - name = p.getlocalname(stackpos + 1, pc); - if (name != null) /* is a local? */ - return new LuaString[] { name, LOCAL }; - i = symbexec(p, pc, stackpos); /* try symbolic execution */ - lua_assert(pc != -1); + static LuaString[] getobjname(DebugInfo di, int lastpc, int reg) { + if (di.closure == null) + return null; /* Not a Lua function? */ + + Prototype p = di.closure.p; + int pc = di.pc; // currentpc(L, ci); + LuaString name = p.getlocalname(reg + 1, pc); + if (name != null) /* is a local? */ + return new LuaString[] { name, LOCAL }; + + /* else try symbolic execution */ + pc = findsetreg(p, lastpc, reg); + if (pc != -1) { /* could find instruction? */ + int i = p.code[pc]; switch (Lua.GET_OPCODE(i)) { - case Lua.OP_GETGLOBAL: { - int g = Lua.GETARG_Bx(i); /* global index */ - // lua_assert(p.k[g].isString()); - return new LuaString[] { p.k[g].strvalue(), GLOBAL }; - } case Lua.OP_MOVE: { int a = Lua.GETARG_A(i); int b = Lua.GETARG_B(i); /* move from `b' to `a' */ if (b < a) - return getobjname(di, b); /* get name for `b' */ + return getobjname(di, pc, b); /* get name for `b' */ break; } + case Lua.OP_GETTABUP: case Lua.OP_GETTABLE: { int k = Lua.GETARG_C(i); /* key index */ + int t = Lua.GETARG_Bx(i); /* table index */ + LuaString vn = (Lua.GET_OPCODE(i) == Lua.OP_GETTABLE) /* name of indexed variable */ + ? p.getlocalname(t + 1, pc) + : (t < p.upvalues.length ? p.upvalues[t].name : QMARK); name = kname(p, k); - return new LuaString[] { name, FIELD }; + return new LuaString[] { name, vn.eq_b(ENV)? GLOBAL: FIELD }; } case Lua.OP_GETUPVAL: { int u = Lua.GETARG_B(i); /* upvalue index */ - name = u < p.upvalues.length ? p.upvalues[u] : QMARK; + name = u < p.upvalues.length ? p.upvalues[u].name : QMARK; return new LuaString[] { name, UPVALUE }; } + case Lua.OP_LOADK: + case Lua.OP_LOADKX: { + int b = (Lua.GET_OPCODE(i) == Lua.OP_LOADK) ? Lua.GETARG_Bx(i) + : Lua.GETARG_Ax(p.code[pc + 1]); + if (p.k[b].isstring()) { + name = p.k[b].strvalue(); + return new LuaString[] { name, CONSTANT }; + } + break; + } case Lua.OP_SELF: { int k = Lua.GETARG_C(i); /* key index */ name = kname(p, k); @@ -782,7 +796,7 @@ public class DebugLib extends VarArgFunction { lua_assert(pt.numparams + (pt.is_vararg & Lua.VARARG_HASARG) <= pt.maxstacksize); lua_assert((pt.is_vararg & Lua.VARARG_NEEDSARG) == 0 || (pt.is_vararg & Lua.VARARG_HASARG) != 0); - if (!(pt.upvalues.length <= pt.nups)) return false; +// if (!(pt.upvalues.length <= pt.nups)) return false; if (!(pt.lineinfo.length == pt.code.length || pt.lineinfo.length == 0)) return false; if (!(Lua.GET_OPCODE(pt.code[pt.code.length - 1]) == Lua.OP_RETURN)) return false; return true; @@ -816,162 +830,51 @@ public class DebugLib extends VarArgFunction { return true; } - - // return last instruction, or 0 if error - static int symbexec(Prototype pt, int lastpc, int reg) { - int pc; - int last; /* stores position of last instruction that changed `reg' */ - last = pt.code.length - 1; /* - * points to final return (a `neutral' - * instruction) - */ - if (!(precheck(pt))) return 0; - for (pc = 0; pc < lastpc; pc++) { - int i = pt.code[pc]; - int op = Lua.GET_OPCODE(i); - int a = Lua.GETARG_A(i); - int b = 0; - int c = 0; - if (!(op < Lua.NUM_OPCODES)) return 0; - if (!checkreg(pt, a)) return 0; - switch (Lua.getOpMode(op)) { - case Lua.iABC: { - b = Lua.GETARG_B(i); - c = Lua.GETARG_C(i); - if (!(checkArgMode(pt, b, Lua.getBMode(op)))) return 0; - if (!(checkArgMode(pt, c, Lua.getCMode(op)))) return 0; - break; - } - case Lua.iABx: { - b = Lua.GETARG_Bx(i); - if (Lua.getBMode(op) == Lua.OpArgK) - if (!(b < pt.k.length)) return 0; - break; - } - case Lua.iAsBx: { - b = Lua.GETARG_sBx(i); - if (Lua.getBMode(op) == Lua.OpArgR) { - int dest = pc + 1 + b; - if (!(0 <= dest && dest < pt.code.length)) return 0; - if (dest > 0) { - /* cannot jump to a setlist count */ - int d = pt.code[dest - 1]; - if ((Lua.GET_OPCODE(d) == Lua.OP_SETLIST && Lua.GETARG_C(d) == 0)) return 0; - } - } - break; - } - } - if (Lua.testAMode(op)) { - if (a == reg) - last = pc; /* change register `a' */ - } - if (Lua.testTMode(op)) { - if (!(pc + 2 < pt.code.length)) return 0; /* check skip */ - if (!(Lua.GET_OPCODE(pt.code[pc + 1]) == Lua.OP_JMP)) return 0; - } - switch (op) { - case Lua.OP_LOADBOOL: { - if (!(c == 0 || pc + 2 < pt.code.length)) return 0; /* check its jump */ - break; - } - case Lua.OP_LOADNIL: { - if (a <= reg && reg <= b) - last = pc; /* set registers from `a' to `b' */ - break; - } - case Lua.OP_GETUPVAL: - case Lua.OP_SETUPVAL: { - if (!(b < pt.nups)) return 0; - break; - } - case Lua.OP_GETGLOBAL: - case Lua.OP_SETGLOBAL: { - if (!(pt.k[b].isstring())) return 0; - break; - } - case Lua.OP_SELF: { - if (!checkreg(pt, a + 1)) return 0; - if (reg == a + 1) - last = pc; - break; - } - case Lua.OP_CONCAT: { - if (!(b < c)) return 0; /* at least two operands */ - break; - } - case Lua.OP_TFORLOOP: { - if (!(c >= 1)) return 0; /* at least one result (control variable) */ - if (!checkreg(pt, a + 2 + c)) return 0; /* space for results */ - if (reg >= a + 2) - last = pc; /* affect all regs above its base */ - break; - } - case Lua.OP_FORLOOP: - case Lua.OP_FORPREP: - if (!checkreg(pt, a + 3)) return 0; - /* go through */ - case Lua.OP_JMP: { - int dest = pc + 1 + b; - /* not full check and jump is forward and do not skip `lastpc'? */ - if (reg != Lua.NO_REG && pc < dest && dest <= lastpc) - pc += b; /* do the jump */ - break; - } - case Lua.OP_CALL: - case Lua.OP_TAILCALL: { - if (b != 0) { - if (!checkreg(pt, a + b - 1)) return 0; - } - c--; /* c = num. returns */ - if (c == Lua.LUA_MULTRET) { - if (!(checkopenop(pt, pc))) return 0; - } else if (c != 0) - if (!checkreg(pt, a + c - 1)) return 0; - if (reg >= a) - last = pc; /* affect all registers above base */ - break; - } - case Lua.OP_RETURN: { - b--; /* b = num. returns */ - if (b > 0) - if (!checkreg(pt, a + b - 1)) return 0; - break; - } - case Lua.OP_SETLIST: { - if (b > 0) - if (!checkreg(pt, a + b)) return 0; - if (c == 0) - pc++; - break; - } - case Lua.OP_CLOSURE: { - int nup, j; - if (!(b < pt.p.length)) return 0; - nup = pt.p[b].nups; - if (!(pc + nup < pt.code.length)) return 0; - for (j = 1; j <= nup; j++) { - int op1 = Lua.GET_OPCODE(pt.code[pc + j]); - if (!(op1 == Lua.OP_GETUPVAL || op1 == Lua.OP_MOVE)) return 0; - } - if (reg != Lua.NO_REG) /* tracing? */ - pc += nup; /* do not 'execute' these pseudo-instructions */ - break; - } - case Lua.OP_VARARG: { - if (!((pt.is_vararg & Lua.VARARG_ISVARARG) != 0 - && (pt.is_vararg & Lua.VARARG_NEEDSARG) == 0)) return 0; - b--; - if (b == Lua.LUA_MULTRET) - if (!(checkopenop(pt, pc))) return 0; - if (!checkreg(pt, a + b - 1)) return 0; - break; - } - default: - break; - } - } - return pt.code[last]; + /* + ** try to find last instruction before 'lastpc' that modified register 'reg' + */ + static int findsetreg (Prototype p, int lastpc, int reg) { + int pc; + int setreg = -1; /* keep last instruction that changed 'reg' */ + for (pc = 0; pc < lastpc; pc++) { + int i = p.code[pc]; + int op = Lua.GET_OPCODE(i); + int a = Lua.GETARG_A(i); + switch (op) { + case Lua.OP_LOADNIL: { + int b = Lua.GETARG_B(i); + if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */ + setreg = pc; + break; + } + case Lua.OP_TFORCALL: { + if (reg >= a + 2) setreg = pc; /* affect all regs above its base */ + break; + } + case Lua.OP_CALL: + case Lua.OP_TAILCALL: { + if (reg >= a) setreg = pc; /* affect all registers above base */ + break; + } + case Lua.OP_JMP: { + int b = Lua.GETARG_sBx(i); + int dest = pc + 1 + b; + /* jump is forward and do not skip `lastpc'? */ + if (pc < dest && dest <= lastpc) + pc += b; /* do the jump */ + break; + } + case Lua.OP_TEST: { + if (reg == a) setreg = pc; /* jumped code can change 'a' */ + break; + } + default: + if (Lua.testAMode(op) && reg == a) /* any instruction that set A */ + setreg = pc; + break; + } + } + return setreg; } - + } diff --git a/src/jse/org/luaj/vm2/luajc/JavaBuilder.java b/src/jse/org/luaj/vm2/luajc/JavaBuilder.java index 2a79bb6e..c11cb9ed 100644 --- a/src/jse/org/luaj/vm2/luajc/JavaBuilder.java +++ b/src/jse/org/luaj/vm2/luajc/JavaBuilder.java @@ -196,7 +196,7 @@ public class JavaBuilder { main = new InstructionList(); // create the fields - for ( int i=0; i=) R(A)*/ - break; - case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ { Prototype newp = p.p[bx]; String protoname = closureName(classname, bx); - int nup = newp.nups; + int nup = newp.upvalues.length; builder.closureCreate( protoname ); if ( nup > 0 ) builder.dup(); diff --git a/src/jse/org/luaj/vm2/luajc/ProtoInfo.java b/src/jse/org/luaj/vm2/luajc/ProtoInfo.java index c33c92d0..b890ed1a 100644 --- a/src/jse/org/luaj/vm2/luajc/ProtoInfo.java +++ b/src/jse/org/luaj/vm2/luajc/ProtoInfo.java @@ -164,7 +164,7 @@ public class ProtoInfo { case Lua.OP_LOADK:/* A Bx R(A) := Kst(Bx) */ case Lua.OP_LOADBOOL:/* A B C R(A) := (Bool)B; if (C) pc++ */ case Lua.OP_GETUPVAL: /* A B R(A) := UpValue[B] */ - case Lua.OP_GETGLOBAL: /* A Bx R(A) := Gbl[Kst(Bx)] */ + case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */ case Lua.OP_NEWTABLE: /* A B C R(A) := {} (size = B,C) */ a = Lua.GETARG_A( ins ); v[a][pc] = new VarInfo(a,pc); @@ -203,6 +203,13 @@ public class ProtoInfo { if (!Lua.ISK(b)) v[b][pc].isreferenced = true; if (!Lua.ISK(c)) v[c][pc].isreferenced = true; break; + + case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */ + b = Lua.GETARG_B( ins ); + c = Lua.GETARG_C( ins ); + if (!Lua.ISK(b)) v[b][pc].isreferenced = true; + if (!Lua.ISK(c)) v[c][pc].isreferenced = true; + break; case Lua.OP_CONCAT: /* A B C R(A) := R(B).. ... ..R(C) */ a = Lua.GETARG_A( ins ); @@ -295,6 +302,7 @@ public class ProtoInfo { v[a+i][pc].isreferenced = true; break; + case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */ case Lua.OP_TFORLOOP: /* 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++ */ a = Lua.GETARG_A( ins ); @@ -311,7 +319,7 @@ public class ProtoInfo { case Lua.OP_CLOSURE: /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ a = Lua.GETARG_A( ins ); b = Lua.GETARG_Bx( ins ); - nups = prototype.p[b].nups; + nups = prototype.p[b].upvalues.length; for ( int k=1; k<=nups; ++k ) { int i = prototype.code[pc+k]; if ( (i&4) == 0 ) { @@ -324,12 +332,7 @@ public class ProtoInfo { propogateVars( v, pc, pc+k ); pc += nups; break; - case Lua.OP_CLOSE: /* A close all variables in the stack up to (>=) R(A)*/ - a = Lua.GETARG_A( ins ); - for ( ; a C) then pc++ */ a = Lua.GETARG_A( ins ); @@ -402,9 +404,9 @@ public class ProtoInfo { if ( Lua.GET_OPCODE(code[pc]) == Lua.OP_CLOSURE ) { int bx = Lua.GETARG_Bx(code[pc]); Prototype newp = prototype.p[bx]; - UpvalInfo[] newu = newp.nups>0? new UpvalInfo[newp.nups]: null; + UpvalInfo[] newu = new UpvalInfo[newp.upvalues.length]; String newname = name + "$" + bx; - for ( int j=0; j