diff --git a/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaClass.java b/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaClass.java index 93e7f5efb0..699d09c7da 100644 --- a/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaClass.java +++ b/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaClass.java @@ -82,7 +82,6 @@ static JavaClass forClass(Class c) { JavaClass(Class c) { super(c); - this.jclass = this; this.bindMethods = c.isAnnotationPresent(LuaBindMethods.class); // planetiler change: compute these maps eagerly computeFields(); diff --git a/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaInstance.java b/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaInstance.java index 8f20998e3f..4667d55ffa 100644 --- a/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaInstance.java +++ b/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaInstance.java @@ -50,14 +50,16 @@ */ class JavaInstance extends LuaUserdata { - volatile JavaClass jclass; + private final JavaClass jclass; private final Map boundMethods; JavaInstance(Object instance) { super(instance); // planetiler change: when class annotated with @LuaBindMethods, allow methods to be called with instance.method() - if (m_instance.getClass().isAnnotationPresent(LuaBindMethods.class)) { - boundMethods = jclass().getMethods().entrySet().stream().collect(Collectors.toMap( + var clazz = m_instance.getClass(); + jclass = this instanceof JavaClass c ? c : JavaClass.forClass(clazz); + if (clazz.isAnnotationPresent(LuaBindMethods.class)) { + boundMethods = jclass.getMethods().entrySet().stream().collect(Collectors.toMap( Map.Entry::getKey, entry -> new BoundMethod(entry.getValue()) )); @@ -111,25 +113,13 @@ public Varargs invoke(Varargs args) { } } - private JavaClass jclass() { - if (jclass == null) { - synchronized (this) { - if (jclass == null) { - jclass = JavaClass.forClass(m_instance.getClass()); - } - } - } - return jclass; - } - public LuaValue get(LuaValue key) { // planetiler change: allow lists to be accessed as tables if (m_instance instanceof List c) { int idx = key.toint(); return idx <= 0 || idx > c.size() ? LuaValue.NIL : CoerceJavaToLua.coerce(c.get(idx - 1)); } - JavaClass clazz = jclass(); - Field f = clazz.getField(key); + Field f = jclass.getField(key); if (f != null) try { return CoerceJavaToLua.coerce(f.get(m_instance)); @@ -137,7 +127,7 @@ public LuaValue get(LuaValue key) { throw new LuaError(e); } // planetiler change: allow getter methods - var getter = clazz.getGetter(key); + var getter = jclass.getGetter(key); if (getter != null) { try { return CoerceJavaToLua.coerce(getter.get(m_instance)); @@ -145,10 +135,10 @@ public LuaValue get(LuaValue key) { throw new LuaError(e); } } - LuaValue m = boundMethods != null ? boundMethods.get(key) : clazz.getMethod(key); + LuaValue m = boundMethods != null ? boundMethods.get(key) : jclass.getMethod(key); if (m != null) return m; - Class c = clazz.getInnerClass(key); + Class c = jclass.getInnerClass(key); if (c != null) return JavaClass.forClass(c); @@ -171,8 +161,7 @@ public void set(LuaValue key, LuaValue value) { c.set(key.toint() - 1, CoerceLuaToJava.coerce(value, Object.class)); return; } - JavaClass clazz = jclass(); - Field f = clazz.getField(key); + Field f = jclass.getField(key); if (f != null) try { f.set(m_instance, CoerceLuaToJava.coerce(value, f.getType())); @@ -181,7 +170,7 @@ public void set(LuaValue key, LuaValue value) { throw new LuaError(e); } // planetiler change: allow setter methods - var setter = clazz.getSetter(key); + var setter = jclass.getSetter(key); if (setter != null) { try { setter.set(m_instance, CoerceLuaToJava.coerce(value, setter.type())); diff --git a/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaMethod.java b/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaMethod.java index 8c27760559..11a01eccfd 100644 --- a/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaMethod.java +++ b/planetiler-experimental/src/main/java/org/luaj/vm2/lib/jse/JavaMethod.java @@ -24,6 +24,8 @@ import java.lang.reflect.InaccessibleObjectException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.luaj.vm2.LuaError; @@ -148,10 +150,25 @@ LuaValue invokeMethod(Object instance, Varargs args) { */ static class Overload extends LuaFunction { - final JavaMethod[] methods; + private final JavaMethod[][] methodsByArgCount; Overload(JavaMethod[] methods) { - this.methods = methods; + int max = 0; + // planetiler change: precompute which methods are valid based on number of args + for (JavaMethod method : methods) { + max = Math.max(max, method.fixedargs.length + (method.varargs != null ? 2 : 0)); + } + this.methodsByArgCount = new JavaMethod[max + 1][]; + for (int nargs = 0; nargs <= max; nargs++) { + List methodsForCount = new ArrayList<>(); + for (JavaMethod javaMethod : methods) { + int fixed = javaMethod.fixedargs.length; + if (nargs == fixed || nargs > fixed && javaMethod.varargs != null) { + methodsForCount.add(javaMethod); + } + } + methodsByArgCount[nargs] = methodsForCount.isEmpty() ? methods : methodsForCount.toArray(JavaMethod[]::new); + } } public LuaValue call() { @@ -176,14 +193,22 @@ public Varargs invoke(Varargs args) { private LuaValue invokeBestMethod(Object instance, Varargs args) { JavaMethod best = null; - int score = Integer.MAX_VALUE; - for (int i = 0; i < methods.length; i++) { - int s = methods[i].score(args); - if (s < score) { - score = s; - best = methods[i]; - if (score == 0) - break; + + // first pass: filter possible methods by number of args provided + JavaMethod[] methods = methodsByArgCount[Math.min(methodsByArgCount.length - 1, args.narg())]; + if (methods.length == 1) { + best = methods[0]; + } else if (methods.length != 0) { + int score = Integer.MAX_VALUE; + // if there are multiple, then pick the one with the best score + for (JavaMethod javaMethod : methods) { + int s = javaMethod.score(args); + if (s < score) { + score = s; + best = javaMethod; + if (score == 0) + break; + } } }