From 01717be197986d327b69af4e449be37920158b9e Mon Sep 17 00:00:00 2001 From: michaeloffner Date: Fri, 22 Mar 2024 20:42:08 +0100 Subject: [PATCH] inital version switching from reflection to ASM bytecode genration --- ant/build-core.xml | 2 +- .../java/lucee/runtime/config/ConfigImpl.java | 19 ++ .../java/lucee/runtime/config/ConfigPro.java | 3 + .../lucee/runtime/config/ConfigWebImpl.java | 6 + .../config/SingleContextConfigWeb.java | 6 + .../lucee/runtime/engine/CFMLEngineImpl.java | 2 + .../runtime/functions/struct/JsonStruct.java | 12 +- .../functions/struct/LiteralStruct.java | 10 +- .../java/lucee/runtime/java/JavaObject.java | 47 ++-- .../lucee/runtime/reflection/Reflector.java | 204 +++++++++++++++--- .../reflection/pairs/MethodInstance.java | 125 +++++++++-- .../lucee/runtime/type/QueryColumnImpl.java | 2 +- .../lucee/runtime/type/util/MemberUtil.java | 2 +- loader/build.xml | 2 +- loader/pom.xml | 2 +- .../lucee/cli/servlet/ServletContextImpl.java | 7 - test/tickets/LDEV3556.cfc | 8 +- 17 files changed, 383 insertions(+), 76 deletions(-) diff --git a/ant/build-core.xml b/ant/build-core.xml index 2d0d618878..ee1aa2d4e6 100644 --- a/ant/build-core.xml +++ b/ant/build-core.xml @@ -26,7 +26,7 @@ - + diff --git a/core/src/main/java/lucee/runtime/config/ConfigImpl.java b/core/src/main/java/lucee/runtime/config/ConfigImpl.java index 5e8c5a24f6..3198412525 100755 --- a/core/src/main/java/lucee/runtime/config/ConfigImpl.java +++ b/core/src/main/java/lucee/runtime/config/ConfigImpl.java @@ -65,6 +65,7 @@ import lucee.commons.lang.ExceptionUtil; import lucee.commons.lang.Md5; import lucee.commons.lang.PhysicalClassLoader; +import lucee.commons.lang.SerializableObject; import lucee.commons.lang.StringUtil; import lucee.commons.lang.types.RefBoolean; import lucee.commons.net.IPRange; @@ -175,6 +176,7 @@ public abstract class ConfigImpl extends ConfigBase implements ConfigPro { private int mode = MODE_CUSTOM; private final Map rpcClassLoaders = new ConcurrentHashMap(); + private PhysicalClassLoader directClassLoader; private Map datasources = new HashMap(); private Map caches = new HashMap(); @@ -2252,6 +2254,23 @@ public ClassLoader getRPCClassLoader(boolean reload, ClassLoader[] parents) thro return rpccl; } + private static final Object dclt = new SerializableObject(); + + public PhysicalClassLoader getDirectClassLoader(boolean reload) throws IOException { + if (directClassLoader == null || reload) { + synchronized (dclt) { + if (directClassLoader == null || reload) { + Resource dir = getClassDirectory().getRealResource("direct/"); + if (!dir.exists()) { + ResourceUtil.createDirectoryEL(dir, true); + } + directClassLoader = new PhysicalClassLoader(this, dir, null); + } + } + } + return directClassLoader; + } + private String toKey(ClassLoader[] parents) { if (parents == null || parents.length == 0) return "orphan"; diff --git a/core/src/main/java/lucee/runtime/config/ConfigPro.java b/core/src/main/java/lucee/runtime/config/ConfigPro.java index e9b6cdc215..e974075e5f 100644 --- a/core/src/main/java/lucee/runtime/config/ConfigPro.java +++ b/core/src/main/java/lucee/runtime/config/ConfigPro.java @@ -18,6 +18,7 @@ import lucee.commons.io.res.util.ResourceClassLoader; import lucee.commons.lang.CharSet; import lucee.commons.lang.ClassException; +import lucee.commons.lang.PhysicalClassLoader; import lucee.commons.lang.types.RefBoolean; import lucee.runtime.CIPage; import lucee.runtime.Mapping; @@ -377,4 +378,6 @@ public Resource[] getResources(PageContext pc, Mapping[] mappings, String realPa public boolean getFormUrlAsStruct(); public int getReturnFormat(); + + public PhysicalClassLoader getDirectClassLoader(boolean reload) throws IOException; } diff --git a/core/src/main/java/lucee/runtime/config/ConfigWebImpl.java b/core/src/main/java/lucee/runtime/config/ConfigWebImpl.java index 4b0876eb42..4447520e51 100644 --- a/core/src/main/java/lucee/runtime/config/ConfigWebImpl.java +++ b/core/src/main/java/lucee/runtime/config/ConfigWebImpl.java @@ -12,6 +12,7 @@ import lucee.commons.io.res.Resource; import lucee.commons.io.res.ResourcesImpl.ResourceProviderFactory; +import lucee.commons.lang.PhysicalClassLoader; import lucee.runtime.config.gateway.GatewayMap; import lucee.runtime.exp.PageException; import lucee.runtime.writer.CFMLWriter; @@ -778,6 +779,11 @@ public java.lang.ClassLoader getRPCClassLoader(boolean arg0, java.lang.ClassLoad return instance.getRPCClassLoader(arg0, arg1); } + @Override + public PhysicalClassLoader getDirectClassLoader(boolean reload) throws IOException { + return instance.getDirectClassLoader(reload); + } + @Override public lucee.runtime.config.MockPool getDatasourceConnectionPool() { return instance.getDatasourceConnectionPool(); diff --git a/core/src/main/java/lucee/runtime/config/SingleContextConfigWeb.java b/core/src/main/java/lucee/runtime/config/SingleContextConfigWeb.java index dd5363f42d..8013082421 100644 --- a/core/src/main/java/lucee/runtime/config/SingleContextConfigWeb.java +++ b/core/src/main/java/lucee/runtime/config/SingleContextConfigWeb.java @@ -37,6 +37,7 @@ import lucee.commons.io.res.util.ResourceUtil; import lucee.commons.lang.CharSet; import lucee.commons.lang.ClassException; +import lucee.commons.lang.PhysicalClassLoader; import lucee.commons.lang.types.RefBoolean; import lucee.commons.lock.KeyLock; import lucee.runtime.CFMLFactory; @@ -708,6 +709,11 @@ public ClassLoader getRPCClassLoader(boolean reload, ClassLoader[] parents) thro return cs.getRPCClassLoader(reload, parents); } + @Override + public PhysicalClassLoader getDirectClassLoader(boolean reload) throws IOException { + return cs.getDirectClassLoader(reload); + } + @Override public Resource getCacheDir() { return cs.getCacheDir(); diff --git a/core/src/main/java/lucee/runtime/engine/CFMLEngineImpl.java b/core/src/main/java/lucee/runtime/engine/CFMLEngineImpl.java index a7deaf29ee..b380f707cb 100644 --- a/core/src/main/java/lucee/runtime/engine/CFMLEngineImpl.java +++ b/core/src/main/java/lucee/runtime/engine/CFMLEngineImpl.java @@ -184,6 +184,7 @@ import lucee.runtime.video.VideoUtil; import lucee.runtime.video.VideoUtilImpl; import lucee.servlet.http.HTTPServletImpl; +import lucee.transformer.direct.DirectCallEngine; /** * The CFMl Engine @@ -931,6 +932,7 @@ private ConfigServerImpl getConfigServerImpl(ConfigServerImpl existing, boolean } try { Resource context = getSeverContextConfigDirectory(factory); + DirectCallEngine.getInstance(context); ConfigServerImpl tmp = ConfigServerFactory.newInstance(this, initContextes, contextes, context, existing, essentialOnly); if (essentialOnly) { return tmp; diff --git a/core/src/main/java/lucee/runtime/functions/struct/JsonStruct.java b/core/src/main/java/lucee/runtime/functions/struct/JsonStruct.java index 2ee034e5c6..5b9c6b1ed1 100644 --- a/core/src/main/java/lucee/runtime/functions/struct/JsonStruct.java +++ b/core/src/main/java/lucee/runtime/functions/struct/JsonStruct.java @@ -19,11 +19,14 @@ package lucee.runtime.functions.struct; import lucee.runtime.PageContext; +import lucee.runtime.exp.FunctionException; import lucee.runtime.exp.PageException; +import lucee.runtime.ext.function.BIF; import lucee.runtime.ext.function.Function; +import lucee.runtime.op.Caster; import lucee.runtime.type.Struct; -public class JsonStruct implements Function { +public class JsonStruct extends BIF implements Function { private static final long serialVersionUID = 3030769464899375329L; @@ -31,4 +34,11 @@ public static Struct call(PageContext pc, Object[] objArr) throws PageException return Struct_._call(objArr, "invalid argument for JSON struct, only named arguments are allowed like {name:\"value\",name2:\"value2\"}", Struct.TYPE_LINKED); } + + @Override + public Object invoke(PageContext pc, Object[] args) throws PageException { + if (args.length < 1 || args.length > 1) throw new FunctionException(pc, "JsonStruct", 1, 1, args.length); + return Struct_._call(Caster.toNativeArray(args[0]), "invalid argument for JSON struct, only named arguments are allowed like {name:\"value\",name2:\"value2\"}", + Struct.TYPE_LINKED); + } } \ No newline at end of file diff --git a/core/src/main/java/lucee/runtime/functions/struct/LiteralStruct.java b/core/src/main/java/lucee/runtime/functions/struct/LiteralStruct.java index c7e4b7f012..824f649db7 100644 --- a/core/src/main/java/lucee/runtime/functions/struct/LiteralStruct.java +++ b/core/src/main/java/lucee/runtime/functions/struct/LiteralStruct.java @@ -19,16 +19,24 @@ package lucee.runtime.functions.struct; import lucee.runtime.PageContext; +import lucee.runtime.exp.FunctionException; import lucee.runtime.exp.PageException; +import lucee.runtime.ext.function.BIF; import lucee.runtime.ext.function.Function; +import lucee.runtime.op.Caster; import lucee.runtime.type.Struct; -public class LiteralStruct implements Function { +public class LiteralStruct extends BIF implements Function { private static final long serialVersionUID = 3030769464899375329L; public static Struct call(PageContext pc, Object[] objArr) throws PageException { return Struct_._call(objArr, "invalid argument for literal struct, only named arguments are allowed like {name:\"value\",name2:\"value2\"}", -1); + } + @Override + public Object invoke(PageContext pc, Object[] args) throws PageException { + if (args.length < 1 || args.length > 1) throw new FunctionException(pc, "LiteralStruct", 1, 1, args.length); + return Struct_._call(Caster.toNativeArray(args[0]), "invalid argument for literal struct, only named arguments are allowed like {name:\"value\",name2:\"value2\"}", -1); } } \ No newline at end of file diff --git a/core/src/main/java/lucee/runtime/java/JavaObject.java b/core/src/main/java/lucee/runtime/java/JavaObject.java index a103785894..18e97e604e 100644 --- a/core/src/main/java/lucee/runtime/java/JavaObject.java +++ b/core/src/main/java/lucee/runtime/java/JavaObject.java @@ -26,6 +26,7 @@ import java.util.Iterator; import java.util.List; +import lucee.print; import lucee.commons.io.log.Log; import lucee.commons.io.log.LogUtil; import lucee.runtime.PageContext; @@ -104,12 +105,12 @@ public Object get(PageContext pc, String propertyName) throws PageException { try { return mi.invoke(null); } - catch (IllegalAccessException e) { - throw Caster.toPageException(e); - } catch (InvocationTargetException e) { throw Caster.toPageException(e.getTargetException()); } + catch (Exception e) { + throw Caster.toPageException(e); + } } } // male Instance @@ -144,13 +145,18 @@ public Object get(PageContext pc, String propertyName, Object defaultValue) { // Getter MethodInstance mi = Reflector.getGetterEL(clazz, propertyName); if (mi != null) { - if (Modifier.isStatic(mi.getMethod().getModifiers())) { - try { - return mi.invoke(null); - } - catch (Exception e) { + try { + if (Modifier.isStatic(mi.getMethod().getModifiers())) { + try { + return mi.invoke(null); + } + catch (Exception e) { + } } } + catch (PageException e) { + return defaultValue; + } } try { return variableUtil(pc).get(pc, init(), propertyName, defaultValue); @@ -191,12 +197,12 @@ public Object set(PageContext pc, Collection.Key propertyName, Object value) thr try { return mi.invoke(null); } - catch (IllegalAccessException e) { - throw Caster.toPageException(e); - } catch (InvocationTargetException e) { throw Caster.toPageException(e.getTargetException()); } + catch (Exception e) { + throw Caster.toPageException(e); + } } } @@ -224,12 +230,12 @@ public Object setEL(PageContext pc, Collection.Key propertyName, Object value) { // Getter MethodInstance mi = Reflector.getSetter(clazz, propertyName.getString(), value, null); if (mi != null) { - if (Modifier.isStatic(mi.getMethod().getModifiers())) { - try { + try { + if (Modifier.isStatic(mi.getMethod().getModifiers())) { return mi.invoke(null); } - catch (Exception e) { - } + } + catch (Exception e) { } } @@ -259,7 +265,8 @@ else if (isInit) { try { // get method - MethodInstance mi = Reflector.getMethodInstance(this, clazz, KeyImpl.init(methodName), arguments); + // if ("toHexString".equals(methodName)) print.ds(); + MethodInstance mi = Reflector.getMethodInstance(clazz, KeyImpl.init(methodName), arguments); // call static method if exist if (Modifier.isStatic(mi.getMethod().getModifiers())) { return mi.invoke(null); @@ -270,7 +277,8 @@ else if (isInit) { } // invoke constructor and call instance method - return mi.invoke(init()); + Object obj = init(); + return mi.invoke(obj); } catch (InvocationTargetException e) { Throwable target = e.getTargetException(); @@ -278,6 +286,11 @@ else if (isInit) { throw Caster.toPageException(e.getTargetException()); } catch (Exception e) { + print.e("------------------------------"); + print.e(methodName); + print.e(clazz.getName()); + print.e(arguments); + throw Caster.toPageException(e); } } diff --git a/core/src/main/java/lucee/runtime/reflection/Reflector.java b/core/src/main/java/lucee/runtime/reflection/Reflector.java index d907a4ed36..8c73d7d7c1 100755 --- a/core/src/main/java/lucee/runtime/reflection/Reflector.java +++ b/core/src/main/java/lucee/runtime/reflection/Reflector.java @@ -18,6 +18,8 @@ */ package lucee.runtime.reflection; +import java.io.IOException; +import java.lang.instrument.UnmodifiableClassException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -43,6 +45,7 @@ import lucee.commons.io.res.Resource; import lucee.commons.lang.ClassUtil; import lucee.commons.lang.ExceptionUtil; +import lucee.commons.lang.Pair; import lucee.commons.lang.StringUtil; import lucee.commons.lang.types.RefInteger; import lucee.commons.lang.types.RefIntegerImpl; @@ -692,14 +695,80 @@ public static ConstructorInstance getConstructorInstance(Class clazz, Object[] a // "+clazz.getName()+"("+getDspMethods(getClasses(args))+") found"); } + public static Constructor getConstructor(Class clazz, Object[] args, boolean convertArgument) throws NoSuchMethodException { + args = cleanArgs(args); + Constructor[] constructors = cStorage.getConstructors(clazz, args.length);// getConstructors(clazz); + if (constructors != null) { + Class[] clazzArgs = getClasses(args); + // exact comparsion + outer: for (int i = 0; i < constructors.length; i++) { + if (constructors[i] != null) { + + Class[] parameterTypes = constructors[i].getParameterTypes(); + for (int y = 0; y < parameterTypes.length; y++) { + if (toReferenceClass(parameterTypes[y]) != clazzArgs[y]) continue outer; + } + return constructors[i]; + } + } + // like comparsion + outer: for (int i = 0; i < constructors.length; i++) { + if (constructors[i] != null) { + Class[] parameterTypes = constructors[i].getParameterTypes(); + for (int y = 0; y < parameterTypes.length; y++) { + if (!like(clazzArgs[y], toReferenceClass(parameterTypes[y]))) continue outer; + } + return constructors[i]; + } + } + // convert comparsion + Pair result = null; + int _rating = 0; + outer: for (int i = 0; i < constructors.length; i++) { + if (constructors[i] != null) { + RefInteger rating = (constructors.length > 1) ? new RefIntegerImpl(0) : null; + Class[] parameterTypes = constructors[i].getParameterTypes(); + Object[] newArgs = new Object[args.length]; + for (int y = 0; y < parameterTypes.length; y++) { + try { + newArgs[y] = convert(args[y], toReferenceClass(parameterTypes[y]), rating); + } + catch (PageException e) { + continue outer; + } + } + if (result == null || rating.toInt() > _rating) { + if (rating != null) _rating = rating.toInt(); + result = new Pair(constructors[i], newArgs); + } + // return new ConstructorInstance(constructors[i],newArgs); + } + } + if (result != null) { + if (convertArgument) { + Object[] newArgs = result.getValue(); + for (int x = 0; x < args.length; x++) { + args[x] = newArgs[x]; + } + } + return result.getName(); + } + } + throw new NoSuchMethodException("No matching Constructor for " + clazz.getName() + "(" + getDspMethods(getClasses(args)) + ") found"); + } + /** - * gets the MethodInstance matching given Parameter @param objMaybeNull maybe null @param clazz - * Class Of the Method to get @param methodName Name of the Method to get @param args Arguments of - * the Method to get @return return Matching Method @throws + * ATTENTION!!!! this method may will rewrite the arguments array + * + * @param clazz + * @param methodName + * @param args + * @param conversionNeeded + * @return + * @throws NoSuchMethodException */ - public static MethodInstance getMethodInstanceEL(Object objMaybeNull, Class clazz, final Collection.Key methodName, Object[] args) { - checkAccessibility(objMaybeNull, clazz, methodName); - args = cleanArgs(args); + public static Method getMethod(Class clazz, final Collection.Key methodName, final Object[] args, boolean convertArgument) throws NoSuchMethodException { + checkAccessibility(clazz, methodName); Method[] methods = mStorage.getMethods(clazz, methodName, args.length);// getDeclaredMethods(clazz); if (methods != null) { @@ -712,7 +781,7 @@ public static MethodInstance getMethodInstanceEL(Object objMaybeNull, Class claz for (int y = 0; y < parameterTypes.length; y++) { if (toReferenceClass(parameterTypes[y]) != clazzArgs[y]) continue outer; } - return new MethodInstance(methods[i], args); + return methods[i]; } } // like comparsion @@ -724,40 +793,79 @@ public static MethodInstance getMethodInstanceEL(Object objMaybeNull, Class claz for (int y = 0; y < parameterTypes.length; y++) { if (!like(clazzArgs[y], toReferenceClass(parameterTypes[y]))) continue outer; } - return new MethodInstance(methods[i], args); + return methods[i]; } } // convert comparsion // print.e("convert:" + methodName); - MethodInstance mi = null; + Pair result = null; int _rating = 0; outer: for (int i = 0; i < methods.length; i++) { if (methods[i] != null) { RefInteger rating = (methods.length > 1) ? new RefIntegerImpl(0) : null; Class[] parameterTypes = methods[i].getParameterTypes(); Object[] newArgs = new Object[args.length]; + for (int y = 0; y < parameterTypes.length; y++) { try { newArgs[y] = convert(args[y], toReferenceClass(parameterTypes[y]), rating); + } catch (PageException e) { continue outer; } } - if (mi == null || rating.toInt() > _rating) { + if (result == null || rating.toInt() > _rating) { if (rating != null) _rating = rating.toInt(); - mi = new MethodInstance(methods[i], newArgs); + result = new Pair(methods[i], newArgs); } - // return new MethodInstance(methods[i],newArgs); } } - return mi; + + if (result != null) { + if (convertArgument) { + Object[] newArgs = result.getValue(); + for (int x = 0; x < args.length; x++) { + args[x] = newArgs[x]; + } + } + return result.getName(); + } } - return null; + Class[] classes = getClasses(args); + // StringBuilder sb=null; + JavaObject jo; + Class c; + ConstructorInstance ci; + for (int i = 0; i < classes.length; i++) { + if (args[i] instanceof JavaObject) { + jo = (JavaObject) args[i]; + c = jo.getClazz(); + ci = Reflector.getConstructorInstance(c, new Object[0], null); + if (ci == null) { + + throw new NoSuchMethodException("The " + pos(i + 1) + " parameter of " + methodName + "(" + getDspMethods(classes) + ") ia an object created " + + "by the createObject function (JavaObject/JavaProxy). This object has not been instantiated because it does not have a constructor " + + "that takes zero arguments. " + Constants.NAME + + " cannot instantiate it for you, please use the .init(...) method to instantiate it with the correct parameters first"); + + } + } + } + /* + * the argument list contains objects created by createObject, that are no instantiated + * (first,third,10th) and because this object have no constructor taking no arguments, Lucee cannot + * instantiate them. you need first to instantiate this objects. + */ + throw new NoSuchMethodException("No matching Method for " + methodName + "(" + getDspMethods(classes) + ") found for " + Caster.toTypeName(clazz)); + } + + public static MethodInstance getMethodInstanceEL(Class clazz, final Collection.Key methodName, Object[] args) { + return new MethodInstance(clazz, methodName, args); } - private static Object[] cleanArgs(Object[] args) { + public static Object[] cleanArgs(Object[] args) { if (args == null) return args; ObjectIdentityHashSet done = new ObjectIdentityHashSet(); @@ -885,10 +993,14 @@ private static Object _clean(ObjectIdentityHashSet done, Object[] src) { * @param args Arguments of the Method to get * @return return Matching Method * @throws NoSuchMethodException + * @throws UnmodifiableClassException + * @throws IOException + * @throws ClassNotFoundException * @throws PageException */ - public static MethodInstance getMethodInstance(Object obj, Class clazz, Collection.Key methodName, Object[] args) throws NoSuchMethodException { - MethodInstance mi = getMethodInstanceEL(obj, clazz, methodName, args); + public static MethodInstance getMethodInstance(Class clazz, Collection.Key methodName, Object[] args) + throws NoSuchMethodException, ClassNotFoundException, IOException, UnmodifiableClassException { + MethodInstance mi = getMethodInstanceEL(clazz, methodName, args); if (mi != null) return mi; Class[] classes = getClasses(args); @@ -1053,11 +1165,18 @@ public static Object callMethod(Object obj, String methodName, Object[] args) th return callMethod(obj, KeyImpl.init(methodName), args); } - public static Object callMethod(Object obj, Collection.Key methodName, Object[] args) throws PageException { + public static Object callMethod(final Object obj, Collection.Key methodName, Object[] args) throws PageException { if (obj == null) { throw new ExpressionException("can't call method [" + methodName + "] on object, object is null"); } - MethodInstance mi = getMethodInstanceEL(obj, obj.getClass(), methodName, args); + + MethodInstance mi; + try { + mi = getMethodInstanceEL(obj.getClass(), methodName, args); + } + catch (Exception e) { + throw Caster.toPageException(e); + } if (mi == null) throw throwCall(obj, methodName, args); try { return mi.invoke(obj); @@ -1089,6 +1208,12 @@ else if (methodName.equals(SET_ACCESSIBLE)) { } } + private static void checkAccessibility(Class clazz, Key methodName) { + if (methodName.equals(EXIT) && (clazz == System.class || clazz == 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 @@ -1104,7 +1229,13 @@ public static Object callMethod(Object obj, Collection.Key methodName, Object[] } // checkAccesibility(obj,methodName); - MethodInstance mi = getMethodInstanceEL(obj, obj.getClass(), methodName, args); + MethodInstance mi; + try { + mi = getMethodInstanceEL(obj.getClass(), methodName, args); + } + catch (Exception e) { + return defaultValue; + } if (mi == null) return defaultValue; try { return mi.invoke(obj); @@ -1134,7 +1265,7 @@ public static ExpressionException throwCall(Object obj, Collection.Key methodNam */ public static Object callStaticMethod(Class clazz, Collection.Key methodName, Object[] args) throws PageException { try { - return getMethodInstance(null, clazz, methodName, args).invoke(null); + return getMethodInstance(clazz, methodName, args).invoke(null); } catch (InvocationTargetException e) { Throwable target = e.getTargetException(); @@ -1157,11 +1288,11 @@ public static Object callStaticMethod(Class clazz, Collection.Key methodName, Ob */ public static MethodInstance getGetter(Class clazz, String prop) throws PageException, NoSuchMethodException { String getterName = "get" + StringUtil.ucFirst(prop); - MethodInstance mi = getMethodInstanceEL(null, clazz, KeyImpl.init(getterName), ArrayUtil.OBJECT_EMPTY); + MethodInstance mi = getMethodInstanceEL(clazz, KeyImpl.init(getterName), ArrayUtil.OBJECT_EMPTY); if (mi == null) { String isName = "is" + StringUtil.ucFirst(prop); - mi = getMethodInstanceEL(null, clazz, KeyImpl.init(isName), ArrayUtil.OBJECT_EMPTY); + mi = getMethodInstanceEL(clazz, KeyImpl.init(isName), ArrayUtil.OBJECT_EMPTY); if (mi != null) { Method m = mi.getMethod(); Class rtn = m.getReturnType(); @@ -1187,9 +1318,14 @@ public static MethodInstance getGetter(Class clazz, String prop) throws PageExce */ public static MethodInstance getGetterEL(Class clazz, String prop) { prop = "get" + StringUtil.ucFirst(prop); - MethodInstance mi = getMethodInstanceEL(null, clazz, KeyImpl.init(prop), ArrayUtil.OBJECT_EMPTY); + MethodInstance mi = getMethodInstanceEL(clazz, KeyImpl.init(prop), ArrayUtil.OBJECT_EMPTY); if (mi == null) return null; - if (mi.getMethod().getReturnType() == void.class) return null; + try { + if (mi.getMethod().getReturnType() == void.class) return null; + } + catch (PageException e) { + return null; + } return mi; } @@ -1224,10 +1360,14 @@ public static Object callGetter(Object obj, String prop) throws PageException { * @return MethodInstance * @throws NoSuchMethodException * @throws PageException + * @throws UnmodifiableClassException + * @throws IOException + * @throws ClassNotFoundException */ - public static MethodInstance getSetter(Object obj, String prop, Object value) throws NoSuchMethodException { + public static MethodInstance getSetter(Object obj, String prop, Object value) + throws NoSuchMethodException, PageException, ClassNotFoundException, IOException, UnmodifiableClassException { prop = "set" + StringUtil.ucFirst(prop); - MethodInstance mi = getMethodInstance(obj, obj.getClass(), KeyImpl.init(prop), new Object[] { value }); + MethodInstance mi = getMethodInstance(obj.getClass(), KeyImpl.init(prop), new Object[] { value }); Method m = mi.getMethod(); if (m.getReturnType() != void.class) @@ -1267,9 +1407,15 @@ public static MethodInstance getSetter(Object obj, String prop, Object value) th */ public static MethodInstance getSetter(Object obj, String prop, Object value, MethodInstance defaultValue) { prop = "set" + StringUtil.ucFirst(prop); - MethodInstance mi = getMethodInstanceEL(obj, obj.getClass(), KeyImpl.init(prop), new Object[] { value }); + MethodInstance mi = getMethodInstanceEL(obj.getClass(), KeyImpl.init(prop), new Object[] { value }); if (mi == null) return defaultValue; - Method m = mi.getMethod(); + Method m; + try { + m = mi.getMethod(); + } + catch (PageException e) { + return defaultValue; + } if (m.getReturnType() != void.class) return defaultValue; return mi; diff --git a/core/src/main/java/lucee/runtime/reflection/pairs/MethodInstance.java b/core/src/main/java/lucee/runtime/reflection/pairs/MethodInstance.java index 531d7331a2..7140ed08ed 100644 --- a/core/src/main/java/lucee/runtime/reflection/pairs/MethodInstance.java +++ b/core/src/main/java/lucee/runtime/reflection/pairs/MethodInstance.java @@ -18,25 +18,51 @@ **/ package lucee.runtime.reflection.pairs; +import java.io.IOException; +import java.lang.instrument.UnmodifiableClassException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import lucee.print; +import lucee.commons.lang.Pair; +import lucee.runtime.exp.ApplicationException; +import lucee.runtime.exp.PageException; +import lucee.runtime.ext.function.BIF; +import lucee.runtime.op.Caster; +import lucee.runtime.type.Collection.Key; +import lucee.runtime.type.util.ListUtil; +import lucee.transformer.direct.DirectCallEngine; +import lucee.transformer.direct.PageContextDummy; /** * class holds a Method and the parameter to call it */ public final class MethodInstance { - private Method method; + private Class clazz; + private Key methodName; private Object[] args; + private Pair result; /** * constructor of the class * * @param method * @param args + * + * public MethodInstance(Method method, Object[] args) { this.method = method; this.args + * = args; } + * @throws UnmodifiableClassException + * @throws IOException + * @throws ClassNotFoundException + * @throws NoSuchMethodException */ - public MethodInstance(Method method, Object[] args) { - this.method = method; + + public MethodInstance(Class clazz, Key methodName, Object[] args) { + this.clazz = clazz; + this.methodName = methodName; this.args = args; } @@ -50,9 +76,71 @@ public MethodInstance(Method method, Object[] args) { * @throws IllegalAccessException * @throws InvocationTargetException * @throws InvocationTargetException + * @throws UnmodifiableClassException + * @throws IOException + * @throws SecurityException + * @throws InstantiationException + * @throws ClassNotFoundException + * @throws NoSuchMethodException */ - public Object invoke(Object o) throws IllegalAccessException, InvocationTargetException { - return method.invoke(o, args); + + public Object invoke(Object o) + throws PageException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + + /* + * if (o == null && "identityHashCode".equals(methodName.getString()) && args.length == 1) { + * print.ds("------ identityHashCode --------"); return System.identityHashCode(args[0]); } + */ + + if (o != null) { + if ("toString".equals(methodName.getString()) && args.length == 0) { + return o.toString(); + } + else if ("equals".equals(methodName.getString()) && args.length == 1) { + return o.equals(args[0]); + } + } + PageContextDummy dummy = null; + BIF instance = (BIF) getResult().getValue().getConstructor().newInstance(); // TODO only load that instance onces + try { + + if (o == null) { + return instance.invoke(null, args); + } + else { + dummy = PageContextDummy.getDummy(o); + return instance.invoke(dummy, args); + } + + } + catch (Exception e) { + print.e("---------"); + if (o != null) print.e(o.getClass().getName()); + print.e(clazz.getName()); + print.e(methodName); + print.e(args); + + List listArgs = new ArrayList<>(); + for (Object arg: args) { + listArgs.add(Caster.toTypeName(arg)); + } + String msg; + if (o == null) msg = "exception while invoking the static method [" + methodName + "] of the class [" + clazz.getName() + "] with the arguments [" + + ListUtil.listToList(listArgs, ", ") + "]"; + else { + msg = "exception while invoking the instance method [" + methodName + "] of the class [" + clazz.getName() + "|" + o.getClass().getName() + "] with the arguments [" + + ListUtil.listToList(listArgs, ", ") + "]"; + } + + ApplicationException ae = new ApplicationException(msg); + ae.initCause(e); + + throw ae; + } + finally { + if (dummy != null) PageContextDummy.returnDummy(dummy); + } + } /** @@ -62,14 +150,27 @@ public Object[] getArgs() { return args; } - /** - * @return Returns the method. - */ - public Method getMethod() { - return method; + public Method getMethod() throws PageException { + return getResult().getName(); } - public void setAccessible(boolean b) { - method.setAccessible(b); + private Pair getResult() throws PageException { + if (result == null) { + try { + result = DirectCallEngine.getInstance(null).createClass(clazz, methodName, args); + } + catch (Exception e) { + throw Caster.toPageException(e); + } + } + return result; } + + /** + * @return Returns the method. + * + * public Method getMethod() { return method; } + * + * public void setAccessible(boolean b) { method.setAccessible(b); } + */ } \ No newline at end of file diff --git a/core/src/main/java/lucee/runtime/type/QueryColumnImpl.java b/core/src/main/java/lucee/runtime/type/QueryColumnImpl.java index 08eb6ab25b..23893e57e9 100755 --- a/core/src/main/java/lucee/runtime/type/QueryColumnImpl.java +++ b/core/src/main/java/lucee/runtime/type/QueryColumnImpl.java @@ -695,7 +695,7 @@ public Object callWithNamedValues(PageContext pc, Key methodName, Struct args) t @Override public Object call(PageContext pc, Key methodName, Object[] arguments) throws PageException { - MethodInstance mi = Reflector.getMethodInstanceEL(this, this.getClass(), methodName, arguments); + MethodInstance mi = Reflector.getMethodInstanceEL(this.getClass(), methodName, arguments); if (mi != null) { try { return mi.invoke(this); diff --git a/core/src/main/java/lucee/runtime/type/util/MemberUtil.java b/core/src/main/java/lucee/runtime/type/util/MemberUtil.java index a59d90f307..f79d1d7623 100644 --- a/core/src/main/java/lucee/runtime/type/util/MemberUtil.java +++ b/core/src/main/java/lucee/runtime/type/util/MemberUtil.java @@ -178,7 +178,7 @@ else if (args.length > ++argIndex) { // careful, argIndex is only incremented wh } private static Object callMethod(Object obj, Collection.Key methodName, Object[] args) throws PageException { - MethodInstance mi = Reflector.getMethodInstanceEL(obj, obj.getClass(), methodName, args); + MethodInstance mi = Reflector.getMethodInstanceEL(obj.getClass(), methodName, args); if (mi == null) return DEFAULT; try { return mi.invoke(obj); diff --git a/loader/build.xml b/loader/build.xml index 43a44e56ee..a0b2270daa 100644 --- a/loader/build.xml +++ b/loader/build.xml @@ -2,7 +2,7 @@ - + diff --git a/loader/pom.xml b/loader/pom.xml index 727c27cae0..262261d52d 100644 --- a/loader/pom.xml +++ b/loader/pom.xml @@ -3,7 +3,7 @@ org.lucee lucee - 6.1.0.83-SNAPSHOT + 6.1.0.84-SNAPSHOT jar Lucee Loader Build diff --git a/loader/src/main/java/lucee/cli/servlet/ServletContextImpl.java b/loader/src/main/java/lucee/cli/servlet/ServletContextImpl.java index a4d5872c3a..b59d34cc2f 100644 --- a/loader/src/main/java/lucee/cli/servlet/ServletContextImpl.java +++ b/loader/src/main/java/lucee/cli/servlet/ServletContextImpl.java @@ -359,37 +359,30 @@ public void setLogger(Logger logger) { } /* noop impl for abstract methods added in Servlet 4.0 */ - @Override public ServletRegistration.Dynamic addJspFile(String s, String s1) { return null; } - @Override public int getSessionTimeout() { return 0; } - @Override public void setSessionTimeout(int i) { } - @Override public String getRequestCharacterEncoding() { return null; } - @Override public void setRequestCharacterEncoding(String s) { } - @Override public String getResponseCharacterEncoding() { return null; } - @Override public void setResponseCharacterEncoding(String s) { } diff --git a/test/tickets/LDEV3556.cfc b/test/tickets/LDEV3556.cfc index 23a97bccc3..610bd33dc4 100644 --- a/test/tickets/LDEV3556.cfc +++ b/test/tickets/LDEV3556.cfc @@ -12,7 +12,7 @@ component extends="org.lucee.cfml.test.LuceeTestCase"{ catch(any e) { var eMessage = e.message; } - expect(eMessage).toInclude("The function [tobase64] does not exist in the Struct"); + expect(eMessage).toInclude("No matching Method for toBase64() found for struct"); }); it(title="Checking error for Using struct with hash() member function", body=function( currentSpec ){ try { @@ -21,7 +21,7 @@ component extends="org.lucee.cfml.test.LuceeTestCase"{ catch(any e) { var eMessage = e.message; } - expect(eMessage).toInclude("The function [hash] does not exist in the Struct"); + expect(eMessage).toInclude("No matching Method for hash() found for struct"); }); it(title="Checking error for Using array with toBase64() member function", body=function( currentSpec ){ try { @@ -30,7 +30,7 @@ component extends="org.lucee.cfml.test.LuceeTestCase"{ catch(any e) { var eMessage = e.message; } - expect(eMessage).toInclude("The function [tobase64] does not exist in the Array"); + expect(eMessage).toInclude("No matching Method for toBase64() found for array"); }); it(title="Checking error for Using array with hash() member function", body=function( currentSpec ){ try { @@ -39,7 +39,7 @@ component extends="org.lucee.cfml.test.LuceeTestCase"{ catch(any e) { var eMessage = e.message; } - expect(eMessage).toInclude("The function [hash] does not exist in the Array"); + expect(eMessage).toInclude("No matching Method for hash() found for array"); }); }); };