From 3f53869dcca8bd4068c918acec4ac4628e52c8ff Mon Sep 17 00:00:00 2001 From: michaeloffner Date: Tue, 17 Dec 2024 11:32:31 +0100 Subject: [PATCH] improve performance with dynamic invoker --- .../lucee/runtime/reflection/Reflector.java | 23 +-- .../dynamic/DynamicClassLoader.java | 2 +- .../transformer/dynamic/DynamicInvoker.java | 171 +++++++++++++----- .../lucee/transformer/dynamic/meta/Clazz.java | 86 ++++----- .../dynamic/meta/FunctionMember.java | 23 +++ .../dynamic/meta/dynamic/ClazzDynamic.java | 23 +-- .../meta/dynamic/FunctionMemberDynamic.java | 27 +++ .../reflection/FunctionMemberReflection.java | 27 +++ loader/build.xml | 2 +- loader/pom.xml | 2 +- 10 files changed, 263 insertions(+), 123 deletions(-) diff --git a/core/src/main/java/lucee/runtime/reflection/Reflector.java b/core/src/main/java/lucee/runtime/reflection/Reflector.java index 10db76e39a..09e195eed6 100755 --- a/core/src/main/java/lucee/runtime/reflection/Reflector.java +++ b/core/src/main/java/lucee/runtime/reflection/Reflector.java @@ -255,13 +255,13 @@ public static Class[] getClasses(Object[] objs) { public static Class toReferenceClass(Class c) { if (c != null && c.isPrimitive()) { if (c == boolean.class) return Boolean.class; + if (c == int.class) return Integer.class; + if (c == long.class) return Long.class; + if (c == double.class) return Double.class; if (c == byte.class) return Byte.class; if (c == short.class) return Short.class; if (c == char.class) return Character.class; - if (c == int.class) return Integer.class; - if (c == long.class) return Long.class; if (c == float.class) return Float.class; - if (c == double.class) return Double.class; } return c; } @@ -623,14 +623,6 @@ public static ConstructorInstance getConstructorInstance(Class clazz, Object[] a static int count = 0; - public static Object[] cleanArgs(Object[] args) { - if (args == null) { - return new Object[0]; - } - return args; - - } - public static Object[] cleanArgsOld(Object[] args) { if (args == null) { return new Object[0]; @@ -892,6 +884,15 @@ public static void checkAccessibility(Class clazz, Key methodName) { } } + public static void checkAccessibility(Clazz clazz, Key methodName) { + if (methodName.equals(KeyConstants._exit)) { // TODO better implementation + Class cl = clazz.getDeclaringClass(); + if ((cl == System.class || cl == Runtime.class)) { // TODO better implementation + throw new PageRuntimeException(new SecurityException("Calling the exit method is not allowed")); + } + } + } + /* * private static void checkAccesibilityx(Object obj, Key methodName) { * if(methodName.equals(SET_ACCESSIBLE) && obj instanceof Member) { if(true) return; Member diff --git a/core/src/main/java/lucee/transformer/dynamic/DynamicClassLoader.java b/core/src/main/java/lucee/transformer/dynamic/DynamicClassLoader.java index 1eb7af3fa4..8eb89eb923 100644 --- a/core/src/main/java/lucee/transformer/dynamic/DynamicClassLoader.java +++ b/core/src/main/java/lucee/transformer/dynamic/DynamicClassLoader.java @@ -184,7 +184,6 @@ private Class loadClass(String name, boolean resolve, boolean loadFromFS) thr } private Class _loadClass(String name, byte[] barr) { - // print.e(">>>>" + name); Class clazz = defineClass(name, barr, 0, barr.length); if (clazz != null) { loadedClasses.put(name, ""); @@ -273,6 +272,7 @@ public boolean hasClass(String className) { } public boolean isClassLoaded(String className) { + if (loadedClasses.containsKey(className)) return true; return findLoadedClass(className) != null; } diff --git a/core/src/main/java/lucee/transformer/dynamic/DynamicInvoker.java b/core/src/main/java/lucee/transformer/dynamic/DynamicInvoker.java index 0874913434..ca2f390596 100644 --- a/core/src/main/java/lucee/transformer/dynamic/DynamicInvoker.java +++ b/core/src/main/java/lucee/transformer/dynamic/DynamicInvoker.java @@ -4,6 +4,9 @@ import java.io.IOException; import java.io.InputStreamReader; import java.lang.instrument.UnmodifiableClassException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; @@ -31,7 +34,6 @@ import org.objectweb.asm.Type; import lucee.aprint; -import lucee.commons.digest.HashUtil; import lucee.commons.io.SystemUtil; import lucee.commons.io.log.Log; import lucee.commons.io.res.Resource; @@ -42,6 +44,7 @@ import lucee.commons.lang.SystemOut; import lucee.loader.engine.CFMLEngineFactory; import lucee.runtime.exp.PageException; +import lucee.runtime.op.Caster; import lucee.runtime.reflection.Reflector; import lucee.runtime.type.Collection.Key; import lucee.runtime.type.KeyImpl; @@ -55,6 +58,7 @@ import lucee.transformer.dynamic.meta.FunctionMember; import lucee.transformer.dynamic.meta.LegacyMethod; import lucee.transformer.dynamic.meta.Method; +import lucee.transformer.dynamic.meta.dynamic.ClazzDynamic; public class DynamicInvoker { @@ -143,28 +147,24 @@ public Clazz getClazz(Class clazz, boolean useReflection) { return Clazz.getClazz(clazz, root, log, useReflection); } - /* - * private static double getClass = 0; private static double match = 0; private static double types - * = 0; private static double getDeclaringClass = 0; private static double lclassPath = 0; private - * static double pathName = 0; private static double clsLoader = 0; private static double - * loadInstance = 0; private static int count = 0; - */ + private static double getClass = 0; + private static double match = 0; + private static double getDeclaringClass = 0; + private static double pathName = 0; + private static double clsLoader = 0; + private static double loadInstance = 0; + public Pair createInstance(Class clazz, Key methodName, Object[] arguments, boolean convertComparsion) throws NoSuchMethodException, IOException, UnmodifiableClassException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, PageException { - /* - * count++; if ((count % 500) == 0) { print.e("-------------------"); print.e("getClass:" + - * getClass); print.e("match:" + match); print.e("types:" + types); print.e("getDeclaringClass:" + - * getDeclaringClass); print.e("lclassPath:" + lclassPath); print.e("pathName:" + pathName); - * print.e("clsLoader:" + clsLoader); print.e("loadInstance:" + loadInstance); } - */ - // double start = SystemUtil.millis(); boolean isConstr = methodName == null; - Clazz clazzz = getClazz(clazz); - - // getClass += (SystemUtil.millis() - start); + // Clazz clazzz = getClazz(clazz); + Clazz clazzz = ClazzDynamic.getInstance(clazz, root, log); + // Clazz clazzz = new ClazzReflection(clazz); + // getClass -= start; // start = SystemUtil.millis(); + // getClass += start; lucee.transformer.dynamic.meta.FunctionMember fm = null; lucee.transformer.dynamic.meta.Method method = null; @@ -176,36 +176,28 @@ public Pair createInstance(Class clazz, Key methodNam // Clazz clazz, final Collection.Key methodName, final Object[] args, boolean convertArgument fm = method = Clazz.getMethodMatch(clazzz, methodName, arguments, true, convertComparsion); } - // match += (SystemUtil.millis() - start); + // match -= start; // start = SystemUtil.millis(); + // match += start; - // types += (SystemUtil.millis() - start); - // start = SystemUtil.millis(); clazz = fm.getDeclaringClass(); // we wanna go as low as possible, to be as open as possible also this avoid not allow to access - // getDeclaringClass += (SystemUtil.millis() - start); + // getDeclaringClass -= start; // start = SystemUtil.millis(); + // getDeclaringClass += start; - StringBuilder sbClassPath = new StringBuilder(); - sbClassPath.append(clazz.getName().replace('.', '/')).append('/').append(isConstr ? "____init____" : fm.getName()); - if (fm.getArgumentCount() > 0) { - StringBuilder sbArgs = new StringBuilder(); - for (String arg: fm.getArguments()) { - sbArgs.append(':').append(arg); - } - sbClassPath.append('_').append(HashUtil.create64BitHashAsString(sbArgs, Character.MAX_RADIX)); - } - // lclassPath += (SystemUtil.millis() - start); - // start = SystemUtil.millis(); + String className = fm.getClassName(); - String classPath = Clazz.getPackagePrefix() + sbClassPath.toString();// StringUtil.replace(sbClassPath.toString(), "javae/lang/", "java_lang/", false); - String className = classPath.replace('/', '.'); - - // pathName += (SystemUtil.millis() - start); + // pathName -= start; // start = SystemUtil.millis(); + // pathName += start; + DynamicClassLoader loader = getCL(clazz); - // clsLoader += (SystemUtil.millis() - start); + + // clsLoader -= start; // start = SystemUtil.millis(); + // clsLoader += start; + if (loader.hasClass(className)) { try { return new Pair(fm, loader.loadInstance(className)); @@ -215,9 +207,9 @@ public Pair createInstance(Class clazz, Key methodNam // simply ignore when fail } // finally { - // loadInstance += (SystemUtil.millis() - start); + // loadInstance -= start; // start = SystemUtil.millis(); - + // loadInstance += start; // } } synchronized (SystemUtil.createToken("dyninvocer", className)) { @@ -226,7 +218,7 @@ public Pair createInstance(Class clazz, Key methodNam ClassWriter cw = ASMUtil.getClassWriter(); MethodVisitor mv; String abstractClassPath = "java/lang/Object"; - cw.visit(ASMUtil.getJavaVersionForBytecodeGeneration(), Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, classPath, + cw.visit(ASMUtil.getJavaVersionForBytecodeGeneration(), Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, fm.getClassPath(), "Ljava/lang/Object;Ljava/util/function/BiFunction;", "java/lang/Object", new String[] { "java/util/function/BiFunction" }); // Constructor @@ -428,12 +420,109 @@ public static Type[] getArgumentTypes(Constructor constructor) { return Type.getArgumentTypes(descriptor.toString()); } - public static void main(String[] args) throws Exception { + public static class TestMule { + public void test(int x) { + + } + } + + public Pair createInstance2(Class clazz, Key methodName, Object[] arguments, boolean convertComparsion) { + + return null; + } + + public static void main(String[] args) throws Throwable { System.setProperty("lucee.allow.reflection", "false"); Resource classes = ResourcesImpl.getFileResourceProvider().getResource("/Users/mic/tmp8/classes/"); ResourceUtil.deleteContent(classes, null); DynamicInvoker e = new DynamicInvoker(classes); + DynamicInvoker.getInstance(classes); + { + int rounds = 6; + int max = 500000; + long dynamicInvoker = Long.MAX_VALUE; + long dynamicInvoker2 = Long.MAX_VALUE; + long reflection = Long.MAX_VALUE; + long direct = Long.MAX_VALUE; + long methodHandle = Long.MAX_VALUE; + long tmp; + TestMule tm = new TestMule(); + Class clazz = tm.getClass(); + Class[] cargs = new Class[] { int.class }; + // reflection + for (int i = 0; i < rounds; i++) { + long start = System.currentTimeMillis(); + for (int y = 0; y < max; y++) { + clazz.getMethod("test", cargs).invoke(tm, new Object[] { 1 }); + } + tmp = System.currentTimeMillis() - start; + if (tmp < reflection) reflection = tmp; + } + + // invokeInstanceMethod + for (int i = 0; i < rounds; i++) { + long start = System.currentTimeMillis(); + for (int y = 0; y < max; y++) { + e.invokeInstanceMethod(tm, "test", new Object[] { 1 }, false); + } + tmp = System.currentTimeMillis() - start; + if (tmp < dynamicInvoker) dynamicInvoker = tmp; + } + + // invokeInstanceMethod + for (int i = 0; i < rounds; i++) { + long start = System.currentTimeMillis(); + for (int y = 0; y < max; y++) { + Reflector.getMethod(clazz, "test", cargs).invoke(tm, new Object[] { 1 }); + } + tmp = System.currentTimeMillis() - start; + if (tmp < dynamicInvoker2) dynamicInvoker2 = tmp; + } + + // MethodHandles + MethodHandles.Lookup lookup = MethodHandles.lookup(); + for (int i = 0; i < rounds; i++) { + long start = System.currentTimeMillis(); + for (int y = 0; y < max; y++) { + MethodType methodType = MethodType.methodType(void.class, int.class); + MethodHandle testHandle = lookup.findVirtual(clazz, "test", methodType); + testHandle.invoke(tm, 1); + + } + tmp = System.currentTimeMillis() - start; + if (tmp < methodHandle) methodHandle = tmp; + } + + // direct + for (int i = 0; i < rounds; i++) { + long start = System.currentTimeMillis(); + for (int y = 0; y < max; y++) { + tm.test(1); + } + tmp = System.currentTimeMillis() - start; + if (tmp < direct) direct = tmp; + } + + aprint.e("dynamicInvoker2:" + dynamicInvoker2); + aprint.e("dynamicInvoker:" + dynamicInvoker); + aprint.e("reflection:" + reflection); + aprint.e("methodHandle:" + reflection); + aprint.e("direct:" + direct); + + aprint.e("-------------------"); + double total = getClass + match + getDeclaringClass + pathName + clsLoader + loadInstance; + + aprint.e(((int) getClass) + ":" + Caster.toIntValue(100d / total * getClass) + "% :getClass"); + aprint.e(((int) match) + ":" + Caster.toIntValue(100d / total * match) + "% :match"); + aprint.e(((int) getDeclaringClass) + ":" + Caster.toIntValue(100d / total * getDeclaringClass) + "% :getDeclaringClass"); + aprint.e(((int) pathName) + ":" + Caster.toIntValue(100d / total * pathName) + "% :pathName"); + aprint.e(((int) clsLoader) + ":" + Caster.toIntValue(100d / total * clsLoader) + "% :clsLoader"); + aprint.e(((int) loadInstance) + ":" + Caster.toIntValue(100d / total * loadInstance) + "% :loadInstance"); + + if (true) return; + } + { List methods; diff --git a/core/src/main/java/lucee/transformer/dynamic/meta/Clazz.java b/core/src/main/java/lucee/transformer/dynamic/meta/Clazz.java index 2d19d8b06d..369ebdbd1c 100644 --- a/core/src/main/java/lucee/transformer/dynamic/meta/Clazz.java +++ b/core/src/main/java/lucee/transformer/dynamic/meta/Clazz.java @@ -57,7 +57,7 @@ public abstract class Clazz implements Serializable { public abstract Type getDeclaringType(); - protected abstract String id(); + public abstract String id(); public static boolean allowReflection() { if (allowReflection == null) { @@ -86,7 +86,6 @@ public static Clazz getClazz(Class clazz, Resource root, Log log) { } } - private static Map>> cachedMethods = new ConcurrentHashMap<>(); private static RefInteger nirvana = new RefIntegerImpl(); /* @@ -96,61 +95,20 @@ public static Clazz getClazz(Class clazz, Resource root, Log log) { * private static double lclasses2 = 0; private static int count = 0; */ + private static Map>> cachedMethods = new ConcurrentHashMap<>(); + public static Method getMethodMatch(Clazz clazz, final Collection.Key methodName, Object[] args, boolean convertArgument, boolean convertComparsion) throws NoSuchMethodException, IOException, PageException { - /* - * count++; if ((count % 500) == 0) { print.e("-------------------"); print.e("cleanArgs:" + - * cleanArgs); print.e("checkAccessibility:" + checkAccessibility); print.e("lmethods:" + lmethods); - * print.e("cache:" + cache); print.e("exact:" + exact); print.e("like:" + like); print.e("convert:" - * + convert); print.e("lclasses:" + lclasses); print.e("lclasses2:" + lclasses2); } double start = - * SystemUtil.millis(); - */ - - args = Reflector.cleanArgs(args); - - // cleanArgs += (SystemUtil.millis() - start); - // start = SystemUtil.millis(); List methods = clazz.getMethods(methodName.getString(), false, args.length); - - // lmethods += (SystemUtil.millis() - start); - // start = SystemUtil.millis(); - if (methods != null && methods.size() > 0) { - Class[] clazzArgs = Reflector.getClasses(args); - - // cache - StringBuilder sb = new StringBuilder(clazz.id()).append(methodName).append(';'); - for (Class cls: clazzArgs) { - sb.append(cls.getName()).append(';'); - } - - String key = sb.toString(); - - // get match from cache - SoftReference> sr = cachedMethods.get(key); - if (sr != null) { - Pair p = sr.get(); - if (p != null) { - // print.e("used cached match(" + p.getValue() + "):" + key + ":" + cachedMethods.size()); - // convert arguments - if (p.getValue()) { - // print.e("------- " + clazz.getDeclaringClass().getName() + ":" + methodName + " -----"); - Class[] trgArgs = p.getName().getArgumentClasses(); - for (int x = 0; x < trgArgs.length; x++) { - if (args[x] != null) args[x] = Reflector.convert(args[x], Reflector.toReferenceClass(trgArgs[x]), nirvana); - } - } - return p.getName(); - } + if (args.length == 0) { + return methods.get(0); } - // cache += (SystemUtil.millis() - start); - // start = SystemUtil.millis(); - Reflector.checkAccessibility(clazz.getDeclaringClass(), methodName); + Class[] clazzArgs = Reflector.getClasses(args); - // checkAccessibility += (SystemUtil.millis() - start); - // start = SystemUtil.millis(); + Reflector.checkAccessibility(clazz, methodName); // exact comparsion outer: for (Method m: methods) { @@ -159,8 +117,6 @@ public static Method getMethodMatch(Clazz clazz, final Collection.Key methodName for (int y = 0; y < parameterTypes.length; y++) { if (Reflector.toReferenceClass(parameterTypes[y]) != clazzArgs[y]) continue outer; } - // print.e("exact match:" + key + ":"); - cachedMethods.put(key, new SoftReference>(new Pair(m, Boolean.FALSE))); return m; } } @@ -174,14 +130,36 @@ public static Method getMethodMatch(Clazz clazz, final Collection.Key methodName for (int y = 0; y < parameterTypes.length; y++) { if (!Reflector.like(clazzArgs[y], Reflector.toReferenceClass(parameterTypes[y]))) continue outer; } - // print.e("like match:" + key + ":"); - cachedMethods.put(key, new SoftReference>(new Pair(m, Boolean.FALSE))); return m; } } // like += (SystemUtil.millis() - start); // start = SystemUtil.millis(); + // cache + StringBuilder sb = new StringBuilder(100).append(clazz.id()).append(methodName).append(';'); + for (Class cls: clazzArgs) { + sb.append(cls.getName()).append(';'); + } + String key = sb.toString(); + + // get match from cache + SoftReference> sr = cachedMethods.get(key); + if (sr != null) { + Pair p = sr.get(); + if (p != null) { + // print.e("used cached match(" + p.getValue() + "):" + key + ":" + cachedMethods.size()); + // convert arguments + if (p.getValue()) { + Class[] trgArgs = p.getName().getArgumentClasses(); + for (int x = 0; x < trgArgs.length; x++) { + if (args[x] != null) args[x] = Reflector.convert(args[x], Reflector.toReferenceClass(trgArgs[x]), nirvana); + } + } + return p.getName(); + } + } + // convert comparsion Pair result = null; int _rating = 0; @@ -309,7 +287,6 @@ else if (trgClassLoader != null) { } public static Constructor getConstructorMatch(Clazz clazz, Object[] args, boolean convertArgument, boolean convertComparsion) throws NoSuchMethodException, IOException { - args = Reflector.cleanArgs(args); List constructors = clazz.getConstructors(args.length); if (constructors != null && constructors.size() > 0) { Class[] clazzArgs = Reflector.getClasses(args); @@ -488,4 +465,5 @@ public static Class toClass(ClassLoader cl, Type type) throws ClassException { public static String getPackagePrefix() { return "lucee/invoc/wrap/v" + VERSION + "/"; } + } \ No newline at end of file diff --git a/core/src/main/java/lucee/transformer/dynamic/meta/FunctionMember.java b/core/src/main/java/lucee/transformer/dynamic/meta/FunctionMember.java index 269105cc56..3687118e9b 100644 --- a/core/src/main/java/lucee/transformer/dynamic/meta/FunctionMember.java +++ b/core/src/main/java/lucee/transformer/dynamic/meta/FunctionMember.java @@ -4,6 +4,8 @@ import org.objectweb.asm.Type; +import lucee.commons.digest.HashUtil; + public interface FunctionMember extends Serializable { public abstract String getName(); @@ -48,4 +50,25 @@ public interface FunctionMember extends Serializable { public abstract boolean inInterface(); + public abstract String getClassPath(); + + public abstract String getClassName(); + + public static String createClassPath(FunctionMember fm) { + StringBuilder sbClassPath = new StringBuilder(); + // sbClassPath.append(getDeclaringClassName().replace('.', '/')).append('/').append(isConstr ? + // "____init____" : fm.getName()); + sbClassPath.append(fm.getDeclaringClassName().replace('.', '/')).append('/'); + if (fm.getName() == null) sbClassPath.append("____init____"); + else sbClassPath.append(fm.getName()); + + if (fm.getArgumentCount() > 0) { + StringBuilder sbArgs = new StringBuilder(); + for (String arg: fm.getArguments()) { + sbArgs.append(':').append(arg); + } + sbClassPath.append('_').append(HashUtil.create64BitHashAsString(sbArgs, Character.MAX_RADIX)); + } + return Clazz.getPackagePrefix() + sbClassPath.toString(); + } } diff --git a/core/src/main/java/lucee/transformer/dynamic/meta/dynamic/ClazzDynamic.java b/core/src/main/java/lucee/transformer/dynamic/meta/dynamic/ClazzDynamic.java index d82673cfc8..73f2beaf76 100644 --- a/core/src/main/java/lucee/transformer/dynamic/meta/dynamic/ClazzDynamic.java +++ b/core/src/main/java/lucee/transformer/dynamic/meta/dynamic/ClazzDynamic.java @@ -54,6 +54,7 @@ public class ClazzDynamic extends Clazz { private final FunctionMember[] declaredConstructors; private String clid; + private String id; private static Map clids = new IdentityHashMap<>(); private static String systemId; @@ -68,17 +69,9 @@ public class ClazzDynamic extends Clazz { */ public static ClazzDynamic getInstance(Class clazz, Resource dir, Log log) throws IOException { - - /* - * count++; if ((count % 500) == 0) { print.e("-------------------"); - * print.e("generateClassLoderId:" + generateClassLoderId); print.e("path:" + path); - * print.e("isFile:" + isFile); print.e("deserialize:" + deserialize); print.e("put:" + put); - * print.e("neww:" + neww); print.e("serialize:" + serialize); print.e("done:" + done); } double - * start = SystemUtil.millis(); - */ - ClazzDynamic cd = null; Reference sr = classes.get(clazz); + if (sr == null || (cd = sr.get()) == null) { synchronized (clazz) { sr = classes.get(clazz); @@ -223,11 +216,13 @@ public Type getDeclaringType() { @Override public String id() { - if (clid == null) { - clid = generateClassLoderId(clazz); + if (id == null) { + if (clid == null) { + clid = generateClassLoderId(clazz); + } + id = clid + ":" + clazz.getName(); } - - return clid + ":" + clazz.getName(); + return id; } @Override @@ -304,7 +299,7 @@ public List getMethods(String methodName, boolean nameCaseSensitive, int for (FunctionMember fm: methods) { if (/* fm.isPublic() && */ - (argumentLength < 0 || argumentLength == fm.getArgumentCount()) && + (argumentLength == fm.getArgumentCount() || argumentLength < 0) && (methodName == null || (nameCaseSensitive ? methodName.equals(fm.getName()) : methodName.equalsIgnoreCase(fm.getName()))) diff --git a/core/src/main/java/lucee/transformer/dynamic/meta/dynamic/FunctionMemberDynamic.java b/core/src/main/java/lucee/transformer/dynamic/meta/dynamic/FunctionMemberDynamic.java index 6195d50a11..0ac5e4daf7 100644 --- a/core/src/main/java/lucee/transformer/dynamic/meta/dynamic/FunctionMemberDynamic.java +++ b/core/src/main/java/lucee/transformer/dynamic/meta/dynamic/FunctionMemberDynamic.java @@ -48,6 +48,9 @@ abstract class FunctionMemberDynamic implements FunctionMember { protected transient Type[] expTypes; protected transient String[] expNames; + private String classPath; + private String className; + public FunctionMemberDynamic(String name) { this.name = name; } @@ -489,4 +492,28 @@ public String toString() { return sb.toString(); } + @Override + public String getClassPath() { + if (classPath == null) { + synchronized (this) { + if (classPath == null) { + classPath = FunctionMember.createClassPath(this); + } + } + } + return classPath; + } + + @Override + public String getClassName() { + if (className == null) { + synchronized (this) { + if (className == null) { + className = getClassPath().replace('/', '.'); + } + } + } + return className; + } + } diff --git a/core/src/main/java/lucee/transformer/dynamic/meta/reflection/FunctionMemberReflection.java b/core/src/main/java/lucee/transformer/dynamic/meta/reflection/FunctionMemberReflection.java index 47c593bb9d..81470555b2 100644 --- a/core/src/main/java/lucee/transformer/dynamic/meta/reflection/FunctionMemberReflection.java +++ b/core/src/main/java/lucee/transformer/dynamic/meta/reflection/FunctionMemberReflection.java @@ -11,6 +11,8 @@ abstract class FunctionMemberReflection implements FunctionMember { private static final long serialVersionUID = 6812458458981023205L; private Executable executable; + private String classPath; + private String className; public FunctionMemberReflection(Executable method) { this.executable = method; @@ -147,4 +149,29 @@ public String[] getExceptions() { public String toString() { return executable.toString(); } + + @Override + public String getClassPath() { + if (classPath == null) { + synchronized (this) { + if (classPath == null) { + classPath = FunctionMember.createClassPath(this); + } + } + } + return classPath; + } + + @Override + public String getClassName() { + if (className == null) { + synchronized (this) { + if (className == null) { + className = getClassPath().replace('/', '.'); + } + } + } + return className; + } + } diff --git a/loader/build.xml b/loader/build.xml index ee95045d89..4549164558 100644 --- a/loader/build.xml +++ b/loader/build.xml @@ -2,7 +2,7 @@ - + diff --git a/loader/pom.xml b/loader/pom.xml index ca74f75ecf..1976f426f4 100644 --- a/loader/pom.xml +++ b/loader/pom.xml @@ -3,7 +3,7 @@ org.lucee lucee - 6.2.0.219-SNAPSHOT + 6.2.0.220-SNAPSHOT jar Lucee Loader Build