diff --git a/.classpath b/.classpath index 4cfd46e4..7b5ef63e 100644 --- a/.classpath +++ b/.classpath @@ -15,5 +15,6 @@ + diff --git a/src/core/org/luaj/vm2/LoadState.java b/src/core/org/luaj/vm2/LoadState.java index 33577ba5..7cb03cf2 100644 --- a/src/core/org/luaj/vm2/LoadState.java +++ b/src/core/org/luaj/vm2/LoadState.java @@ -39,8 +39,7 @@ public class LoadState { /** format corresponding to number-patched lua, all numbers are 32-bit (4 byte) ints */ public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4; - // type constants - + // type constants public static final int LUA_TINT = (-2); public static final int LUA_TNONE = (-1); public static final int LUA_TNIL = 0; @@ -56,7 +55,10 @@ public class LoadState { /** Interface for the compiler, if it is installed. */ public interface LuaCompiler { + /** Compile into a prototype, without taking the additional step of create a LuaFunction or LuaClosure */ public Prototype compile(int firstByte, InputStream stream, String name) throws IOException; + /** Load into a Closure or LuaFunction, with the supplied initial environment */ + public LuaFunction load(int firstByte, InputStream stream, String name, LuaValue env) throws IOException; } /** Compiler instance, if installed */ @@ -266,12 +268,11 @@ public class LoadState { luacNumberFormat = is.readByte(); } - public static Prototype undump( InputStream stream, String name ) throws IOException { - // check first byte to see if its a precompiled chunk + public static LuaFunction load( InputStream stream, String name, LuaValue env ) throws IOException { int c = stream.read(); if ( c != LUA_SIGNATURE[0] ) { if ( compiler != null ) - return compiler.compile(c, stream, name); + return compiler.load(c, stream, name, env); throw new LuaError("no compiler"); } @@ -296,7 +297,8 @@ public class LoadState { throw new LuaError("unsupported int size"); } - return s.loadFunction( LuaString.valueOf(sname) ); + Prototype p = s.loadFunction( LuaString.valueOf(sname) ); + return new LuaClosure( p, env ); } public static String getSourceName(String name) { diff --git a/src/core/org/luaj/vm2/LuaValue.java b/src/core/org/luaj/vm2/LuaValue.java index efa1d2bc..74e53630 100644 --- a/src/core/org/luaj/vm2/LuaValue.java +++ b/src/core/org/luaj/vm2/LuaValue.java @@ -310,6 +310,7 @@ public class LuaValue extends Varargs { // table initializers public static LuaTable tableOf() { return new LuaTable(); } + public static LuaTable tableOf(int narray, int nhash) { return new LuaTable(narray, nhash); } public static LuaTable listOf(LuaValue[] unnamedValues) { return new LuaTable(null,unnamedValues,null); } public static LuaTable listOf(LuaValue[] unnamedValues,Varargs lastarg) { return new LuaTable(null,unnamedValues,lastarg); } public static LuaTable tableOf(LuaValue[] namedValues) { return new LuaTable(namedValues,null,null); } diff --git a/src/core/org/luaj/vm2/Print.java b/src/core/org/luaj/vm2/Print.java index 12762483..afdad44d 100644 --- a/src/core/org/luaj/vm2/Print.java +++ b/src/core/org/luaj/vm2/Print.java @@ -299,7 +299,11 @@ public class Print extends Lua { } } - public void printFunction(Prototype f, boolean full) { + public static void print(Prototype p) { + printFunction(p, true); + } + + public static void printFunction(Prototype f, boolean full) { int i, n = f.p.length; printHeader(f); printCode(f); diff --git a/src/core/org/luaj/vm2/compiler/LuaC.java b/src/core/org/luaj/vm2/compiler/LuaC.java index 4e9dbb64..05e2fec7 100644 --- a/src/core/org/luaj/vm2/compiler/LuaC.java +++ b/src/core/org/luaj/vm2/compiler/LuaC.java @@ -27,10 +27,12 @@ import java.util.Hashtable; import org.luaj.vm2.LocVars; import org.luaj.vm2.Lua; +import org.luaj.vm2.LuaClosure; import org.luaj.vm2.LuaError; -import org.luaj.vm2.Prototype; +import org.luaj.vm2.LuaFunction; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Prototype; import org.luaj.vm2.LoadState.LuaCompiler; @@ -158,8 +160,19 @@ public class LuaC extends Lua implements LuaCompiler { /** Utility method to invoke the compiler for an input stream */ - public static Prototype compile(InputStream is, String string) throws IOException { - return new LuaC().compile(is.read(), is, string); + public static Prototype compile(InputStream is, String name) throws IOException { + return new LuaC().compile(is.read(), is, name); + } + + /** Load into a Closure or LuaFunction, with the supplied initial environment */ + public static LuaFunction load(InputStream is, String name, LuaValue env) throws IOException { + return new LuaC().load(is.read(), is, name, env); + } + + /** Load into a Closure or LuaFunction, with the supplied initial environment */ + public LuaFunction load(int firstByte, InputStream stream, String name, LuaValue env) throws IOException { + Prototype p = compile(firstByte, stream, name); + return new LuaClosure( p, env ); } /** Compile source bytes into a LPrototype. diff --git a/src/core/org/luaj/vm2/lib/BaseLib.java b/src/core/org/luaj/vm2/lib/BaseLib.java index 0edf66e4..95349e91 100644 --- a/src/core/org/luaj/vm2/lib/BaseLib.java +++ b/src/core/org/luaj/vm2/lib/BaseLib.java @@ -222,8 +222,7 @@ public class BaseLib extends LuaTable implements ResourceFinder { try { LuaValue func = args.checkfunction(1); String chunkname = args.optString(2, "function"); - Prototype p = LoadState.undump(new StringInputStream(func), chunkname); - return new LuaClosure(p,LuaThread.getRunningEnv(env)); + return LoadState.load(new StringInputStream(func), chunkname, LuaThread.getRunningEnv(env)); } catch ( Exception e ) { return varargsOf(NIL, valueOf(e.getMessage())); } @@ -240,8 +239,7 @@ public class BaseLib extends LuaTable implements ResourceFinder { try { LuaString script = args.checkstring(1); LuaString chunkname = args.optstring(2, script); - Prototype p = LoadState.undump(script.toInputStream(), chunkname.toString()); - return new LuaClosure(p,LuaThread.getRunningEnv(env)); + return LoadState.load(script.toInputStream(), chunkname.toString(),LuaThread.getRunningEnv(env)); } catch ( Exception e ) { return varargsOf(NIL, valueOf(e.getMessage())); } @@ -344,8 +342,7 @@ public class BaseLib extends LuaTable implements ResourceFinder { if ( is == null ) return varargsOf(NIL, valueOf("not found: "+filename)); try { - Prototype p = LoadState.undump(is, filename); - return new LuaClosure(p,LuaThread.getRunningEnv(env)); + return LoadState.load(is, filename, LuaThread.getRunningEnv(env)); } finally { is.close(); } diff --git a/src/jse/lua.java b/src/jse/lua.java index c7df8d17..5de9364a 100644 --- a/src/jse/lua.java +++ b/src/jse/lua.java @@ -29,9 +29,8 @@ import java.io.InputStreamReader; import org.luaj.vm2.LoadState; import org.luaj.vm2.Lua; -import org.luaj.vm2.LuaClosure; +import org.luaj.vm2.LuaFunction; import org.luaj.vm2.LuaValue; -import org.luaj.vm2.Prototype; import org.luaj.vm2.compiler.LuaC; import org.luaj.vm2.lib.DebugLib; import org.luaj.vm2.lib.JsePlatform; @@ -167,10 +166,9 @@ public class lua { private static void processScript( InputStream script, String chunkname, String[] args, int offset ) throws IOException { try { - LuaClosure c; + LuaFunction c; try { - Prototype p = LoadState.undump(script, chunkname ); - c = new LuaClosure(p,_G); + c = LoadState.load(script, chunkname, _G); } finally { script.close(); } diff --git a/src/jse/org/luaj/vm2/luajc/JavaBytecodeCompiler.java b/src/jse/org/luaj/vm2/luajc/JavaBytecodeCompiler.java new file mode 100644 index 00000000..b3ff682f --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/JavaBytecodeCompiler.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2009 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.vm2.luajc; + +import java.io.IOException; +import java.io.InputStream; + +import org.luaj.vm2.LoadState; +import org.luaj.vm2.LuaClosure; +import org.luaj.vm2.LuaFunction; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.LoadState.LuaCompiler; +import org.luaj.vm2.compiler.LuaC; + +public class JavaBytecodeCompiler implements LuaCompiler { + + private static JavaBytecodeCompiler instance; + private JavaBytecodeGenerator gen; + private LuaC luac; + + public static JavaBytecodeCompiler getInstance() { + if ( instance == null ) + instance = new JavaBytecodeCompiler(); + return instance; + } + + /** + * Install the compiler as the main compiler to use. + * Will fall back to the LuaC prototype compiler. + */ + public static final void install() { + LoadState.compiler = getInstance(); + } + + private JavaBytecodeCompiler() { + luac = new LuaC(); + gen = new JavaBytecodeGenerator(); + } + + /** Compile into protoype form. */ + public Prototype compile(int firstByte, InputStream stream, String name) throws IOException { + return luac.compile(firstByte, stream, name); + } + + /** Compile into class form. */ + public LuaFunction load(int firstByte, InputStream stream, String name, LuaValue env) throws IOException { + Prototype p = compile( firstByte, stream, name); + try { + System.out.println("compiling "+name); + Class c = gen.toJavaBytecode(p, name); + Object o = c.newInstance(); + System.out.println("instance is: "+o); + LuaFunction f = (LuaFunction) o; + f.setfenv(env); + return f; + } catch ( Throwable t ) { + t.printStackTrace(); + return new LuaClosure( p, env ); + } + + } + +} diff --git a/src/jse/org/luaj/vm2/luajc/JavaBytecodeGenerator.java b/src/jse/org/luaj/vm2/luajc/JavaBytecodeGenerator.java new file mode 100644 index 00000000..904b9a05 --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/JavaBytecodeGenerator.java @@ -0,0 +1,1051 @@ +/******************************************************************************* + * Copyright (c) 2009 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.vm2.luajc; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.apache.bcel.Constants; +import org.apache.bcel.classfile.Field; +import org.apache.bcel.generic.AASTORE; +import org.apache.bcel.generic.ALOAD; +import org.apache.bcel.generic.ANEWARRAY; +import org.apache.bcel.generic.ASTORE; +import org.apache.bcel.generic.ArrayType; +import org.apache.bcel.generic.BranchInstruction; +import org.apache.bcel.generic.ClassGen; +import org.apache.bcel.generic.ConstantPoolGen; +import org.apache.bcel.generic.FieldGen; +import org.apache.bcel.generic.GOTO; +import org.apache.bcel.generic.IFEQ; +import org.apache.bcel.generic.IFNE; +import org.apache.bcel.generic.InstructionConstants; +import org.apache.bcel.generic.InstructionFactory; +import org.apache.bcel.generic.InstructionHandle; +import org.apache.bcel.generic.InstructionList; +import org.apache.bcel.generic.LocalVariableGen; +import org.apache.bcel.generic.MethodGen; +import org.apache.bcel.generic.ObjectType; +import org.apache.bcel.generic.PUSH; +import org.apache.bcel.generic.Type; +import org.luaj.vm2.Buffer; +import org.luaj.vm2.Lua; +import org.luaj.vm2.LuaBoolean; +import org.luaj.vm2.LuaDouble; +import org.luaj.vm2.LuaInteger; +import org.luaj.vm2.LuaNumber; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.Varargs; +import org.luaj.vm2.lib.VarArgFunction; + +import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable; + + +public class JavaBytecodeGenerator { + public static boolean DUMPCLASSES = "true".equals(System.getProperty("DUMPCLASSES")); + + private static final String STR_FUNCV = VarArgFunction.class.getName(); + private static final String STR_VARARGS = Varargs.class.getName(); + private static final String STR_LUAVALUE = LuaValue.class.getName(); + private static final String STR_LUASTRING = LuaString.class.getName(); + private static final String STR_LUAINTEGER = LuaInteger.class.getName(); + private static final String STR_LUADOUBLE = LuaDouble.class.getName(); + private static final String STR_LUANUMBER = LuaNumber.class.getName(); + private static final String STR_LUABOOLEAN = LuaBoolean.class.getName(); + private static final String STR_LUATABLE = LuaTable.class.getName(); + private static final String STR_BUFFER = Buffer.class.getName(); + + private static final ObjectType TYPE_VARARGS = new ObjectType(STR_VARARGS); + private static final ObjectType TYPE_LUAVALUE = new ObjectType(STR_LUAVALUE); + private static final ObjectType TYPE_LUASTRING = new ObjectType(STR_LUASTRING); + private static final ObjectType TYPE_LUAINTEGER = new ObjectType(STR_LUAINTEGER); + private static final ObjectType TYPE_LUADOUBLE = new ObjectType(STR_LUADOUBLE); + private static final ObjectType TYPE_LUANUMBER = new ObjectType(STR_LUANUMBER); + private static final ObjectType TYPE_LUABOOLEAN = new ObjectType(STR_LUABOOLEAN); + private static final ObjectType TYPE_LUATABLE = new ObjectType(STR_LUATABLE); + private static final ObjectType TYPE_BUFFER = new ObjectType(STR_BUFFER); + + private static final ArrayType TYPE_LOCALUPVALUE = new ArrayType( TYPE_LUAVALUE, 1 ); + + private static final Class[] NO_INNER_CLASSES = {}; + + private static String toLegalLocalName(String string) { + String better = string.replaceAll("[^\\w]", "_"); + return string.equals(better)? string: "$"+better; + } + + /** + * Turn a lua prototype into a Java class using the Bcel Java Bytecode Generator + * @param p Prototype to encode as java, must not have upvalues itself. + * @param name String name to apply, without extensions + * @return Java Class that extends LuaValue that can be instatiated via newInstance + * @throws Exception + */ + public static Class toJavaBytecode( Prototype p, String classname ) throws Exception { + return new JavaBytecodeGenerator().loadPrototype( p, classname ); + } + + private final Hashtable prototypes; + private final ClassLoader classLoader; + + JavaBytecodeGenerator() { + // load the file + prototypes = new Hashtable(); + classLoader = new ClassLoader() { + public Class findClass(String name) throws ClassNotFoundException { + Object o = prototypes.get(name); + if ( o instanceof Prototype) { + try { + byte[] b = generateBytecode( (Prototype) o, name ); + prototypes.put(name, Boolean.TRUE); + return defineClass(name, b, 0, b.length); + } catch (IOException e) { + e.printStackTrace(); + } + } + return super.findClass(name); + } + + }; + } + + private Class loadPrototype(Prototype p, String classname) throws ClassNotFoundException { + indexPrototype( p, classname ); + return classLoader.loadClass(classname); + } + + private void indexPrototype(Prototype p, String name) { + if ( prototypes.containsKey(name) ) + return; + prototypes.put(name, p); + Class[] inners = (p.p!=null && p.p.length>0? new Class[p.p.length]: NO_INNER_CLASSES); + for ( int i=0, n=inners.length; i> 6) & 0xff; + } + private static final int B(int i) { + return i >>> 23; + } + private static final int Bx(int i) { + return i >>> 14; + } + private static final int C(int i) { + return (i >> 14) & 0x1ff; + } + + private byte[] generateBytecode(Prototype p, String CLASSNAME) + throws IOException { + + // compile our class next + ClassGen cg = new ClassGen(CLASSNAME, STR_FUNCV, "tryit.lua", + Constants.ACC_PUBLIC | Constants.ACC_SUPER, null); + ConstantPoolGen cp = cg.getConstantPool(); // cg creates constant pool + + // add static constants + int nk = p.k.length; + Field[] k = new Field[nk]; + for (int i = 0; i < nk; i++) { + FieldGen fg = new FieldGen(Constants.ACC_STATIC + | Constants.ACC_FINAL, // access + TYPE_LUAVALUE, // type + "k" + (i + 1), // name + cp); + k[i] = fg.getField(); + cg.addField(k[i]); + } + + // static initializer for static constants + { + InstructionList il = new InstructionList(); + MethodGen mg = new MethodGen(Constants.ACC_STATIC, Type.VOID, + new Type[] {}, new String[] {}, "", cg + .getClassName(), il, cg.getConstantPool()); + InstructionFactory factory = new InstructionFactory(cg); + + // initialze the constants + for (int i = 0; i < nk; i++) { + LuaValue ki = p.k[i]; + switch (ki.type()) { + case LuaValue.TNIL: + il.append(factory.createFieldAccess(STR_LUAVALUE, "NIL", + TYPE_LUAVALUE, Constants.GETSTATIC)); + il.append(factory.createPutStatic(CLASSNAME, + k[i].getName(), k[i].getType())); + break; + case LuaValue.TBOOLEAN: + String b = ki.toboolean() ? "TRUE" : "FALSE"; + il.append(factory.createFieldAccess(STR_LUAVALUE, b, + TYPE_LUABOOLEAN, Constants.GETSTATIC)); + il.append(factory.createPutStatic(CLASSNAME, + k[i].getName(), k[i].getType())); + break; + case LuaValue.TSTRING: + il.append(new PUSH(cp, ki.toString())); + il.append(factory.createInvoke(STR_LUASTRING, "valueOf", + TYPE_LUASTRING, new Type[] { Type.STRING }, + Constants.INVOKESTATIC)); + il.append(factory.createPutStatic(CLASSNAME, + k[i].getName(), k[i].getType())); + break; + case LuaValue.TNUMBER: + if (ki.isinttype()) { + il.append(new PUSH(cp, ki.toint())); + il.append(factory + .createInvoke(STR_LUAINTEGER, "valueOf", + TYPE_LUAINTEGER, + new Type[] { Type.INT }, + Constants.INVOKESTATIC)); + il.append(factory.createPutStatic(CLASSNAME, k[i] + .getName(), k[i].getType())); + } else { + il.append(new PUSH(cp, ki.todouble())); + il.append(factory.createInvoke(STR_LUADOUBLE, + "valueOf", TYPE_LUANUMBER, + new Type[] { Type.DOUBLE }, + Constants.INVOKESTATIC)); + il.append(factory.createPutStatic(CLASSNAME, k[i] + .getName(), k[i].getType())); + } + break; + default: + throw new RuntimeException("illegal constant type: " + + ki.type()); + } + } + + il.append(InstructionConstants.RETURN); + mg.setMaxStack(); + cg.addMethod(mg.getMethod()); + il.dispose(); // Allow instruction handles to be reused + } + + // upvalues are fields + int nu = p.upvalues.length; + Field[] u = new Field[nu]; + for (int i = 0; i < nu; i++) { + String name = getUpvalueName(p.upvalues, i); + FieldGen fg = new FieldGen(Constants.ACC_PUBLIC, // | ACC_FINAL, // + // access + TYPE_LOCALUPVALUE, // type + name, // name + cp); + u[i] = fg.getField(); + cg.addField(u[i]); + } + + // implement LuaValue.invoke(Varargs args) + InstructionList il = new InstructionList(); + MethodGen mg = new MethodGen( + Constants.ACC_PUBLIC | Constants.ACC_FINAL, // access flags + TYPE_VARARGS, // return type + new Type[] { TYPE_VARARGS }, // argument types + new String[] { "args" }, // arg names + "invoke", STR_LUAVALUE, // method, class + il, cp); + InstructionFactory factory = new InstructionFactory(cg); + + // common iterators used for code + int[] code = p.code; + int nc = code.length; + int pc, i, a, b, c; + + // create locals + int np = p.numparams; + int nl = p.maxstacksize; + LocalVariableGen[] locals = new LocalVariableGen[nl]; + + // find upvalues + boolean isup[] = new boolean[nl]; + for (pc = 0; pc < nc; pc++) { + if (OP(code[pc]) == Lua.OP_CLOSURE) { + b = Bx(code[pc]); + Prototype newp = p.p[b]; + for (int j = 0, nup = newp.nups; j < nup; ++j) { + i = code[++pc]; + if ((i & 4) == 0) + isup[B(i)] = true; + } + } + } + + // initialize locals + LocalVariableGen nil = null; + for (int j = 0; j < nl; j++) { + + String name = j < p.locvars.length && p.locvars[j].varname != null ? + toLegalLocalName(p.locvars[j].varname.toString()): + "r" + j; + + locals[j] = mg.addLocalVariable(name, isup[j] ? TYPE_LOCALUPVALUE + : TYPE_LUAVALUE, null, null); + + if (isup[j]) { // upvalue storage + il.append(new PUSH(cp, 1)); + il.append(new ANEWARRAY(cp.addClass(STR_LUAVALUE))); + il.append(InstructionConstants.DUP); + il.append(new ASTORE(locals[j].getIndex())); + il.append(new PUSH(cp, 0)); + // leave array & index 0 on stack + } + // put initial value onto stack + if (j < np) { + il.append(new ALOAD(1)); + il.append(new PUSH(cp, j + 1)); + il.append(factory.createInvoke(STR_VARARGS, "arg", + TYPE_LUAVALUE, new Type[] { Type.INT }, + Constants.INVOKEVIRTUAL)); + } else { + nil = il_initLocalNil(mg, factory, il, nil); + il.append(new ALOAD(nil.getIndex())); + } + if (isup[j]) { // upvalue local, array is already on stack + il.append(InstructionConstants.AASTORE); + } else { // plain local + il.append(new ASTORE(locals[j].getIndex())); + } + } + + // trim varargs to those that are in excess of what is needed + if ((p.is_vararg & Lua.VARARG_ISVARARG) != 0 && np > 0) { + il.append(new ALOAD(1)); + il.append(new PUSH(cp, 1 + np)); + il.append(factory.createInvoke(STR_VARARGS, "subargs", + TYPE_VARARGS, new Type[] { Type.INT }, + Constants.INVOKEVIRTUAL)); + il.append(new ASTORE(1)); + } + + // this local variable stores varargs, + // and is only created by functions that need it. + // TODO: only include if necessary + LocalVariableGen v = null; + int vbase = 0; + v = mg.addLocalVariable("v", TYPE_VARARGS, null, null); + il.append(factory.createFieldAccess(STR_LUAVALUE, "NONE", + TYPE_LUAVALUE, Constants.GETSTATIC)); + il.append(new ASTORE(v.getIndex())); + + // storage for goto locations + int[] targets = new int[nc]; + BranchInstruction[] branches = new BranchInstruction[nc]; + InstructionHandle[] ih = new InstructionHandle[nc]; + + // each lua bytecode gets some java bytecodes + for (pc = 0; pc < nc; pc++) { + + // pull out instruction + i = code[pc]; + a = A(i); + + // process the op code + switch ( OP(i) ) { + + case Lua.OP_MOVE:/* A B R(A):= R(B) */ + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[B(i)])); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + + case Lua.OP_LOADK:/* A Bx R(A):= Kst(Bx) */ + ih[pc] = + il.append(factory.createFieldAccess(CLASSNAME, k[Bx(i)].getName(), TYPE_LUAVALUE, Constants.GETSTATIC)); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + + case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */ + //stack[a] = (B(i)!=0)? LuaValue.TRUE: LuaValue.FALSE; + ih[pc] = + il.append(factory.createFieldAccess(STR_LUAVALUE, ((B(i)!=0)? "TRUE": "FALSE"), TYPE_LUABOOLEAN, Constants.GETSTATIC)); + il_append_new_ASTORE(cp,il, (locals[a])); + if (C(i) != 0) { + // pc++; /* skip next instruction (if C) */ + branches[pc] = new GOTO(null); + targets[pc] = pc + 2; + } + break; + + case Lua.OP_LOADNIL: /* A B R(A):= ...:= R(B):= nil */ + ih[pc] = + il.append(factory.createFieldAccess(STR_LUAVALUE, "NIL", TYPE_LUAVALUE, Constants.GETSTATIC)); + for ( b=B(i); a<=b; ) { + il.append(InstructionConstants.DUP); + il_append_new_ASTORE(cp,il, (locals[a++])); + } + il.append(InstructionConstants.POP); + break; + + case Lua.OP_GETUPVAL: /* A B R(A):= UpValue[B] */ + ih[pc] = + il.append(InstructionConstants.THIS); + il.append(factory.createFieldAccess(CLASSNAME, u[B(i)].getName(), TYPE_LOCALUPVALUE, Constants.GETFIELD)); + il.append(new PUSH(cp,0)); + il.append(InstructionConstants.AALOAD); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + + case Lua.OP_GETGLOBAL: /* A Bx R(A):= Gbl[Kst(Bx)] */ + ih[pc] = + il.append(InstructionConstants.THIS); + il.append(factory.createFieldAccess(CLASSNAME, "env", TYPE_LUAVALUE, Constants.GETFIELD)); + il.append(factory.createFieldAccess(CLASSNAME, k[Bx(i)].getName(), TYPE_LUAVALUE, Constants.GETSTATIC)); + il.append(factory.createInvoke(STR_LUAVALUE, "get", TYPE_LUAVALUE, new Type[] { TYPE_LUAVALUE }, Constants.INVOKEVIRTUAL)); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + + case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */ + // stack[a] = stack[B(i)].get((c=C(i))>0xff? k[c&0x0ff]: stack[c]); + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[B(i)])); + if ((c=C(i))>0xff) + il.append(factory.createFieldAccess(CLASSNAME, k[c&0x0ff].getName(), TYPE_LUAVALUE, Constants.GETSTATIC)); + else + il_append_new_ALOAD(cp,il, (locals[c])); + il.append(factory.createInvoke(STR_LUAVALUE, "get", TYPE_LUAVALUE, new Type[] { TYPE_LUAVALUE }, Constants.INVOKEVIRTUAL)); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + + case Lua.OP_SETGLOBAL: /* A Bx Gbl[Kst(Bx)]:= R(A) */ + // env.set(k[Bx(i)], stack[a]); + ih[pc] = + il.append(InstructionConstants.THIS); + il.append(factory.createFieldAccess(CLASSNAME, "env", TYPE_LUAVALUE, Constants.GETFIELD)); + il.append(factory.createFieldAccess(CLASSNAME, k[Bx(i)].getName(), TYPE_LUAVALUE, Constants.GETSTATIC)); + il_append_new_ALOAD(cp,il, (locals[a])); + il.append(factory.createInvoke(STR_LUAVALUE, "set", Type.VOID, new Type[] { TYPE_LUAVALUE, TYPE_LUAVALUE }, Constants.INVOKEVIRTUAL)); + break; + + case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */ + // upValues[B(i)].setValue(stack[a]); + ih[pc] = + il.append(InstructionConstants.THIS); + il.append(factory.createFieldAccess(CLASSNAME, u[B(i)].getName(), TYPE_LOCALUPVALUE, Constants.GETFIELD)); + il.append(new PUSH(cp,0)); + il_append_new_ALOAD(cp,il, (locals[a])); + il.append(InstructionConstants.AASTORE); + break; + + case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */ + // stack[a].set(((b=B(i))>0xff? k[b&0x0ff]: stack[b]), (c=C(i))>0xff? k[c&0x0ff]: stack[c]); + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[a])); + if ((b=B(i))>0xff) + il.append(factory.createFieldAccess(CLASSNAME, k[b&0x0ff].getName(), TYPE_LUAVALUE, Constants.GETSTATIC)); + else + il_append_new_ALOAD(cp,il, (locals[b])); + if ((c=C(i))>0xff) + il.append(factory.createFieldAccess(CLASSNAME, k[c&0x0ff].getName(), TYPE_LUAVALUE, Constants.GETSTATIC)); + else + il_append_new_ALOAD(cp,il, (locals[c])); + il.append(factory.createInvoke(STR_LUAVALUE, "set", Type.VOID, new Type[] { TYPE_LUAVALUE, TYPE_LUAVALUE }, Constants.INVOKEVIRTUAL)); + break; + + case Lua.OP_NEWTABLE: /* A B C R(A):= {} (size = B,C) */ + // stack[a] = new LuaTable(B(i),C(i)); + ih[pc] = + il.append(new PUSH(cp, B(i))); + il.append(new PUSH(cp, C(i))); + il.append(factory.createInvoke(STR_LUAVALUE, "tableOf", TYPE_LUATABLE, new Type[] { Type.INT, Type.INT }, Constants.INVOKESTATIC)); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + + case Lua.OP_SELF: /* A B C R(A+1):= R(B): R(A):= R(B)[RK(C)] */ + // stack[a+1] = (o = stack[B(i)]); + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[B(i)])); + il_append_new_ASTORE(cp,il, (locals[a+1])); + // stack[a] = o.get((c=C(i))>0xff? k[c&0x0ff]: stack[c]); + if ((c=C(i))>0xff) + il.append(factory.createFieldAccess(CLASSNAME, k[c&0x0ff].getName(), TYPE_LUAVALUE, Constants.GETSTATIC)); + else + il_append_new_ALOAD(cp,il, (locals[c])); + il.append(factory.createInvoke(STR_LUAVALUE, "get", TYPE_LUAVALUE, new Type[] { TYPE_LUAVALUE }, Constants.INVOKEVIRTUAL)); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + + case Lua.OP_ADD: /* A B C R(A):= RK(B) + RK(C) */ + case Lua.OP_SUB: /* A B C R(A):= RK(B) - RK(C) */ + case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */ + case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */ + case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */ + case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */ + { + String op; + switch (OP(i)) { + default: + case Lua.OP_ADD: op = "add"; break; + case Lua.OP_SUB: op = "sub"; break; + case Lua.OP_MUL: op = "mul"; break; + case Lua.OP_DIV: op = "div"; break; + case Lua.OP_MOD: op = "mod"; break; + case Lua.OP_POW: op = "pow"; break; + } + // stack[a] = ((b=B(i))>0xff? k[b&0x0ff]: stack[b]).add((c=C(i))>0xff? k[c&0x0ff]: stack[c]); + if ((b=B(i))>0xff) + ih[pc] = + il.append(factory.createFieldAccess(CLASSNAME, k[b&0x0ff].getName(), TYPE_LUAVALUE, Constants.GETSTATIC)); + else + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[b])); + if ((c=C(i))>0xff) + il.append(factory.createFieldAccess(CLASSNAME, k[c&0x0ff].getName(), TYPE_LUAVALUE, Constants.GETSTATIC)); + else + il_append_new_ALOAD(cp,il, (locals[c])); + il.append(factory.createInvoke(STR_LUAVALUE, op, TYPE_LUAVALUE, new Type[] { TYPE_LUAVALUE }, Constants.INVOKEVIRTUAL)); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + } + case Lua.OP_UNM: /* A B R(A):= -R(B) */ + // stack[a] = stack[B(i)].neg(); + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[B(i)])); + il.append(factory.createInvoke(STR_LUAVALUE, "neg", TYPE_LUAVALUE, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + + case Lua.OP_NOT: /* A B R(A):= not R(B) */ + // stack[a] = stack[B(i)].not(); + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[B(i)])); + il.append(factory.createInvoke(STR_LUAVALUE, "not", TYPE_LUAVALUE, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + + case Lua.OP_LEN: /* A B R(A):= length of R(B) */ + // stack[a] = stack[B(i)].len(); + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[B(i)])); + il.append(factory.createInvoke(STR_LUAVALUE, "len", TYPE_LUAVALUE, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + + case Lua.OP_CONCAT: /* A B C R(A):= R(B).. ... ..R(C) */ + { + b = B(i); + c = C(i); + + // Buffer sb = new Buffer(); + ih[pc] = + il.append(factory.createNew(TYPE_BUFFER)); + il.append(InstructionConstants.DUP); + il.append(factory.createInvoke(STR_BUFFER, "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); + + // concatenate + for ( ; b<=c; ) { + // sb.append( stack[b++].checkstring() ); + il.append(InstructionConstants.DUP); + il_append_new_ALOAD(cp,il, (locals[b++])); + il.append(factory.createInvoke(STR_LUAVALUE, "checkstring", TYPE_LUASTRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + il.append(factory.createInvoke(STR_BUFFER, "append", Type.VOID, new Type[] { TYPE_LUASTRING }, Constants.INVOKEVIRTUAL)); + } + + // store + // stack[a] = sb.tostrvalue(); + il.append(factory.createInvoke(STR_BUFFER, "tostrvalue", TYPE_LUASTRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + il_append_new_ASTORE(cp,il, (locals[a])); + } + break; + + case Lua.OP_JMP: /* sBx pc+=sBx */ + // pc += (Bx(i))-0x1ffff; + branches[pc] = new GOTO(null); + targets[pc] = pc + 1 + (Bx(i))-0x1ffff; + ih[pc] = + il.append(branches[pc]); + break; + + case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ + case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ + case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ + { + String op; + switch (OP(i)) { + default: + case Lua.OP_EQ: op = "eq_b"; break; + case Lua.OP_LT: op = "lt_b"; break; + case Lua.OP_LE: op = "lteq_b"; break; + } + if ((b=B(i))>0xff) + ih[pc] = + il.append(factory.createFieldAccess(CLASSNAME, k[b&0x0ff].getName(), TYPE_LUAVALUE, Constants.GETSTATIC)); + else + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[b])); + if ((c=C(i))>0xff) + il.append(factory.createFieldAccess(CLASSNAME, k[c&0x0ff].getName(), TYPE_LUAVALUE, Constants.GETSTATIC)); + else + il_append_new_ALOAD(cp,il, (locals[c])); + il.append(factory.createInvoke(STR_LUAVALUE, op, Type.BOOLEAN, new Type[] { TYPE_LUAVALUE }, Constants.INVOKEVIRTUAL)); + branches[pc] = (a!=0)? new IFEQ(null): new IFNE(null); + targets[pc] = pc + 2; + il.append(branches[pc]); + break; + } + case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */ + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[a])); + il.append(factory.createInvoke(STR_LUAVALUE, "toboolean", Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + branches[pc] = (C(i)!=0)? new IFEQ(null): new IFNE(null); + targets[pc] = pc + 2; + il.append(branches[pc]); + break; + + case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */ + /* note: doc appears to be reversed */ + //if ( (o=stack[B(i)]).toboolean() != (C(i)!=0) ) + // ++pc; + //else + // stack[a] = o; // TODO: should be sBx? + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[B(i)])); + il.append(factory.createInvoke(STR_LUAVALUE, "toboolean", Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + branches[pc] = (C(i)!=0)? new IFEQ(null): new IFNE(null); + targets[pc] = pc + 2; + il.append(branches[pc]); + il_append_new_ALOAD(cp,il, (locals[B(i)])); + il_append_new_ASTORE(cp,il, (locals[a])); + break; + + case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */ + case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ + ih[pc] = + il_append_new_ALOAD(cp,il, (locals[a])); + b = B(i); + c = C(i); + switch ( b ) { + case 1: // noargs + il.append(factory.createFieldAccess(STR_LUAVALUE, "NONE", TYPE_LUAVALUE, Constants.GETSTATIC)); + break; + case 2: // one arg + il_append_new_ALOAD(cp,il, (locals[a+1])); + break; + case 3: // two args + il_append_new_ALOAD(cp,il, (locals[a+1])); + il_append_new_ALOAD(cp,il, (locals[a+2])); + il.append(factory.createInvoke(STR_LUAVALUE, "varargsOf", TYPE_VARARGS, new Type[] { TYPE_LUAVALUE, TYPE_VARARGS }, Constants.INVOKESTATIC)); + break; + case 4: // three args + il_append_new_ALOAD(cp,il, (locals[a+1])); + il_append_new_ALOAD(cp,il, (locals[a+2])); + il_append_new_ALOAD(cp,il, (locals[a+3])); + il.append(factory.createInvoke(STR_LUAVALUE, "varargsOf", TYPE_VARARGS, new Type[] { TYPE_LUAVALUE, TYPE_LUAVALUE, TYPE_VARARGS }, Constants.INVOKESTATIC)); + break; + default: // fixed arg count + il.append(new PUSH(cp, b-1)); + il.append(new ANEWARRAY(cp.addClass(STR_LUAVALUE))); + for ( int j=0; j=) R(A)*/ + break; + + case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ + { + b = Bx(i); + Prototype newp = p.p[b]; + String protoname = CLASSNAME+"$"+b; + + // instantiate the class + ih[pc] = + il.append(factory.createNew(new ObjectType(protoname))); + il.append(InstructionConstants.DUP); + il.append(factory.createInvoke(protoname, "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); + + // set the environment + il.append(InstructionConstants.DUP); + il.append(InstructionConstants.THIS); + il.append(factory.createFieldAccess(CLASSNAME, "env", TYPE_LUAVALUE, Constants.GETFIELD)); + il.append(factory.createInvoke(STR_LUAVALUE, "setfenv", Type.VOID, new Type[] { TYPE_LUAVALUE }, Constants.INVOKEVIRTUAL)); + + // initialize upvalues of new instance + for ( int j=0, nup=newp.nups; j