org.luaj.vm2.lib.jse.LuajavaLib 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) 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.lib.jse;
/** LuaJava-like bindings to Java scripting.
*
* TODO: coerce types on way in and out, pick method base on arg count ant types.
*/
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaUserdata;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.PackageLib;
import org.luaj.vm2.lib.ThreeArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.VarArgFunction;
public class LuajavaLib extends VarArgFunction {
static final int INIT = 0;
static final int BINDCLASS = 1;
static final int NEWINSTANCE = 2;
static final int NEW = 3;
static final int CREATEPROXY = 4;
static final int LOADLIB = 5;
static final String[] NAMES = {
"bindClass",
"newInstance",
"new",
"createProxy",
"loadLib",
};
static final Map classMetatables = new HashMap();
static final int METHOD_MODIFIERS_VARARGS = 0x80;
static final LuaValue LENGTH = valueOf("length");
static final Map consCache = new HashMap();
static final Map consIndex = new HashMap();
static final Map methCache = new HashMap();
static final Map methIndex = new HashMap();
public LuajavaLib() {
}
public Varargs invoke(Varargs args) {
try {
switch ( opcode ) {
case INIT: {
LuaTable t = new LuaTable();
bind( t, LuajavaLib.class, NAMES, BINDCLASS );
env.set("luajava", t);
PackageLib.instance.LOADED.set("luajava", t);
return t;
}
case BINDCLASS: {
final Class clazz = Class.forName(args.checkjstring(1));
return toUserdata( clazz, clazz );
}
case NEWINSTANCE:
case NEW: {
// get constructor
final LuaValue c = args.checkvalue(1);
final Class clazz = (opcode==NEWINSTANCE? Class.forName(c.tojstring()): (Class) c.checkuserdata(Class.class));
final Varargs consargs = args.subargs(2);
final long paramssig = LuajavaLib.paramsSignatureOf( consargs );
final Constructor con = resolveConstructor( clazz, paramssig );
final boolean isvarargs = ((con.getModifiers() & METHOD_MODIFIERS_VARARGS) != 0);
// coerce args, construct instance
final Object[] cargs = CoerceLuaToJava.coerceArgs( consargs, con.getParameterTypes(), isvarargs );
final Object o = con.newInstance( cargs );
// return result
return toUserdata( o, clazz );
}
case CREATEPROXY: {
final int niface = args.narg()-1;
if ( niface <= 0 )
throw new LuaError("no interfaces");
final LuaValue lobj = args.checktable(niface+1);
// get the interfaces
final Class[] ifaces = new Class[niface];
for ( int i=0; i>(6*(argindex+1)))) & 0x3F;
}
public static int paramBaseTypeFromParamType(int paramType) {
int t = paramType & 0xf;
return t == (TINT & 0xF)? TINT: t;
}
public static int paramDepthFromParamType(int paramType) {
return (paramType >> 4) & 0x3;
}
public static int paramComponentTypeOfParamType(int paramType) {
int b = paramBaseTypeFromParamType( paramType );
int d = paramDepthFromParamType( paramType );
d = d>0? d-1: 0;
return (d<<4) | (b&0xF);
}
static LuaUserdata toUserdata(Object instance, final Class clazz) {
LuaTable mt = (LuaTable) classMetatables.get(clazz);
if ( mt == null ) {
mt = new LuaTable();
mt.set( LuaValue.INDEX, new TwoArgFunction() {
private Map methods;
public LuaValue call(LuaValue table, LuaValue key) {
Object instance = table.touserdata();
if ( key.isinttype() ) {
if ( clazz.isArray() ) {
int index = key.toint() - 1;
if ( index >= 0 && index < Array.getLength(instance) )
return CoerceJavaToLua.coerce( Array.get(instance, index) );
return NIL;
}
}
final String s = key.tojstring();
try {
Field f = clazz.getField(s);
Object o = f.get(instance);
return CoerceJavaToLua.coerce( o );
} catch (NoSuchFieldException nsfe) {
if ( clazz.isArray() && key.equals(LENGTH) )
return LuaValue.valueOf( Array.getLength(instance) );
if ( methods == null )
methods = new HashMap();
LMethod m = (LMethod) methods.get(s);
if ( m == null ) {
m = new LMethod(clazz,s);
// not safe - param list needs to
// distinguish between more cases
// methods.put(s, m);
}
return m;
} catch (Exception e) {
throw new LuaError(e);
}
}
});
mt.set( LuaValue.NEWINDEX, new ThreeArgFunction() {
public LuaValue call(LuaValue table, LuaValue key, LuaValue val) {
Object instance = table.touserdata();
if ( key.isinttype() ) {
if ( clazz.isArray() ) {
Object v = CoerceLuaToJava.coerceArg(val, clazz.getComponentType());
int index = key.toint() - 1;
if ( index >= 0 && index < Array.getLength(instance) )
Array.set(instance, index, v);
else
throw new LuaError("array bounds exceeded "+index);
return NIL;
}
}
String s = key.tojstring();
try {
Field f = clazz.getField(s);
Object v = CoerceLuaToJava.coerceArg(val, f.getType());
f.set(table.checkuserdata(Object.class),v);
} catch (Exception e) {
throw new LuaError(e);
}
return NONE;
}
});
classMetatables.put(clazz, mt);
}
return LuaValue.userdataOf(instance,mt);
}
static final class LMethod extends VarArgFunction {
private final Class clazz;
private final String s;
private LMethod(Class clazz, String s) {
this.clazz = clazz;
this.s = s;
}
public String tojstring() {
return clazz.getName()+"."+s+"()";
}
public Varargs invoke(Varargs args) {
try {
// find the method
final Object instance = args.touserdata(1);
final Varargs methargs = args.subargs(2);
final long paramssig = LuajavaLib.paramsSignatureOf(methargs);
final Method meth = resolveMethod( clazz, s, paramssig );
final boolean isvarargs = ((meth.getModifiers() & METHOD_MODIFIERS_VARARGS) != 0);
// coerce the arguments
final Object[] margs = CoerceLuaToJava.coerceArgs( methargs, meth.getParameterTypes(), isvarargs );
final Object result = meth.invoke( instance, margs );
// coerce the result
return CoerceJavaToLua.coerce(result);
} catch (InvocationTargetException ite) {
throw new LuaError(ite.getTargetException());
} catch (Exception e) {
throw new LuaError(e);
}
}
}
static Constructor resolveConstructor(Class clazz, long paramssig ) {
// get the cache
Map cache = (Map) consCache.get( clazz );
if ( cache == null )
consCache.put( clazz, cache = new HashMap() );
// look up in the cache
Constructor c = (Constructor) cache.get( Long.valueOf(paramssig) );
if ( c != null )
return c;
// get index
Constructor[] cons = (Constructor[]) consIndex.get( clazz );
if ( cons == null ) {
consIndex.put( clazz, cons = clazz.getConstructors() );
if ( cons == null )
throw new IllegalArgumentException("no public constructors");
}
// find constructor with best score
int bests = Integer.MAX_VALUE;
int besti = 0;
for ( int i=0, size=cons.length; i