+ * Will respond to get() and set() by returning field values or methods.
+ */
+public class JavaInstance extends LuaUserdata {
+
+ JavaClass jclass;
+
+ JavaInstance(Object instance) {
+ super(instance);
+ }
+
+ public LuaValue get(LuaValue key) {
+ if ( jclass == null )
+ jclass = JavaClass.forClass(m_instance.getClass());
+ Field f = jclass.getField(key);
+ if ( f != null )
+ try {
+ return CoerceJavaToLua.coerce(f.get(m_instance));
+ } catch (Exception e) {
+ throw new LuaError(e);
+ }
+ LuaValue m = jclass.getMethod(key);
+ if ( m != null )
+ return m;
+ return super.get(key);
+ }
+
+ public void set(LuaValue key, LuaValue value) {
+ if ( jclass == null )
+ jclass = JavaClass.forClass(m_instance.getClass());
+ Field f = jclass.getField(key);
+ if ( f != null )
+ try {
+ f.set(m_instance, CoerceLuaToJava.coerce(value, f.getType()));
+ return;
+ } catch (Exception e) {
+ throw new LuaError(e);
+ }
+ super.set(key, value);
+ }
+
+}
diff --git a/src/jse/org/luaj/vm2/lib/jse/JavaMember.java b/src/jse/org/luaj/vm2/lib/jse/JavaMember.java
new file mode 100644
index 00000000..289c37d8
--- /dev/null
+++ b/src/jse/org/luaj/vm2/lib/jse/JavaMember.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+* Copyright (c) 2011 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;
+
+import org.luaj.vm2.Varargs;
+import org.luaj.vm2.lib.VarArgFunction;
+import org.luaj.vm2.lib.jse.CoerceLuaToJava.Coercion;
+
+/**
+ * Java method or constructor.
+ *
+ * Primarily handles argument coercion for parameter lists including scoring of compatibility and
+ * java varargs handling.
+ */
+abstract
+class JavaMember extends VarArgFunction {
+
+ static final int METHOD_MODIFIERS_VARARGS = 0x80;
+
+ final Coercion[] fixedargs;
+ final Coercion varargs;
+
+ protected JavaMember(Class[] params, int modifiers) {
+ boolean isvarargs = ((modifiers & METHOD_MODIFIERS_VARARGS) != 0);
+ fixedargs = new CoerceLuaToJava.Coercion[isvarargs? params.length-1: params.length];
+ for ( int i=0; ifixedargs.length? CoerceLuaToJava.SCORE_WRONG_TYPE * (n-fixedargs.length): 0;
+ for ( int j=0; j
+ * Can be invoked.
+ */
+public class JavaMethod extends JavaMember {
+
+ static final Map methods = Collections.synchronizedMap(new HashMap());
+
+ static JavaMethod forMethod(Method m) {
+ JavaMethod j = (JavaMethod) methods.get(m);
+ if ( j == null )
+ methods.put( m, j = new JavaMethod(m) );
+ return j;
+ }
+
+ static LuaFunction forMethods(JavaMethod[] m) {
+ return new Overload(m);
+ }
+
+ final Method method;
+
+ private JavaMethod(Method m) {
+ super( m.getParameterTypes(), m.getModifiers() );
+ this.method = m;
+ }
+
+ public LuaValue call() {
+ return error("method cannot be called without instance");
+ }
+
+ public LuaValue call(LuaValue arg) {
+ return invokeMethod(arg.checkuserdata(), LuaValue.NONE);
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2) {
+ return invokeMethod(arg1.checkuserdata(), arg2);
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
+ return invokeMethod(arg1.checkuserdata(), LuaValue.varargsOf(arg2, arg3));
+ }
+
+ public Varargs invoke(Varargs args) {
+ return invokeMethod(args.checkuserdata(1), args.subargs(2));
+ }
+
+ LuaValue invokeMethod(Object instance, Varargs args) {
+ Object[] a = convertArgs(args);
+ try {
+ return CoerceJavaToLua.coerce( method.invoke(instance, a) );
+ } catch (InvocationTargetException e) {
+ throw new LuaError(e.getTargetException());
+ } catch (Exception e) {
+ return LuaValue.error("coercion error "+e);
+ }
+ }
+
+ /**
+ * LuaValue that represents an overloaded Java method.
+ *
+ * On invocation, will pick the best method fromi the list, and invoke it.
+ */
+ static class Overload extends LuaFunction {
+
+ final JavaMethod[] methods;
+
+ Overload(JavaMethod[] methods) {
+ this.methods = methods;
+ }
+
+ public LuaValue call() {
+ return error("method cannot be called without instance");
+ }
+
+ public LuaValue call(LuaValue arg) {
+ return invokeBestMethod(arg.checkuserdata(), LuaValue.NONE);
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2) {
+ return invokeBestMethod(arg1.checkuserdata(), arg2);
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
+ return invokeBestMethod(arg1.checkuserdata(), LuaValue.varargsOf(arg2, arg3));
+ }
+
+ public Varargs invoke(Varargs args) {
+ return invokeBestMethod(args.checkuserdata(1), args.subargs(2));
+ }
+
+ private LuaValue invokeBestMethod(Object instance, Varargs args) {
+ JavaMethod best = null;
+ int score = CoerceLuaToJava.SCORE_UNCOERCIBLE;
+ 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