org.luaj.vm2.luajc.JavaBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of luaj-jse Show documentation
Show all versions of luaj-jse Show documentation
Luaj 2.0 for the jse platform
/*******************************************************************************
* Copyright (c) 2010 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.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.bcel.Constants;
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.CompoundInstruction;
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.Instruction;
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.OneArgFunction;
import org.luaj.vm2.lib.ThreeArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.VarArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
public class JavaBuilder {
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 String STR_STRING = String.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 ArrayType TYPE_CHARARRAY = new ArrayType( Type.CHAR, 1 );
private static final Class[] NO_INNER_CLASSES = {};
private static final String STR_FUNCV = VarArgFunction.class.getName();
private static final String STR_FUNC0 = ZeroArgFunction.class.getName();
private static final String STR_FUNC1 = OneArgFunction.class.getName();
private static final String STR_FUNC2 = TwoArgFunction.class.getName();
private static final String STR_FUNC3 = ThreeArgFunction.class.getName();
// argument list types
private static final Type[] ARG_TYPES_NONE = {};
private static final Type[] ARG_TYPES_INT = { Type.INT };
private static final Type[] ARG_TYPES_DOUBLE = { Type.DOUBLE };
private static final Type[] ARG_TYPES_STRING = { Type.STRING };
private static final Type[] ARG_TYPES_CHARARRAY = { TYPE_CHARARRAY };
private static final Type[] ARG_TYPES_VARARGS_INT = { TYPE_VARARGS, Type.INT };
private static final Type[] ARG_TYPES_INT_LUAVALUE = { Type.INT, TYPE_LUAVALUE };
private static final Type[] ARG_TYPES_INT_VARARGS = { Type.INT, TYPE_VARARGS };
private static final Type[] ARG_TYPES_LUAVALUE_VARARGS = { TYPE_LUAVALUE, TYPE_VARARGS };
private static final Type[] ARG_TYPES_LUAVALUE_LUAVALUE_VARARGS = { TYPE_LUAVALUE, TYPE_LUAVALUE, TYPE_VARARGS };
private static final Type[] ARG_TYPES_LUAVALUEARRAY = { new ArrayType( TYPE_LUAVALUE, 1 ) };
private static final Type[] ARG_TYPES_LUAVALUEARRAY_VARARGS = { new ArrayType( TYPE_LUAVALUE, 1 ), TYPE_VARARGS };
private static final Type[] ARG_TYPES_LUAVALUE_LUAVALUE_LUAVALUE = { TYPE_LUAVALUE, TYPE_LUAVALUE, TYPE_LUAVALUE };
private static final Type[] ARG_TYPES_VARARGS = { TYPE_VARARGS };
private static final Type[] ARG_TYPES_LUAVALUE_LUAVALUE = { TYPE_LUAVALUE, TYPE_LUAVALUE };
private static final Type[] ARG_TYPES_INT_INT = { Type.INT, Type.INT };
private static final Type[] ARG_TYPES_LUAVALUE = { TYPE_LUAVALUE };
private static final Type[] ARG_TYPES_BUFFER = { TYPE_BUFFER };
// names, arg types for main prototype classes
private static final String[] SUPER_NAME_N = { STR_FUNC0, STR_FUNC1, STR_FUNC2, STR_FUNC3, STR_FUNCV, };
private static final ObjectType[] RETURN_TYPE_N = { TYPE_LUAVALUE, TYPE_LUAVALUE, TYPE_LUAVALUE, TYPE_LUAVALUE, TYPE_VARARGS, };
private static final Type[][] ARG_TYPES_N = { ARG_TYPES_NONE, ARG_TYPES_LUAVALUE, ARG_TYPES_LUAVALUE_LUAVALUE, ARG_TYPES_LUAVALUE_LUAVALUE_LUAVALUE, ARG_TYPES_VARARGS, };
private static final String[][] ARG_NAMES_N = { {}, {"arg"}, {"arg1","arg2"}, {"arg1","arg2","arg3"}, {"args"}, };
private static final String[] METH_NAME_N = { "call", "call", "call", "call", "onInvoke", };
// varable naming
private static final String PREFIX_CONSTANT = "k";
private static final String PREFIX_UPVALUE = "u";
private static final String PREFIX_PLAIN_SLOT = "s";
private static final String PREFIX_UPVALUE_SLOT = "a";
private static final String NAME_VARRESULT = "v";
// basic info
private final ProtoInfo pi;
private final Prototype p;
private final String classname;
// bcel variables
private final ClassGen cg;
private final ConstantPoolGen cp;
private final InstructionFactory factory;
// main instruction list for the main function of this class
private final InstructionList init;
private final InstructionList main;
private final MethodGen mg;
// the superclass arg count, 0-3 args, 4=varargs
private int superclassType;
private static int SUPERTYPE_VARARGS = 4;
// storage for goto locations
private final int[] targets;
private final BranchInstruction[] branches;
private final InstructionHandle[] branchDestHandles;
private InstructionHandle beginningOfLuaInstruction;
// hold vararg result
private LocalVariableGen varresult = null;
public JavaBuilder(ProtoInfo pi, String classname, String filename) {
this.pi = pi;
this.p = pi.prototype;
this.classname = classname;
// what class to inherit from
superclassType = p.numparams;
if ( p.is_vararg != 0 || superclassType >= SUPERTYPE_VARARGS )
superclassType = SUPERTYPE_VARARGS;
for ( int i=0, n=p.code.length; i 2)) ) {
superclassType = SUPERTYPE_VARARGS;
break;
}
}
// create class generator
cg = new ClassGen(classname, SUPER_NAME_N[superclassType], filename,
Constants.ACC_PUBLIC | Constants.ACC_SUPER, null);
cp = cg.getConstantPool(); // cg creates constant pool
// main instruction lists
factory = new InstructionFactory(cg);
init = new InstructionList();
main = new InstructionList();
// create the fields
for ( int i=0; i 0 ) {
append(new ALOAD(1));
append(new PUSH(cp, 1 + p.numparams));
append(factory.createInvoke(STR_VARARGS, "subargs", TYPE_VARARGS, ARG_TYPES_INT, Constants.INVOKEVIRTUAL));
append(new ASTORE(1));
}
} else {
// fixed arg function between 0 and 3 arguments
for ( slot=0; slot",
cg.getClassName(), init, cg.getConstantPool());
init.append(InstructionConstants.RETURN);
mg.setMaxStack();
cg.addMethod(mg.getMethod());
init.dispose();
}
// add default constructor
cg.addEmptyConstructor(Constants.ACC_PUBLIC);
// gen method
resolveBranches();
mg.setMaxStack();
cg.addMethod(mg.getMethod());
main.dispose();
// convert to class bytes
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
cg.getJavaClass().dump(baos);
return baos.toByteArray();
} catch ( IOException ioe ) {
throw new RuntimeException("JavaClass.dump() threw "+ioe);
}
}
public void dup() {
append(InstructionConstants.DUP);
}
public void pop() {
append(InstructionConstants.POP);
}
public void loadNil() {
append(factory.createFieldAccess(STR_LUAVALUE, "NIL", TYPE_LUAVALUE, Constants.GETSTATIC));
}
public void loadNone() {
append(factory.createFieldAccess(STR_LUAVALUE, "NONE", TYPE_LUAVALUE, Constants.GETSTATIC));
}
public void loadBoolean(boolean b) {
String field = (b? "TRUE": "FALSE");
append(factory.createFieldAccess(STR_LUAVALUE, field, TYPE_LUABOOLEAN, Constants.GETSTATIC));
}
private Map plainSlotVars = new HashMap();
private Map upvalueSlotVars = new HashMap();
private int findSlot( int slot, Map map, String prefix, Type type ) {
Integer islot = Integer.valueOf(slot);
if ( map.containsKey(islot) )
return ((Integer)map.get(islot)).intValue();
String name = prefix+slot;
LocalVariableGen local = mg.addLocalVariable(name, type, null, null);
int index = local.getIndex();
map.put(islot, Integer.valueOf(index));
return index;
}
private int findSlotIndex( int slot, boolean isupvalue ) {
return isupvalue?
findSlot( slot, upvalueSlotVars, PREFIX_UPVALUE_SLOT, TYPE_LOCALUPVALUE ):
findSlot( slot, plainSlotVars, PREFIX_PLAIN_SLOT, TYPE_LUAVALUE );
}
public void loadLocal(int pc, int slot) {
boolean isupval = pi.isUpvalueRefer(pc, slot);
int index = findSlotIndex( slot, isupval );
append(new ALOAD(index));
if (isupval) {
append(new PUSH(cp, 0));
append(InstructionConstants.AALOAD);
}
}
public void storeLocal(int pc, int slot) {
boolean isupval = pi.isUpvalueAssign(pc, slot);
int index = findSlotIndex( slot, isupval );
if (isupval) {
boolean isupcreate = pi.isUpvalueCreate(pc, slot);
if ( isupcreate ) {
append(factory.createInvoke(classname, "newupe", TYPE_LOCALUPVALUE, ARG_TYPES_NONE, Constants.INVOKESTATIC));
append(InstructionConstants.DUP);
append(new ASTORE(index));
} else {
append(new ALOAD(index));
}
append(InstructionConstants.SWAP);
append(new PUSH(cp, 0));
append(InstructionConstants.SWAP);
append(InstructionConstants.AASTORE);
} else {
append(new ASTORE(index));
}
}
public void createUpvalues(int pc, int firstslot, int numslots) {
for ( int i=0; i", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL));
append(InstructionConstants.DUP);
loadEnv();
append(factory.createInvoke(STR_LUAVALUE, "setfenv", Type.VOID, ARG_TYPES_LUAVALUE, Constants.INVOKEVIRTUAL));
}
public void closureInitUpvalueFromUpvalue(String protoname, int newup, int upindex) {
boolean isrw = pi.isReadWriteUpvalue( pi.upvals[upindex] );
Type uptype = isrw? (Type) TYPE_LOCALUPVALUE: (Type) TYPE_LUAVALUE;
String srcname = upvalueName(upindex);
String destname = upvalueName(newup);
append(InstructionConstants.THIS);
append(factory.createFieldAccess(classname, srcname, uptype, Constants.GETFIELD));
append(factory.createFieldAccess(protoname, destname, uptype, Constants.PUTFIELD));
}
public void closureInitUpvalueFromLocal(String protoname, int newup, int pc, int srcslot) {
boolean isrw = pi.isReadWriteUpvalue( pi.vars[srcslot][pc].upvalue );
Type uptype = isrw? (Type) TYPE_LOCALUPVALUE: (Type) TYPE_LUAVALUE;
String destname = upvalueName(newup);
int index = findSlotIndex( srcslot, isrw );
append(new ALOAD(index));
append(factory.createFieldAccess(protoname, destname, uptype, Constants.PUTFIELD));
}
private Map constants = new HashMap();
public void loadConstant(LuaValue value) {
switch ( value.type() ) {
case LuaValue.TNIL:
loadNil();
break;
case LuaValue.TBOOLEAN:
loadBoolean( value.toboolean() );
break;
case LuaValue.TNUMBER:
case LuaValue.TSTRING:
String name = (String) constants.get(value);
if ( name == null ) {
name = value.type() == LuaValue.TNUMBER?
value.isinttype()?
createLuaIntegerField(value.checkint()):
createLuaDoubleField(value.checkdouble()):
createLuaStringField(value.checkstring());
constants.put(value, name);
}
append(factory.createGetStatic(classname, name, TYPE_LUAVALUE));
break;
default:
throw new IllegalArgumentException("bad constant type: "+value.type());
}
}
private String createLuaIntegerField(int value) {
String name = PREFIX_CONSTANT+constants.size();
FieldGen fg = new FieldGen(Constants.ACC_STATIC | Constants.ACC_FINAL,
TYPE_LUAVALUE, name, cp);
cg.addField(fg.getField());
init.append(new PUSH(cp, value));
init.append(factory.createInvoke(STR_LUAVALUE, "valueOf",
TYPE_LUAINTEGER, ARG_TYPES_INT, Constants.INVOKESTATIC));
init.append(factory.createPutStatic(classname, name, TYPE_LUAVALUE));
return name;
}
private String createLuaDoubleField(double value) {
String name = PREFIX_CONSTANT+constants.size();
FieldGen fg = new FieldGen(Constants.ACC_STATIC | Constants.ACC_FINAL,
TYPE_LUAVALUE, name, cp);
cg.addField(fg.getField());
init.append(new PUSH(cp, value));
init.append(factory.createInvoke(STR_LUAVALUE, "valueOf",
TYPE_LUANUMBER, ARG_TYPES_DOUBLE, Constants.INVOKESTATIC));
init.append(factory.createPutStatic(classname, name, TYPE_LUAVALUE));
return name;
}
private String createLuaStringField(LuaString value) {
String name = PREFIX_CONSTANT+constants.size();
FieldGen fg = new FieldGen(Constants.ACC_STATIC | Constants.ACC_FINAL,
TYPE_LUAVALUE, name, cp);
cg.addField(fg.getField());
LuaString ls = value.checkstring();
if ( ls.isValidUtf8() ) {
init.append(new PUSH(cp, value.tojstring()));
init.append(factory.createInvoke(STR_LUASTRING, "valueOf",
TYPE_LUASTRING, ARG_TYPES_STRING, Constants.INVOKESTATIC));
} else {
char[] c = new char[ls.m_length];
for ( int j=0; j= branchDestHandles.length )
throw new IllegalArgumentException("no target at or after "+targets[pc]+" op="+Lua.GET_OPCODE(p.code[targets[pc]]));
branches[pc].setTarget(branchDestHandles[t]);
}
}
}
public void setlistStack(int pc, int a0, int index0, int nvals) {
for ( int i=0; i