diff --git a/README.html b/README.html index e851f777..629ef0d3 100644 --- a/README.html +++ b/README.html @@ -724,10 +724,10 @@ and LuaForge:   2.0.2 diff --git a/src/core/org/luaj/vm2/LuaUserdata.java b/src/core/org/luaj/vm2/LuaUserdata.java index a790838f..cf2b7b28 100644 --- a/src/core/org/luaj/vm2/LuaUserdata.java +++ b/src/core/org/luaj/vm2/LuaUserdata.java @@ -91,7 +91,7 @@ public class LuaUserdata extends LuaValue { } public void set( LuaValue key, LuaValue value ) { - if ( m_metatable!=null && ! settable(this,key,value) ) + if ( m_metatable==null || ! settable(this,key,value) ) error( "cannot set "+key+" for userdata" ); } diff --git a/src/jse/org/luaj/vm2/lib/jse/CoerceJavaToLua.java b/src/jse/org/luaj/vm2/lib/jse/CoerceJavaToLua.java index 6b5e58df..452f2b93 100644 --- a/src/jse/org/luaj/vm2/lib/jse/CoerceJavaToLua.java +++ b/src/jse/org/luaj/vm2/lib/jse/CoerceJavaToLua.java @@ -21,12 +21,15 @@ ******************************************************************************/ package org.luaj.vm2.lib.jse; +import java.lang.reflect.Array; import java.util.HashMap; import java.util.Map; import org.luaj.vm2.LuaDouble; import org.luaj.vm2.LuaInteger; import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaUserdata; import org.luaj.vm2.LuaValue; /** @@ -103,6 +106,7 @@ public class CoerceJavaToLua { COERCIONS.put( Character.class, charCoercion ); COERCIONS.put( Short.class, intCoercion ); COERCIONS.put( Integer.class, intCoercion ); + COERCIONS.put( Long.class, doubleCoercion ); COERCIONS.put( Float.class, doubleCoercion ); COERCIONS.put( Double.class, doubleCoercion ); COERCIONS.put( String.class, stringCoercion ); @@ -129,9 +133,25 @@ public class CoerceJavaToLua { return LuaValue.NIL; Class clazz = o.getClass(); Coercion c = (Coercion) COERCIONS.get( clazz ); - if ( c != null ) - return c.coerce( o ); - return LuajavaLib.toUserdata( o, clazz ); + if ( c == null ) { + c = o instanceof Class? JavaClass.forClass((Class)o): + clazz.isArray()? arrayCoercion: + instanceCoercion; + COERCIONS.put( clazz, c ); + } + return c.coerce(o); } + static final Coercion instanceCoercion = new Coercion() { + public LuaValue coerce(Object javaValue) { + return new JavaInstance(javaValue); + } + }; + + // should be userdata? + static final Coercion arrayCoercion = new Coercion() { + public LuaValue coerce(Object javaValue) { + return new JavaArray(javaValue); + } + }; } diff --git a/src/jse/org/luaj/vm2/lib/jse/CoerceLuaToJava.java b/src/jse/org/luaj/vm2/lib/jse/CoerceLuaToJava.java index e759c534..7191162e 100644 --- a/src/jse/org/luaj/vm2/lib/jse/CoerceLuaToJava.java +++ b/src/jse/org/luaj/vm2/lib/jse/CoerceLuaToJava.java @@ -22,10 +22,16 @@ package org.luaj.vm2.lib.jse; import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.luaj.vm2.LuaError; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Varargs; @@ -58,187 +64,272 @@ import org.luaj.vm2.Varargs; */ public class CoerceLuaToJava { + static int SCORE_NULL_VALUE = 0x10; + static int SCORE_WRONG_TYPE = 0x100; + static int SCORE_UNCOERCIBLE = 0x10000; + static interface Coercion { + public int score( LuaValue value ); public Object coerce( LuaValue value ); - public int score( int paramType ); }; + + /** + * Coerce a LuaValue value to a specified java class + * @param value LuaValue to coerce + * @param clazz Class to coerce into + * @return Object of type clazz (or a subclass) with the corresponding value. + */ + public static Object coerce(LuaValue value, Class clazz) { + return getCoercion(clazz).coerce(value); + } - static final Map COERCIONS = new HashMap(); + static final Map COERCIONS = Collections.synchronizedMap(new HashMap()); - static { - Coercion boolCoercion = new Coercion() { - public Object coerce(LuaValue value) { - return value.toboolean()? Boolean.TRUE: Boolean.FALSE; - } - public int score(int paramType) { - switch ( paramType ) { - case LuaValue.TNIL: - case LuaValue.TBOOLEAN: - return 0; - case LuaValue.TINT: - case LuaValue.TNUMBER: - return 1; - default: - return 4; + static final class BoolCoercion implements Coercion { + public String toString() { + return "BoolCoercion()"; + } + public int score( LuaValue value ) { + switch ( value.type() ) { + case LuaValue.TBOOLEAN: + return 0; + } + return 1; + } + + public Object coerce(LuaValue value) { + return value.toboolean()? Boolean.TRUE: Boolean.FALSE; + } + } + + static final class NumericCoercion implements Coercion { + static final int TARGET_TYPE_BYTE = 0; + static final int TARGET_TYPE_CHAR = 1; + static final int TARGET_TYPE_SHORT = 2; + static final int TARGET_TYPE_INT = 3; + static final int TARGET_TYPE_LONG = 4; + static final int TARGET_TYPE_FLOAT = 5; + static final int TARGET_TYPE_DOUBLE = 6; + static final String[] TYPE_NAMES = { "byte", "char", "short", "int", "long", "float", "double" }; + final int targetType; + public String toString() { + return "NumericCoercion("+TYPE_NAMES[targetType]+")"; + } + NumericCoercion(int targetType) { + this.targetType = targetType; + } + public int score( LuaValue value ) { + if ( value.isint() ) { + switch ( targetType ) { + case TARGET_TYPE_BYTE: { + int i = value.toint(); + return (i==(byte)i)? 0: SCORE_WRONG_TYPE; } - } - }; - Coercion byteCoercion = new Coercion() { - public Object coerce(LuaValue value) { - return new Byte( (byte) value.toint() ); - } - public int score(int paramType) { - switch ( paramType ) { - case LuaValue.TINT: - return 1; - case LuaValue.TNUMBER: - return 2; - default: - return 4; + case TARGET_TYPE_CHAR: { + int i = value.toint(); + return (i==(byte)i)? 1: (i==(char)i)? 0: SCORE_WRONG_TYPE; } - } - }; - Coercion charCoercion = new Coercion() { - public Object coerce(LuaValue value) { - return new Character( (char) value.toint() ); - } - public int score(int paramType) { - switch ( paramType ) { - case LuaValue.TINT: - return 1; - case LuaValue.TNUMBER: - return 2; - default: - return 4; + case TARGET_TYPE_SHORT: { + int i = value.toint(); + return (i==(byte)i)? 1: (i==(short)i)? 0: SCORE_WRONG_TYPE; } - } - }; - Coercion shortCoercion = new Coercion() { - public Object coerce(LuaValue value) { - return new Short( (short) value.toint() ); - } - public int score(int paramType) { - switch ( paramType ) { - case LuaValue.TINT: - return 1; - case LuaValue.TNUMBER: - return 2; - default: - return 4; + case TARGET_TYPE_INT: { + int i = value.toint(); + return (i==(byte)i)? 2: ((i==(char)i) || (i==(short)i))? 1: 0; } - } - }; - Coercion intCoercion = new Coercion() { - public Object coerce(LuaValue value) { - return new Integer( value.toint() ); - } - public int score(int paramType) { - switch ( paramType ) { - case LuaValue.TINT: - return 0; - case LuaValue.TNUMBER: - return 1; - case LuaValue.TBOOLEAN: - case LuaValue.TNIL: - return 2; - default: - return 4; + case TARGET_TYPE_FLOAT: return 1; + case TARGET_TYPE_LONG: return 1; + case TARGET_TYPE_DOUBLE: return 2; + default: return SCORE_WRONG_TYPE; } - } - }; - Coercion longCoercion = new Coercion() { - public Object coerce(LuaValue value) { - return new Long( value.tolong() ); - } - public int score(int paramType) { - switch ( paramType ) { - case LuaValue.TINT: - return 1; - case LuaValue.TNUMBER: - return 2; - default: - return 4; + } else if ( value.isnumber() ) { + switch ( targetType ) { + case TARGET_TYPE_BYTE: return SCORE_WRONG_TYPE; + case TARGET_TYPE_CHAR: return SCORE_WRONG_TYPE; + case TARGET_TYPE_SHORT: return SCORE_WRONG_TYPE; + case TARGET_TYPE_INT: return SCORE_WRONG_TYPE; + case TARGET_TYPE_LONG: { + double d = value.todouble(); + return (d==(long)d)? 0: SCORE_WRONG_TYPE; } - } - }; - Coercion floatCoercion = new Coercion() { - public Object coerce(LuaValue value) { - return new Float( value.tofloat() ); - } - public int score( int paramType ) { - switch ( paramType ) { - case LuaValue.TINT: - case LuaValue.TNUMBER: - return 1; - case LuaValue.TBOOLEAN: - return 2; - default: - return 4; + case TARGET_TYPE_FLOAT: { + double d = value.todouble(); + return (d==(float)d)? 0: SCORE_WRONG_TYPE; } - } - }; - Coercion doubleCoercion = new Coercion() { - public Object coerce(LuaValue value) { - return new Double( value.todouble() ); - } - public int score(int paramType) { - switch ( paramType ) { - case LuaValue.TINT: - return 1; - case LuaValue.TNUMBER: - return 0; - case LuaValue.TBOOLEAN: - return 2; - default: - return 4; + case TARGET_TYPE_DOUBLE: { + double d = value.todouble(); + return ((d==(long)d) || (d==(float)d))? 1: 0; } + default: return SCORE_WRONG_TYPE; + } + } else { + return SCORE_UNCOERCIBLE; } - }; - Coercion stringCoercion = new Coercion() { - public Object coerce(LuaValue value) { + } + + public Object coerce(LuaValue value) { + switch ( targetType ) { + case TARGET_TYPE_BYTE: return new Byte( (byte) value.toint() ); + case TARGET_TYPE_CHAR: return new Character( (char) value.toint() ); + case TARGET_TYPE_SHORT: return new Short( (short) value.toint() ); + case TARGET_TYPE_INT: return new Integer( (int) value.toint() ); + case TARGET_TYPE_LONG: return new Long( (long) value.todouble() ); + case TARGET_TYPE_FLOAT: return new Float( (float) value.todouble() ); + case TARGET_TYPE_DOUBLE: return new Double( (double) value.todouble() ); + default: return null; + } + } + } + + static final class StringCoercion implements Coercion { + public static final int TARGET_TYPE_STRING = 0; + public static final int TARGET_TYPE_BYTES = 1; + final int targetType; + public StringCoercion(int targetType) { + this.targetType = targetType; + } + public String toString() { + return "StringCoercion("+(targetType==TARGET_TYPE_STRING? "String": "byte[]")+")"; + } + public int score(LuaValue value) { + switch ( value.type() ) { + case LuaValue.TSTRING: + return value.checkstring().isValidUtf8()? + (targetType==TARGET_TYPE_STRING? 0: 1): + (targetType==TARGET_TYPE_BYTES? 0: SCORE_WRONG_TYPE); + case LuaValue.TNIL: + return SCORE_NULL_VALUE; + default: + return targetType == TARGET_TYPE_STRING? SCORE_WRONG_TYPE: SCORE_UNCOERCIBLE; + } + } + public Object coerce(LuaValue value) { + if ( value.isnil() ) + return null; + if ( targetType == TARGET_TYPE_STRING ) return value.tojstring(); - } - public int score(int paramType) { - switch ( paramType ) { - case LuaValue.TSTRING: - return 0; - case LuaValue.TUSERDATA: - return 1; - default: - return 2; - } + LuaString s = value.checkstring(); + byte[] b = new byte[s.m_length]; + s.copyInto(0, b, 0, b.length); + return b; + } + } + + static final class ArrayCoercion implements Coercion { + final Class componentType; + final Coercion componentCoercion; + public ArrayCoercion(Class componentType) { + this.componentType = componentType; + this.componentCoercion = getCoercion(componentType); + } + public String toString() { + return "ArrayCoercion("+componentType.getName()+")"; + } + public int score(LuaValue value) { + switch ( value.type() ) { + case LuaValue.TTABLE: + return value.length()==0? 0: componentCoercion.score( value.get(1) ); + case LuaValue.TUSERDATA: + return inheritanceLevels( componentType, value.touserdata().getClass().getComponentType() ); + case LuaValue.TNIL: + return SCORE_NULL_VALUE; + default: + return SCORE_UNCOERCIBLE; } - }; - Coercion objectCoercion = new Coercion() { - public Object coerce(LuaValue value) { - switch ( value.type() ) { - case LuaValue.TUSERDATA: - return value.optuserdata(Object.class, null); - case LuaValue.TSTRING: - return value.tojstring(); - case LuaValue.TINT: - return new Integer(value.toint()); - case LuaValue.TNUMBER: - return new Double(value.todouble()); - case LuaValue.TBOOLEAN: - return value.toboolean()? Boolean.TRUE: Boolean.FALSE; - case LuaValue.TNIL: - return null; - default: - return value; - } - } - public int score(int paramType) { - switch ( paramType ) { - case LuaValue.TUSERDATA: - return 0; - case LuaValue.TSTRING: - return 1; - default: - return 0x10; - } + } + public Object coerce(LuaValue value) { + switch ( value.type() ) { + case LuaValue.TTABLE: { + int n = value.length(); + Object a = Array.newInstance(componentType, n); + for ( int i=0; i 0 ) - return scoreParam( LuajavaLib.paramComponentTypeOfParamType(paramType), typ ); - else - return 0x10 + (scoreParam(paramType, typ) << 8); + co = new ArrayCoercion(c.getComponentType()); + } else { + co = new ObjectCoercion(c); } - return 0x1000; - } - - /** Do a conversion */ - static Object coerceArg(LuaValue a, Class c) { - if ( a.isuserdata(c) ) - return a.touserdata(c); - Coercion co = (Coercion) COERCIONS.get( c ); - if ( co != null ) { - return co.coerce( a ); - } - if ( c.isArray() ) { - boolean istable = a.istable(); - int n = istable? a.length(): 1; - Class typ = c.getComponentType(); - Object arr = Array.newInstance(typ, n); - for ( int i=0; i nargs? 0x4000: 0x8000); - for ( int i=0; i + * Can get elements by their integer key index, as well as the length. + */ +public class JavaArray extends LuaUserdata { + + static final LuaValue LENGTH = valueOf("length"); + + JavaArray(Object instance) { + super(instance); + } + + public LuaValue get(LuaValue key) { + if ( key.equals(LENGTH) ) + return CoerceJavaToLua.coerce(Array.getLength(m_instance)); + if ( key.isint() ) { + int i = key.toint() - 1; + return i>=0 && i=0 && i + * Will respond to get() and set() by returning field values, or java methods. + */ +public class JavaClass extends JavaInstance implements CoerceJavaToLua.Coercion { + + static final Map classes = Collections.synchronizedMap(new HashMap()); + + static final LuaValue NEW = valueOf("new"); + + Map fields; + Map methods; + + static JavaClass forClass(Class c) { + JavaClass j = (JavaClass) classes.get(c); + if ( j == null ) + classes.put( c, j = new JavaClass(c) ); + return j; + } + + JavaClass(Class c) { + super(c); + this.jclass = this; + } + + public LuaValue coerce(Object javaValue) { + return this; + } + + Field getField(LuaValue key) { + if ( fields == null ) { + Map m = new HashMap(); + Field[] f = ((Class)m_instance).getFields(); + for ( int i=0; i + * May be called with arguments to return a JavaInstance + * created by calling the constructor. + */ +public class JavaConstructor extends JavaMember { + + static final Map constructors = Collections.synchronizedMap(new HashMap()); + + static JavaConstructor forConstructor(Constructor c) { + JavaConstructor j = (JavaConstructor) constructors.get(c); + if ( j == null ) + constructors.put( c, j = new JavaConstructor(c) ); + return j; + } + + public static LuaValue forConstructors(JavaConstructor[] array) { + return new Overload(array); + } + + final Constructor constructor; + + private JavaConstructor(Constructor c) { + super( c.getParameterTypes(), c.getModifiers() ); + this.constructor = c; + } + + public Varargs invoke(Varargs args) { + Object[] a = convertArgs(args); + try { + return CoerceJavaToLua.coerce( constructor.newInstance(a) ); + } catch (InvocationTargetException e) { + throw new LuaError(e.getTargetException()); + } catch (Exception e) { + return LuaValue.error("coercion error "+e); + } + } + + static class Overload extends VarArgFunction { + final JavaConstructor[] constructors; + public Overload(JavaConstructor[] c) { + this.constructors = c; + } + + public Varargs invoke(Varargs args) { + JavaConstructor best = null; + int score = CoerceLuaToJava.SCORE_UNCOERCIBLE; + for ( int i=0; i + * 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