| /******************************************************************************* |
| * 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.vm3.lib.jse; |
| |
| |
| import java.lang.reflect.Array; |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| |
| import org.luaj.vm3.LuaError; |
| import org.luaj.vm3.LuaTable; |
| import org.luaj.vm3.LuaValue; |
| import org.luaj.vm3.Varargs; |
| import org.luaj.vm3.compiler.LuaC; |
| import org.luaj.vm3.lib.LibFunction; |
| import org.luaj.vm3.lib.VarArgFunction; |
| |
| /** |
| * Subclass of {@link LibFunction} which implements the features of the luajava package. |
| * <p> |
| * Luajava is an approach to mixing lua and java using simple functions that bind |
| * java classes and methods to lua dynamically. The API is documented on the |
| * <a href="http://www.keplerproject.org/luajava/">luajava</a> documentation pages. |
| * |
| * <p> |
| * Typically, this library is included as part of a call to |
| * {@link JsePlatform#standardGlobals()} |
| * <pre> {@code |
| * Globals globals = JsePlatform.standardGlobals(); |
| * System.out.println( globals.get("luajava").get("bindClass").call( LuaValue.valueOf("java.lang.System") ).invokeMethod("currentTimeMillis") ); |
| * } </pre> |
| * <p> |
| * To instantiate and use it directly, |
| * link it into your globals table via {@link Globals#load} using code such as: |
| * <pre> {@code |
| * Globals globals = new Globals(); |
| * globals.load(new JseBaseLib()); |
| * globals.load(new PackageLib()); |
| * globals.load(new LuajavaLib()); |
| * globals.load( |
| * "sys = luajava.bindClass('java.lang.System')\n"+ |
| * "print ( sys:currentTimeMillis() )\n", "main.lua" ).call(); |
| * } </pre> |
| * <p> |
| * |
| * The {@code luajava} library is available |
| * on all JSE platforms via the call to {@link JsePlatform#standardGlobals()} |
| * and the luajava api's are simply invoked from lua. |
| * Because it makes extensive use of Java's reflection API, it is not available |
| * on JME, but can be used in Android applications. |
| * <p> |
| * This has been implemented to match as closely as possible the behavior in the corresponding library in C. |
| * |
| * @see LibFunction |
| * @see org.luaj.vm3.lib.jse.JsePlatform |
| * @see org.luaj.vm3.lib.jme.JmePlatform |
| * @see LuaC |
| * @see CoerceJavaToLua |
| * @see CoerceLuaToJava |
| * @see <a href="http://www.keplerproject.org/luajava/manual.html#luareference">http://www.keplerproject.org/luajava/manual.html#luareference</a> |
| */ |
| 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 int METHOD_MODIFIERS_VARARGS = 0x80; |
| |
| public LuajavaLib() { |
| } |
| |
| public Varargs invoke(Varargs args) { |
| try { |
| switch ( opcode ) { |
| case INIT: { |
| // LuaValue modname = args.arg1(); |
| LuaValue env = args.arg(2); |
| LuaTable t = new LuaTable(); |
| bind( t, LuajavaLib.class, NAMES, BINDCLASS ); |
| env.set("luajava", t); |
| env.get("package").get("loaded").set("luajava", t); |
| return t; |
| } |
| case BINDCLASS: { |
| final Class clazz = classForName(args.checkjstring(1)); |
| return JavaClass.forClass(clazz); |
| } |
| case NEWINSTANCE: |
| case NEW: { |
| // get constructor |
| final LuaValue c = args.checkvalue(1); |
| final Class clazz = (opcode==NEWINSTANCE? classForName(c.tojstring()): (Class) c.checkuserdata(Class.class)); |
| final Varargs consargs = args.subargs(2); |
| return JavaClass.forClass(clazz).getConstructor().invoke(consargs); |
| } |
| |
| 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<niface; i++ ) |
| ifaces[i] = classForName(args.checkjstring(i+1)); |
| |
| // create the invocation handler |
| InvocationHandler handler = new InvocationHandler() { |
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
| String name = method.getName(); |
| LuaValue func = lobj.get(name); |
| if ( func.isnil() ) |
| return null; |
| boolean isvarargs = ((method.getModifiers() & METHOD_MODIFIERS_VARARGS) != 0); |
| int n = args!=null? args.length: 0; |
| LuaValue[] v; |
| if ( isvarargs ) { |
| Object o = args[--n]; |
| int m = Array.getLength( o ); |
| v = new LuaValue[n+m]; |
| for ( int i=0; i<n; i++ ) |
| v[i] = CoerceJavaToLua.coerce(args[i]); |
| for ( int i=0; i<m; i++ ) |
| v[i+n] = CoerceJavaToLua.coerce(Array.get(o,i)); |
| } else { |
| v = new LuaValue[n]; |
| for ( int i=0; i<n; i++ ) |
| v[i] = CoerceJavaToLua.coerce(args[i]); |
| } |
| LuaValue result = func.invoke(v).arg1(); |
| return CoerceLuaToJava.coerce(result, method.getReturnType()); |
| } |
| }; |
| |
| // create the proxy object |
| Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), ifaces, handler); |
| |
| // return the proxy |
| return LuaValue.userdataOf( proxy ); |
| } |
| case LOADLIB: { |
| // get constructor |
| String classname = args.checkjstring(1); |
| String methodname = args.checkjstring(2); |
| Class clazz = classForName(classname); |
| Method method = clazz.getMethod(methodname, new Class[] {}); |
| Object result = method.invoke(clazz, new Object[] {}); |
| if ( result instanceof LuaValue ) { |
| return (LuaValue) result; |
| } else { |
| return NIL; |
| } |
| } |
| default: |
| throw new LuaError("not yet supported: "+this); |
| } |
| } catch (LuaError e) { |
| throw e; |
| } catch (InvocationTargetException ite) { |
| throw new LuaError(ite.getTargetException()); |
| } catch (Exception e) { |
| throw new LuaError(e); |
| } |
| } |
| |
| // load classes using app loader to allow luaj to be used as an extension |
| protected Class classForName(String name) throws ClassNotFoundException { |
| return Class.forName(name, true, ClassLoader.getSystemClassLoader()); |
| } |
| |
| } |