Refactor luajava method mapping computation.

This commit is contained in:
James Roseborough
2010-04-26 05:09:44 +00:00
parent 29d6f2ce58
commit 2f222c0f8a
3 changed files with 166 additions and 116 deletions

View File

@@ -27,13 +27,14 @@ import java.util.Map;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
public class CoerceLuaToJava {
public static interface Coercion {
public Object coerce( LuaValue value );
public int score( LuaValue value );
public int score( int paramType );
};
private static Map COERCIONS = new HashMap();
@@ -44,11 +45,12 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
return value.toboolean()? Boolean.TRUE: Boolean.FALSE;
}
public int score(LuaValue value) {
switch ( value.type() ) {
public int score(int paramType) {
switch ( paramType ) {
case LuaValue.TNIL:
case LuaValue.TBOOLEAN:
return 0;
case LuaValue.TINT:
case LuaValue.TNUMBER:
return 1;
default:
@@ -60,10 +62,12 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
return new Byte( (byte) value.toint() );
}
public int score(LuaValue value) {
switch ( value.type() ) {
public int score(int paramType) {
switch ( paramType ) {
case LuaValue.TINT:
return 1;
case LuaValue.TNUMBER:
return (value.isinttype()? 1: 2);
return 2;
default:
return 4;
}
@@ -73,10 +77,12 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
return new Character( (char) value.toint() );
}
public int score(LuaValue value) {
switch ( value.type() ) {
public int score(int paramType) {
switch ( paramType ) {
case LuaValue.TINT:
return 1;
case LuaValue.TNUMBER:
return (value.isinttype()? 1: 2);
return 2;
default:
return 4;
}
@@ -86,10 +92,12 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
return new Short( (short) value.toint() );
}
public int score(LuaValue value) {
switch ( value.type() ) {
public int score(int paramType) {
switch ( paramType ) {
case LuaValue.TINT:
return 1;
case LuaValue.TNUMBER:
return (value.isinttype()? 1: 2);
return 2;
default:
return 4;
}
@@ -99,10 +107,12 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
return new Integer( value.toint() );
}
public int score(LuaValue value) {
switch ( value.type() ) {
public int score(int paramType) {
switch ( paramType ) {
case LuaValue.TINT:
return 0;
case LuaValue.TNUMBER:
return (value.isinttype()? 0: 1);
return 1;
case LuaValue.TBOOLEAN:
case LuaValue.TNIL:
return 2;
@@ -115,10 +125,12 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
return new Long( value.tolong() );
}
public int score(LuaValue value) {
switch ( value.type() ) {
public int score(int paramType) {
switch ( paramType ) {
case LuaValue.TINT:
return 1;
case LuaValue.TNUMBER:
return (value.isinttype()? 1: 2);
return 2;
default:
return 4;
}
@@ -128,8 +140,9 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
return new Float( value.tofloat() );
}
public int score( LuaValue value ) {
switch ( value.type() ) {
public int score( int paramType ) {
switch ( paramType ) {
case LuaValue.TINT:
case LuaValue.TNUMBER:
return 1;
case LuaValue.TBOOLEAN:
@@ -143,10 +156,12 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
return new Double( value.todouble() );
}
public int score(LuaValue value) {
switch ( value.type() ) {
public int score(int paramType) {
switch ( paramType ) {
case LuaValue.TINT:
return 1;
case LuaValue.TNUMBER:
return (value.isinttype()? 1: 0);
return 0;
case LuaValue.TBOOLEAN:
return 2;
default:
@@ -158,8 +173,8 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
return value.tojstring();
}
public int score(LuaValue value) {
switch ( value.type() ) {
public int score(int paramType) {
switch ( paramType ) {
case LuaValue.TUSERDATA:
return 0;
default:
@@ -174,10 +189,10 @@ public class CoerceLuaToJava {
return value.optuserdata(Object.class, null);
case LuaValue.TSTRING:
return value.tojstring();
case LuaValue.TINT:
return new Integer(value.toint());
case LuaValue.TNUMBER:
return (value.isinttype()?
new Integer(value.toint()):
new Double(value.todouble()));
return new Double(value.todouble());
case LuaValue.TBOOLEAN:
return value.toboolean()? Boolean.TRUE: Boolean.FALSE;
case LuaValue.TNIL:
@@ -186,8 +201,8 @@ public class CoerceLuaToJava {
return value;
}
}
public int score(LuaValue value) {
switch ( value.type() ) {
public int score(int paramType) {
switch ( paramType ) {
case LuaValue.TSTRING:
return 0;
default:
@@ -217,21 +232,22 @@ public class CoerceLuaToJava {
/** Score a single parameter, including array handling */
private static int scoreParam(LuaValue a, Class c) {
if ( a.isuserdata(c) )
private static int scoreParam(int paramType, Class c) {
if ( paramType == LuaValue.TUSERDATA && !c.isArray() )
return 0;
Coercion co = (Coercion) COERCIONS.get( c );
if ( co != null ) {
return co.score( a );
int b = LuajavaLib.paramBaseTypeFromParamType(paramType);
int d = LuajavaLib.paramDepthFromParamType(paramType);
return co.score( b ) * d;
}
if ( c.isArray() ) {
Class typ = c.getComponentType();
switch ( a.type() ) {
case LuaValue.TTABLE:
return scoreParam( a.checktable().get(1), typ );
default:
return 0x10 + (scoreParam(a, typ) << 8);
}
int d = LuajavaLib.paramDepthFromParamType(paramType);
if ( d > 0 )
return scoreParam( LuajavaLib.paramComponentTypeOfParamType(paramType), typ );
else
return 0x10 + (scoreParam(paramType, typ) << 8);
}
return 0x1000;
}
@@ -261,12 +277,11 @@ public class CoerceLuaToJava {
throw new LuaError("no coercion found for "+a.getClass()+" to "+c);
}
static Object[] coerceArgs(LuaValue[] suppliedArgs, Class[] parameterTypes) {
int nargs = suppliedArgs.length;
static Object[] coerceArgs(Varargs suppliedArgs, Class[] parameterTypes) {
int n = parameterTypes.length;
Object[] args = new Object[n];
for ( int i=0; i<n && i<nargs; i++ )
args[i] = coerceArg( suppliedArgs[i], parameterTypes[i] );
for ( int i=0; i<n; i++ )
args[i] = coerceArg( suppliedArgs.arg(i+1), parameterTypes[i] );
return args;
}
@@ -278,14 +293,14 @@ public class CoerceLuaToJava {
* 3) java has less args
* 4) types coerce well
*/
static int scoreParamTypes(LuaValue[] suppliedArgs, Class[] paramTypes) {
int nargs = suppliedArgs.length;
static int scoreParamTypes(long paramssig, Class[] paramTypes) {
int nargs = LuajavaLib.paramsCountFromSig(paramssig);
int njava = paramTypes.length;
int score = (njava == nargs? 0: njava > nargs? 0x4000: 0x8000);
for ( int i=0; i<nargs && i<njava; i++ ) {
LuaValue a = suppliedArgs[i];
int paramType = LuajavaLib.paramTypeFromSig(paramssig, i);
Class c = paramTypes[i];
int s = scoreParam( a, c );
int s = scoreParam( paramType, c );
score += s;
}
return score;

View File

@@ -34,7 +34,6 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -96,11 +95,11 @@ public class LuajavaLib extends OneArgFunction {
// get constructor
final LuaValue c = args.checkvalue(1);
final Class clazz = (opcode==NEWINSTANCE? Class.forName(c.tojstring()): (Class) c.checkuserdata(Class.class));
final ParamsList params = new ParamsList( args );
final Constructor con = resolveConstructor( clazz, params );
final long paramssig = LuajavaLib.paramsSignatureOf( args );
final Constructor con = resolveConstructor( clazz, paramssig );
// coerce args, construct instance
Object[] cargs = CoerceLuaToJava.coerceArgs( params.values, con.getParameterTypes() );
Object[] cargs = CoerceLuaToJava.coerceArgs( args, con.getParameterTypes() );
Object o = con.newInstance( cargs );
// return result
@@ -177,28 +176,64 @@ public class LuajavaLib extends OneArgFunction {
}
}
public static class ParamsList {
public final LuaValue[] values;
public final Class[] classes;
public int hash;
ParamsList( Varargs args ) {
int n = Math.max(args.narg()-1,0);
values = new LuaValue[n];
classes = new Class[n];
for ( int i=0; i<n; i++ ) {
values[i] = args.arg(i+2);
classes[i] = values[i].getClass();
hash += classes[i].hashCode();
// params signature is
// - low 6-bits are number of parameters
// - each of next 9 6-bit fields encode a parameter type:
// - low 4 bits are lua type
// - high 2 bits are number of indexes deep (0,1,2, or 3)
public static long paramsSignatureOf( Varargs args ) {
long sig = 0;
int narg = args.narg();
int n = Math.min( narg, 9 );
for ( int i=1; i<=n; i++ ) {
LuaValue a = args.arg(i);
sig |= (paramTypeOf(a) << (i*6));
}
return sig | Math.min( narg, 0x3F );
}
public static int paramTypeOf( LuaValue arg ) {
int type = arg.type();
int tabledepth = 0;
if ( type == TTABLE ) {
for ( tabledepth=1; (type=(arg=arg.get(1)).type()) == TTABLE && (tabledepth<3); )
++tabledepth;
}
if ( type == TNUMBER && arg.isinttype() )
type = TINT;
if ( type == TUSERDATA ) {
Class c = arg.touserdata().getClass();
for ( ; c.isArray() && (tabledepth<3); ) {
c = c.getComponentType();
++tabledepth;
}
}
public int hashCode() {
return hash;
}
public boolean equals( Object o ) {
return ( o instanceof ParamsList )?
Arrays.equals( classes, ((ParamsList) o).classes ):
false;
}
return (type & 0xF) | (tabledepth << 4);
}
public static int paramsCountFromSig( long paramssig ) {
return ((int) paramssig) & 0x3F;
}
public static int paramTypeFromSig(long paramssig, int argindex) {
return ((int) (paramssig>>(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) {
@@ -284,11 +319,12 @@ public class LuajavaLib extends OneArgFunction {
try {
// find the method
Object instance = args.touserdata(1);
ParamsList params = new ParamsList( args );
Method meth = resolveMethod( clazz, s, params );
Varargs methargs = args.subargs(2);
long paramssig = LuajavaLib.paramsSignatureOf(methargs);
Method meth = resolveMethod( clazz, s, paramssig );
// coerce the arguments
Object[] margs = CoerceLuaToJava.coerceArgs( params.values, meth.getParameterTypes() );
Object[] margs = CoerceLuaToJava.coerceArgs( methargs, meth.getParameterTypes() );
Object result = meth.invoke( instance, margs );
// coerce the result
@@ -307,7 +343,7 @@ public class LuajavaLib extends OneArgFunction {
private static Map consIndex =
new HashMap();
private static Constructor resolveConstructor(Class clazz, ParamsList params ) {
private static Constructor resolveConstructor(Class clazz, long paramssig ) {
// get the cache
Map cache = (Map) consCache.get( clazz );
@@ -315,7 +351,7 @@ public class LuajavaLib extends OneArgFunction {
consCache.put( clazz, cache = new HashMap() );
// look up in the cache
Constructor c = (Constructor) cache.get( params );
Constructor c = (Constructor) cache.get( paramssig );
if ( c != null )
return c;
@@ -335,7 +371,7 @@ public class LuajavaLib extends OneArgFunction {
}
// figure out best list of arguments == supplied args
Integer n = new Integer( params.classes.length );
Integer n = new Integer( LuajavaLib.paramsCountFromSig(paramssig) );
List list = (List) index.get(n);
if ( list == null )
throw new IllegalArgumentException("no constructor with "+n+" args");
@@ -345,7 +381,8 @@ public class LuajavaLib extends OneArgFunction {
int besti = 0;
for ( int i=0, size=list.size(); i<size; i++ ) {
Constructor con = (Constructor) list.get(i);
int s = CoerceLuaToJava.scoreParamTypes(params.values, con.getParameterTypes());
int paramType = LuajavaLib.paramTypeFromSig(paramssig, 0);
int s = CoerceLuaToJava.scoreParamTypes(paramType, con.getParameterTypes());
if ( s < bests ) {
bests = s;
besti = i;
@@ -354,7 +391,7 @@ public class LuajavaLib extends OneArgFunction {
// put into cache
c = (Constructor) list.get(besti);
cache.put( params, c );
cache.put( paramssig, c );
return c;
}
@@ -365,7 +402,7 @@ public class LuajavaLib extends OneArgFunction {
private static Map methIndex =
new HashMap();
private static Method resolveMethod(Class clazz, String methodName, ParamsList params ) {
private static Method resolveMethod(Class clazz, String methodName, long paramssig ) {
// get the cache
Map nameCache = (Map) methCache.get( clazz );
@@ -376,7 +413,7 @@ public class LuajavaLib extends OneArgFunction {
nameCache.put( methodName, cache = new HashMap() );
// look up in the cache
Method m = (Method) cache.get( params );
Method m = (Method) cache.get( paramssig );
if ( m != null )
return m;
@@ -403,7 +440,7 @@ public class LuajavaLib extends OneArgFunction {
Map map = (Map) index.get(methodName);
if ( map == null )
throw new IllegalArgumentException("no method named '"+methodName+"'");
Integer n = new Integer( params.classes.length );
Integer n = new Integer( LuajavaLib.paramsCountFromSig( paramssig ) );
List list = (List) map.get(n);
if ( list == null )
throw new IllegalArgumentException("no method named '"+methodName+"' with "+n+" args");
@@ -413,7 +450,7 @@ public class LuajavaLib extends OneArgFunction {
int besti = 0;
for ( int i=0, size=list.size(); i<size; i++ ) {
Method meth = (Method) list.get(i);
int s = CoerceLuaToJava.scoreParamTypes(params.values, meth.getParameterTypes());
int s = CoerceLuaToJava.scoreParamTypes(paramssig, meth.getParameterTypes());
if ( s < bests ) {
bests = s;
besti = i;
@@ -422,7 +459,7 @@ public class LuajavaLib extends OneArgFunction {
// put into cache
m = (Method) list.get(besti);
cache.put( params, m );
cache.put( paramssig, m );
return m;
}

View File

@@ -48,7 +48,7 @@ public class LuaJavaCoercionTest extends TestCase {
String s = new String("777");
LuaValue v = CoerceJavaToLua.coerce(s);
assertEquals( LuaString.class, v.getClass() );
assertEquals( "777", v.tojstring() );
assertEquals( "777", v.toString() );
}
public void testLuaStringToJavaString() {
@@ -112,37 +112,37 @@ public class LuaJavaCoercionTest extends TestCase {
LuaValue va = CoerceJavaToLua.coerce(a);
LuaValue vb = CoerceJavaToLua.coerce(b);
LuaValue vc = CoerceJavaToLua.coerce(c);
tc.set( ONE, new LuaTable() );
tc.set( 1, new LuaTable() );
int saa = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { la }, new Class[] { int.class } );
int sab = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { la }, new Class[] { int[].class } );
int sac = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { la }, new Class[] { int[][].class } );
int saa = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(la), new Class[] { int.class } );
int sab = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(la), new Class[] { int[].class } );
int sac = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(la), new Class[] { int[][].class } );
assertTrue( saa < sab );
assertTrue( saa < sac );
int sba = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { tb }, new Class[] { int.class } );
int sbb = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { tb }, new Class[] { int[].class } );
int sbc = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { tb }, new Class[] { int[][].class } );
int sba = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(tb), new Class[] { int.class } );
int sbb = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(tb), new Class[] { int[].class } );
int sbc = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(tb), new Class[] { int[][].class } );
assertTrue( sbb < sba );
assertTrue( sbb < sbc );
int sca = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { tc }, new Class[] { int.class } );
int scb = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { tc }, new Class[] { int[].class } );
int scc = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { tc }, new Class[] { int[][].class } );
int sca = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(tc), new Class[] { int.class } );
int scb = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(tc), new Class[] { int[].class } );
int scc = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(tc), new Class[] { int[][].class } );
assertTrue( scc < sca );
assertTrue( scc < scb );
int vaa = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { va }, new Class[] { int.class } );
int vab = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { va }, new Class[] { int[].class } );
int vac = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { va }, new Class[] { int[][].class } );
int vaa = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(va), new Class[] { int.class } );
int vab = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(va), new Class[] { int[].class } );
int vac = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(va), new Class[] { int[][].class } );
assertTrue( vaa < vab );
assertTrue( vaa < vac );
int vba = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { vb }, new Class[] { int.class } );
int vbb = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { vb }, new Class[] { int[].class } );
int vbc = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { vb }, new Class[] { int[][].class } );
int vba = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(vb), new Class[] { int.class } );
int vbb = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(vb), new Class[] { int[].class } );
int vbc = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(vb), new Class[] { int[][].class } );
assertTrue( vbb < vba );
assertTrue( vbb < vbc );
int vca = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { vc }, new Class[] { int.class } );
int vcb = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { vc }, new Class[] { int[].class } );
int vcc = CoerceLuaToJava.scoreParamTypes( new LuaValue[] { vc }, new Class[] { int[][].class } );
int vca = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(vc), new Class[] { int.class } );
int vcb = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(vc), new Class[] { int[].class } );
int vcc = CoerceLuaToJava.scoreParamTypes( LuajavaLib.paramsSignatureOf(vc), new Class[] { int[][].class } );
assertTrue( vcc < vca );
assertTrue( vcc < vcb );
}
@@ -154,30 +154,28 @@ public class LuaJavaCoercionTest extends TestCase {
public String sample(int[][] a) { return "int-array-array-args "+a[0][0]+","+a[0][1]+","+a[1][0]+","+a[1][1]; }
}
private static final LuaString SAMPLE = LuaString.valueOf("sample");
public void testIntArrayParameterMatching() {
LuaValue v = CoerceJavaToLua.coerce(new SampleClass());
// get sample field, call with no arguments
LuaValue method = v.get(SAMPLE);
LuaValue result = method.call(v);
assertEquals( "void-args", result.tojstring() );
LuaValue result = v.method("sample");
assertEquals( "void-args", result.toString() );
// get sample field, call with one arguments
LuaValue arg = CoerceJavaToLua.coerce(new Integer(123));
result = method.call(v,arg);
assertEquals( "int-args 123", result.tojstring() );
result = v.method("sample",arg);
assertEquals( "int-args 123", result.toString() );
// get sample field, call with array argument
arg = CoerceJavaToLua.coerce(new int[]{345,678});
result = method.call(v,arg);
assertEquals( "int-array-args 345,678", result.tojstring() );
result = v.method("sample",arg);
assertEquals( "int-array-args 345,678", result.toString() );
// get sample field, call with two-d array argument
arg = CoerceJavaToLua.coerce(new int[][]{{22,33},{44,55}});
result = method.call(v,arg);
assertEquals( "int-array-array-args 22,33,44,55", result.tojstring() );
result = v.method("sample",arg);
assertEquals( "int-array-array-args 22,33,44,55", result.toString() );
}
public static final class SomeException extends RuntimeException {
@@ -198,7 +196,7 @@ public class LuaJavaCoercionTest extends TestCase {
LuaValue status = vresult.arg1();
LuaValue message = vresult.arg(2);
assertEquals( LuaValue.FALSE, status );
assertEquals( "vm error: "+SomeException.class.getName()+": this is some message", message.tojstring() );
assertEquals( "vm error: "+SomeException.class.getName()+": this is some message", message.toString() );
}
public void testLuaErrorCause() {
@@ -234,7 +232,7 @@ public class LuaJavaCoercionTest extends TestCase {
"} )\n";
Varargs chunk = _G.get("loadstring").call(LuaValue.valueOf(script));
if ( ! chunk.arg1().toboolean() )
fail( chunk.arg(2).tojstring() );
fail( chunk.arg(2).toString() );
LuaValue result = chunk.arg1().call();
Object u = result.touserdata();
VarArgsInterface v = (VarArgsInterface) u;