diff --git a/core/src/main/java/lucee/commons/io/res/util/ResourceUtil.java b/core/src/main/java/lucee/commons/io/res/util/ResourceUtil.java index 1132f0c633e..7b8a1319ee5 100755 --- a/core/src/main/java/lucee/commons/io/res/util/ResourceUtil.java +++ b/core/src/main/java/lucee/commons/io/res/util/ResourceUtil.java @@ -197,10 +197,20 @@ public final class ResourceUtil { public static Resource toResourceExisting(PageContext pc, String path, Resource defaultValue) { try { - return toResourceExisting(pc, path); + if (pc == null) { + pc = ThreadLocalPageContext.get(); + if (pc == null) { + Config c = ThreadLocalPageContext.getConfig(); + if (c != null) return toResourceExisting(c, path, defaultValue); + Resource res = ResourcesImpl.getFileResourceProvider().getResource(path); + return res.exists() ? res : defaultValue; + } + } + Resource res = _findExisting(pc, path, pc.getConfig().allowRealPath()); + return res != null ? res : defaultValue; } - catch (Throwable e) { - ExceptionUtil.rethrowIfNecessary(e); + catch (Throwable t) { + ExceptionUtil.rethrowIfNecessary(t); return defaultValue; } } @@ -209,7 +219,7 @@ public static Resource toResourceExisting(PageContext pc, String path, Resource * cast a String (argument destination) to a File Object, if destination is not an absolute, file * object will be relative to current position (get from PageContext) file must exist otherwise * throw exception - * + * * @param pc Page Context to the current position in filesystem * @param path relative or absolute path for file object * @return file object from destination @@ -229,20 +239,27 @@ public static Resource toResourceExisting(PageContext pc, String path) throws Ex public static Resource toResourceExisting(PageContext pc, String path, boolean allowRealpath, Resource defaultValue) { try { - return toResourceExisting(pc, path, allowRealpath); + Resource res = _findExisting(pc, path, allowRealpath); + return res != null ? res : defaultValue; } - catch (Throwable e) { - ExceptionUtil.rethrowIfNecessary(e); + catch (Throwable t) { + ExceptionUtil.rethrowIfNecessary(t); return defaultValue; } } public static Resource toResourceExisting(PageContext pc, String path, boolean allowRealpath) throws ExpressionException { + Resource res = _findExisting(pc, path, allowRealpath); + if (res != null) return res; + throw new ExpressionException("file or directory [" + StringUtil.max(path.replace('\\', '/'), 255, "...") + "] does not exist"); + } + + private static Resource _findExisting(PageContext pc, String path, boolean allowRealpath) { path = path.replace('\\', '/'); Resource res = pc.getConfig().getResource(path); if (res.exists()) return res; - else if (!allowRealpath) throw new ExpressionException("file or directory [" + StringUtil.max(path, 255, "...") + "] does not exist"); + if (!allowRealpath) return null; if (StringUtil.startsWith(path, '/')) { PageContextImpl pci = (PageContextImpl) pc; @@ -258,8 +275,7 @@ public static Resource toResourceExisting(PageContext pc, String path, boolean a } } res = getRealResource(pc, path, res); - if (res.exists()) return res; - throw new ExpressionException("file or directory [" + StringUtil.max(path, 255, "...") + "] does not exist"); + return res.exists() ? res : null; } public static Resource toResourceExisting(Config config, String path) throws ExpressionException { diff --git a/core/src/main/java/lucee/commons/lang/ClassUtil.java b/core/src/main/java/lucee/commons/lang/ClassUtil.java index 0ca70789127..34afdc83c77 100644 --- a/core/src/main/java/lucee/commons/lang/ClassUtil.java +++ b/core/src/main/java/lucee/commons/lang/ClassUtil.java @@ -61,6 +61,7 @@ import lucee.runtime.type.Array; import lucee.runtime.type.util.ListUtil; import lucee.transformer.dynamic.DynamicInvoker; +import lucee.transformer.dynamic.meta.Constructor; import lucee.transformer.dynamic.meta.Method; public final class ClassUtil { @@ -98,55 +99,41 @@ private static Class checkPrimaryTypes(String className, Class defaultValue) { Class res = checkPrimaryTypesBytecodeDef(className, null); if (res != null) return res; - String lcClassName = className.toLowerCase(); + // length-based fast exits to avoid String allocation on the common path + int len = className.length(); + if (len < 3 || len > 19) return defaultValue; // shortest "int" = 3, longest "java.lang.character" = 19 + + String target; boolean isRef = false; - if (lcClassName.startsWith("java.lang.")) { - lcClassName = lcClassName.substring(10); + if (len > 9) { + // only "java.lang.X" prefixed form possible + if (len < 14) return defaultValue; // "java.lang." + min 4 ("void") = 14 + if (!className.regionMatches(true, 0, "java.lang.", 0, 10)) return defaultValue; + target = className.substring(10); isRef = true; + if (target.length() > 9) return defaultValue; } - - if (lcClassName.length() > 9) return defaultValue; // short circuit longest below match is "character" - - if (lcClassName.equals("void")) { - return void.class; - } - if (lcClassName.equals("boolean")) { - if (isRef) return Boolean.class; - return boolean.class; - } - if (lcClassName.equals("byte")) { - if (isRef) return Byte.class; - return byte.class; - } - if (lcClassName.equals("int")) { - return int.class; - } - if (lcClassName.equals("long")) { - if (isRef) return Long.class; - return long.class; - } - if (lcClassName.equals("float")) { - if (isRef) return Float.class; - return float.class; - } - if (lcClassName.equals("double")) { - if (isRef) return Double.class; - return double.class; - } - if (lcClassName.equals("char")) { - return char.class; - } - if (lcClassName.equals("short")) { - if (isRef) return Short.class; - return short.class; - } - - if (lcClassName.equals("integer")) return Integer.class; - if (lcClassName.equals("character")) return Character.class; - if (lcClassName.equals("object")) return Object.class; - if (lcClassName.equals("string")) return String.class; - if (lcClassName.equals("null")) return Object.class; - if (lcClassName.equals("numeric")) return Double.class; + else { + target = className; + } + + // equalsIgnoreCase doesn't allocate + if (target.equalsIgnoreCase("void")) return void.class; + if (target.equalsIgnoreCase("boolean")) return isRef ? Boolean.class : boolean.class; + if (target.equalsIgnoreCase("byte")) return isRef ? Byte.class : byte.class; + if (target.equalsIgnoreCase("int")) return int.class; + if (target.equalsIgnoreCase("long")) return isRef ? Long.class : long.class; + if (target.equalsIgnoreCase("float")) return isRef ? Float.class : float.class; + if (target.equalsIgnoreCase("double")) return isRef ? Double.class : double.class; + if (target.equalsIgnoreCase("char")) return char.class; + if (target.equalsIgnoreCase("short")) return isRef ? Short.class : short.class; + + if (target.equalsIgnoreCase("integer")) return Integer.class; + if (target.equalsIgnoreCase("character")) return Character.class; + if (target.equalsIgnoreCase("object")) return Object.class; + if (target.equalsIgnoreCase("string")) return String.class; + if (target.equalsIgnoreCase("null")) return Object.class; + if (target.equalsIgnoreCase("numeric")) return Double.class; return defaultValue; } @@ -392,11 +379,14 @@ private static Class loadClass(ClassLoader cl, String className, Class defaultVa */ public static Class loadClass(ClassLoader cl, String className) throws ClassException { - Set exceptions = new HashSet(); - Class clazz = loadClass(cl, className, null, exceptions); - + // fast path: skip the HashSet allocation when the class is found + Class clazz = loadClass(cl, className, null, null); if (clazz != null) return clazz; + // slow path: capture exceptions for diagnostic + Set exceptions = new HashSet(); + loadClass(cl, className, null, exceptions); + String msg = "cannot load class through its string name, because no definition for the class with the specified name [" + className + "] could be found"; // single exception @@ -508,8 +498,30 @@ private static Class __loadClass(ClassLoading cl, String className, Class * @return matching Class * @throws ClassException */ + private static final ClassValue NO_ARG_CONSTRUCTOR = new ClassValue() { + @Override + protected Constructor computeValue(Class clazz) { + try { + DynamicInvoker di = DynamicInvoker.getExistingInstance(); + return di.toClazz(clazz).getConstructor(EMPTY_OBJ, true, false); + } + catch (Throwable t) { + return null; + } + } + }; + + private static final ClassValue> ONE_ARG_CONSTRUCTOR_CACHE = new ClassValue>() { + @Override + protected ConcurrentHashMap computeValue(Class clazz) { + return new ConcurrentHashMap<>(); + } + }; + public static Object loadInstance(Class clazz) throws ClassException { try { + Constructor cached = NO_ARG_CONSTRUCTOR.get(clazz); + if (cached != null) return cached.newInstance(EMPTY_OBJ); return Reflector.getConstructorInstance(clazz, EMPTY_OBJ, false).invoke(); } catch (InstantiationException e) { @@ -587,6 +599,36 @@ public static Object loadInstance(Class clazz, Object[] args) throws ClassExcept if (args == null || args.length == 0) return loadInstance(clazz); try { + // 1-arg fast path: cache resolved Constructor by (clazz, arg0.getClass()), convert args explicitly per call + if (args.length == 1 && args[0] != null) { + Class argClass = args[0].getClass(); + ConcurrentHashMap classCache = ONE_ARG_CONSTRUCTOR_CACHE.get(clazz); + Constructor cached = classCache.get(argClass); + if (cached == null) { + try { + DynamicInvoker di = DynamicInvoker.getExistingInstance(); + // Lookup mutates args[], use a copy so caller's args is preserved for the slow-path fallback + Object[] probe = new Object[] { args[0] }; + cached = di.toClazz(clazz).getConstructor(probe, true, true); + if (cached != null) classCache.put(argClass, cached); + } + catch (Throwable t) { + // fall through to slow path + } + } + if (cached != null) { + try { + Class[] paramTypes = cached.getArgumentClasses(); + Object converted = Reflector.convertSafe(args[0], Reflector.toReferenceClass(paramTypes[0]), null); + if (converted != Reflector.UNCONVERTIBLE) { + return cached.newInstance(new Object[] { converted }); + } + } + catch (lucee.runtime.exp.PageException pe) { + // fall through to slow path + } + } + } return Reflector.getConstructorInstance(clazz, args, false).invoke(); } catch (SecurityException e) { diff --git a/core/src/main/java/lucee/runtime/config/ConfigImpl.java b/core/src/main/java/lucee/runtime/config/ConfigImpl.java index 280f52d0477..b186f975a70 100755 --- a/core/src/main/java/lucee/runtime/config/ConfigImpl.java +++ b/core/src/main/java/lucee/runtime/config/ConfigImpl.java @@ -201,6 +201,7 @@ public abstract class ConfigImpl extends ConfigBase implements ConfigPro { private static final long CACHE_DIR_SIZE_DEFAULT = 1024L * 1024L * 100L; private final Map rpcClassLoaders = new ConcurrentHashMap(); + private volatile ClassLoader defaultRpcClassLoader; private PhysicalClassLoader directClassLoader; private Map datasourcesAll; private Map datasourcesNoQoQ; @@ -3262,12 +3263,23 @@ protected void setSessionScopeDir(Resource sessionScopeDir) { @Override public ClassLoader getRPCClassLoader(boolean reload) throws IOException { - return PhysicalClassLoaderFactory.getRPCClassLoader(this, getJavaSettings(), reload); + ClassLoader cached = defaultRpcClassLoader; + if (!reload && cached != null) return cached; + ClassLoader cl = PhysicalClassLoaderFactory.getRPCClassLoader(this, getJavaSettings(), reload); + if (!reload) defaultRpcClassLoader = cl; + return cl; } @Override public ClassLoader getRPCClassLoader(boolean reload, JavaSettings js) throws IOException { - return PhysicalClassLoaderFactory.getRPCClassLoader(this, js != null ? js : getJavaSettings(), reload); + if (js == null || js == getJavaSettings()) { + ClassLoader cached = defaultRpcClassLoader; + if (!reload && cached != null) return cached; + ClassLoader cl = PhysicalClassLoaderFactory.getRPCClassLoader(this, getJavaSettings(), reload); + if (!reload) defaultRpcClassLoader = cl; + return cl; + } + return PhysicalClassLoaderFactory.getRPCClassLoader(this, js, reload); } private static final Object dclt = new SerializableObject(); @@ -3291,6 +3303,7 @@ public PhysicalClassLoader getDirectClassLoader(boolean reload) throws IOExcepti public void clearRPCClassLoader() { rpcClassLoaders.clear(); + defaultRpcClassLoader = null; } @Override @@ -6316,6 +6329,7 @@ public ConfigImpl resetJavaSettings() { synchronized (javaSettingsInstances) { if (javaSettings != null) { javaSettings = null; + defaultRpcClassLoader = null; } } } diff --git a/core/src/main/java/lucee/runtime/functions/other/JavaProxy.java b/core/src/main/java/lucee/runtime/functions/other/JavaProxy.java index f8dc38aa9e9..58d06e419ba 100644 --- a/core/src/main/java/lucee/runtime/functions/other/JavaProxy.java +++ b/core/src/main/java/lucee/runtime/functions/other/JavaProxy.java @@ -56,6 +56,39 @@ public final class JavaProxy implements Function { private static final long serialVersionUID = 2696152022196556309L; + private static final java.util.concurrent.ConcurrentHashMap>> CLASS_CACHE = new java.util.concurrent.ConcurrentHashMap<>(); + + public static Class tryCachedLoad(PageContext pc, String className) { + try { + ClassLoader cl = ((PageContextImpl) pc).getRPCClassLoader(null); + java.util.concurrent.ConcurrentHashMap> classCache = CLASS_CACHE.get(cl); + if (classCache == null) return null; + return classCache.get(className); + } + catch (Exception e) { + return null; + } + } + + public static void cacheClassLookup(PageContext pc, String className, Class cls) { + if (cls == null) return; + try { + ClassLoader cl = ((PageContextImpl) pc).getRPCClassLoader(null); + getClassCacheFor(cl).put(className, cls); + } + catch (Exception e) { + // ignore — cache miss is recoverable + } + } + + private static java.util.concurrent.ConcurrentHashMap> getClassCacheFor(ClassLoader cl) { + java.util.concurrent.ConcurrentHashMap> classCache = CLASS_CACHE.get(cl); + if (classCache == null) { + classCache = CLASS_CACHE.computeIfAbsent(cl, k -> new java.util.concurrent.ConcurrentHashMap<>()); + } + return classCache; + } + public static Object call(PageContext pc, String className) throws PageException { return call(pc, className, null, null, null); } @@ -141,6 +174,38 @@ else if (Decision.isStruct(pathOrName)) { private static Class loadClassByPath(PageContext pc, String className, String[] paths) throws PageException { PageContextImpl pci = (PageContextImpl) pc; + + // Fast path: no paths means the default RPC classloader resolves this className. Cache the result. + if (paths == null) { + try { + ClassLoader cl = pci.getRPCClassLoader(null); + java.util.concurrent.ConcurrentHashMap> classCache = getClassCacheFor(cl); + Class cached = classCache.get(className); + if (cached != null) return cached; + Class loaded; + try { + loaded = ClassUtil.loadClass(cl, className); + } + catch (ClassException ce) { + if (className.indexOf('.') == -1) { + try { + loaded = ClassUtil.loadClass(cl, "java.lang." + className); + } + catch (ClassException e) { + throw ce; + } + } + else throw ce; + } + classCache.put(className, loaded); + return loaded; + } + catch (Exception e) { + if (e instanceof PageException) throw (PageException) e; + throw Caster.toPageException(e); + } + } + java.util.List resources = new ArrayList(); if (paths != null && paths.length > 0) { diff --git a/core/src/main/java/lucee/runtime/functions/other/_CreateComponent.java b/core/src/main/java/lucee/runtime/functions/other/_CreateComponent.java index 4b11b8de379..9be167b71c4 100755 --- a/core/src/main/java/lucee/runtime/functions/other/_CreateComponent.java +++ b/core/src/main/java/lucee/runtime/functions/other/_CreateComponent.java @@ -64,8 +64,12 @@ public static Object call(PageContext pc, Object[] objArr) throws PageException path = Caster.toString(objArr[objArr.length - 2]); } - // not store the index to make it faster - ComponentImpl cfc = type != TYPE_JAVA ? ComponentLoader.searchComponent(pc, null, path, null, null, false, true, true, type == TYPE_CFML) : null; + // FQN fast-path: when the name is unambiguously a Java fully-qualified name + // (java.*, javax.*, jakarta.*, sun.*, jdk.*), skip the CFC search entirely. + // _search would walk every component mapping looking for e.g. /java/lang/StringBuilder.cfc + // and never find one. Only short-circuits when type is TYPE_BOTH (default for new operator). + boolean skipCfcSearch = type == TYPE_BOTH && isJavaFqn(path); + ComponentImpl cfc = (type != TYPE_JAVA && !skipCfcSearch) ? ComponentLoader.searchComponent(pc, null, path, null, null, false, true, true, type == TYPE_CFML) : null; // if type is TYPE_CFML we do not have to check for it here anymore, because the line above has a // cfc or throws an exception Class cls = cfc == null ? cls = loadClass(pc, path, type) : null; @@ -136,6 +140,18 @@ else if (objArr[0] instanceof FunctionValue) { return rtn; } + private static boolean isJavaFqn(String path) { + if (path == null || path.length() < 5) return false; + return path.startsWith("java.") + || path.startsWith("javax.") + || path.startsWith("jakarta.") + || path.startsWith("sun.") + || path.startsWith("jdk.") + || path.startsWith("com.sun.") + || path.startsWith("org.w3c.") + || path.startsWith("org.xml."); + } + public static Class loadClass(PageContext pc, String path, int type) throws ApplicationException { Class cls = null; // no package @@ -156,8 +172,12 @@ public static Class loadClass(PageContext pc, String path, int type) throws Appl } } if (cls == null) { + // Fast path: cached resolution via JavaProxy's class cache (same default RPC classloader) + cls = JavaProxy.tryCachedLoad(pc, path); + if (cls != null) return cls; try { cls = ClassUtil.loadClass(pc, path); + JavaProxy.cacheClassLookup(pc, path, cls); } catch (Exception e) { ApplicationException ae = new ApplicationException("could not find " + (type == TYPE_BOTH ? "component or class" : "class") + " with name [" + path + "]"); diff --git a/core/src/main/java/lucee/runtime/osgi/OSGiUtil.java b/core/src/main/java/lucee/runtime/osgi/OSGiUtil.java index a235ddea8d3..3db1e53a2e3 100644 --- a/core/src/main/java/lucee/runtime/osgi/OSGiUtil.java +++ b/core/src/main/java/lucee/runtime/osgi/OSGiUtil.java @@ -41,6 +41,7 @@ import java.util.Map.Entry; import java.util.Properties; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.jar.Attributes; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; @@ -2361,12 +2362,23 @@ public static String[] getBootdelegation() { return bootDelegation; } - public static boolean isClassInBootelegation(String className) { - return isInBootelegation(className, false); - } + private static final ConcurrentHashMap CLASS_BOOTELEGATION_CACHE = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap PKG_BOOTELEGATION_CACHE = new ConcurrentHashMap<>(); - public static boolean isPackageInBootelegation(String className) { - return isInBootelegation(className, true); + public static boolean isClassInBootelegation(String className) { + Boolean cached = CLASS_BOOTELEGATION_CACHE.get(className); + if (cached != null) return cached; + boolean result = isInBootelegation(className, false); + CLASS_BOOTELEGATION_CACHE.put(className, result); + return result; + } + + public static boolean isPackageInBootelegation(String packageName) { + Boolean cached = PKG_BOOTELEGATION_CACHE.get(packageName); + if (cached != null) return cached; + boolean result = isInBootelegation(packageName, true); + PKG_BOOTELEGATION_CACHE.put(packageName, result); + return result; } private static boolean isInBootelegation(String name, boolean isPackage) { diff --git a/core/src/main/java/lucee/runtime/reflection/Reflector.java b/core/src/main/java/lucee/runtime/reflection/Reflector.java index 836966be2a0..8c53ae63ee2 100755 --- a/core/src/main/java/lucee/runtime/reflection/Reflector.java +++ b/core/src/main/java/lucee/runtime/reflection/Reflector.java @@ -90,6 +90,15 @@ public final class Reflector { private static WeakFieldStorage fStorage = new WeakFieldStorage(); + public static final Object UNCONVERTIBLE = new Object(); + + private static final ClassValue INTERFACES_CACHE = new ClassValue() { + @Override + protected Class[] computeValue(Class type) { + return type.getInterfaces(); + } + }; + /** * check if Class is instanceof another Class * @@ -202,7 +211,7 @@ public static boolean isInstaneOf(Class src, Class trg, boolean exatctMatch) { } private static boolean _checkInterfaces(Class src, String trg, boolean caseSensitive) { - Class[] interfaces = src.getInterfaces(); + Class[] interfaces = INTERFACES_CACHE.get(src); if (interfaces == null) return false; for (int i = 0; i < interfaces.length; i++) { if (caseSensitive) { @@ -218,7 +227,7 @@ private static boolean _checkInterfaces(Class src, String trg, boolean caseSensi } private static boolean _checkInterfaces(Class src, Class trg, boolean exatctMatch) { - Class[] interfaces = src.getInterfaces(); + Class[] interfaces = INTERFACES_CACHE.get(src); if (interfaces == null) return false; for (int i = 0; i < interfaces.length; i++) { if (interfaces[i] == trg || (!exatctMatch && interfaces[i].getName().equals(trg.getName()))) return true; @@ -294,16 +303,24 @@ public static boolean like(Class src, Class trg) { public static Object convert(Object src, Class trgClass, RefInteger rating, Object defaultValue) { try { - return convert(src, trgClass, rating); + Object trg = _convertOrSentinel(src, trgClass, rating); + if (trg == UNCONVERTIBLE) return defaultValue; + return rating == null ? trg : _applyRating(src, trg, rating); } - catch (PageException e) {// MUST handle this better + catch (PageException e) { return defaultValue; } } + public static Object convertSafe(Object src, Class trgClass, RefInteger rating) throws PageException { + Object trg = _convertOrSentinel(src, trgClass, rating); + if (trg == UNCONVERTIBLE) return UNCONVERTIBLE; + return rating == null ? trg : _applyRating(src, trg, rating); + } + /** * convert Object from src to trg Type, if possible - * + * * @param src Object to convert * @param trgClass Target Class * @param rating @@ -311,72 +328,86 @@ public static Object convert(Object src, Class trgClass, RefInteger rating, Obje * @throws PageException */ public static Object convert(Object src, Class trgClass, RefInteger rating) throws PageException { - if (rating != null) { - Object trg = _convert(src, trgClass, rating); - if (src == trg) { - rating.plus(10); - return trg; - } - if (src == null || trg == null) { - rating.plus(0); + Object trg = _convert(src, trgClass, rating); + return rating == null ? trg : _applyRating(src, trg, rating); + } + + private static Object _applyRating(Object src, Object trg, RefInteger rating) { + if (src == trg) { + rating.plus(10); + return trg; + } + if (src == null || trg == null) { + rating.plus(0); + return trg; + } + if (isInstaneOf(src.getClass(), trg.getClass(), true)) { + rating.plus(9); + return trg; + } + if (src.equals(trg)) { + rating.plus(8); + return trg; + } + + // different number + boolean bothNumbers = src instanceof Number && trg instanceof Number; + if (bothNumbers && ((Number) src).doubleValue() == ((Number) trg).doubleValue()) { + rating.plus(7); + return trg; + } + + String sSrc = Caster.toString(src, null); + String sTrg = Caster.toString(trg, null); + if (sSrc != null && sTrg != null) { + + // different number types + if (src instanceof Number && trg instanceof Number && sSrc.equals(sTrg)) { + rating.plus(6); return trg; } - if (isInstaneOf(src.getClass(), trg.getClass(), true)) { - rating.plus(9); + + // looks the same + if (sSrc.equals(sTrg)) { + rating.plus(5); return trg; } - if (src.equals(trg)) { - rating.plus(8); + if (sSrc.equalsIgnoreCase(sTrg)) { + rating.plus(4); return trg; } + } - // different number - boolean bothNumbers = src instanceof Number && trg instanceof Number; - if (bothNumbers && ((Number) src).doubleValue() == ((Number) trg).doubleValue()) { - rating.plus(7); + // CF Equal + try { + if (OpUtil.equals(ThreadLocalPageContext.get(), src, trg, false, true)) { + rating.plus(3); return trg; } + } + catch (Throwable t) { + ExceptionUtil.rethrowIfNecessary(t); + } - String sSrc = Caster.toString(src, null); - String sTrg = Caster.toString(trg, null); - if (sSrc != null && sTrg != null) { - - // different number types - if (src instanceof Number && trg instanceof Number && sSrc.equals(sTrg)) { - rating.plus(6); - return trg; - } - - // looks the same - if (sSrc.equals(sTrg)) { - rating.plus(5); - return trg; - } - if (sSrc.equalsIgnoreCase(sTrg)) { - rating.plus(4); - return trg; - } - } - - // CF Equal - try { - if (OpUtil.equals(ThreadLocalPageContext.get(), src, trg, false, true)) { - rating.plus(3); - return trg; - } - } - catch (Throwable t) { - ExceptionUtil.rethrowIfNecessary(t); - } + return trg; + } - return trg; + public static Object _convert(Object src, final Class trgClass, RefInteger rating) throws PageException { + Object result = _convertOrSentinel(src, trgClass, rating); + if (result == UNCONVERTIBLE) { + if (src == null) throw new ApplicationException("can't convert [null] to [" + trgClass.getName() + "]"); + throw new ApplicationException("can't convert [" + Caster.toClassName(src) + "] to [" + Caster.toClassName(trgClass) + "]"); } - return _convert(src, trgClass, rating); + return result; } - public static Object _convert(Object src, final Class trgClass, RefInteger rating) throws PageException { + public static Object convertOrSentinel(Object src, final Class trgClass, RefInteger rating) throws PageException { + return _convertOrSentinel(src, trgClass, rating); + } + + private static Object _convertOrSentinel(Object src, final Class trgClass, RefInteger rating) throws PageException { if (src == null) { - if (trgClass.isPrimitive()) throw new ApplicationException("can't convert [null] to [" + trgClass.getName() + "]"); + if (trgClass.isPrimitive()) return UNCONVERTIBLE; return null; } if (like(src.getClass(), trgClass)) return src; @@ -384,7 +415,7 @@ public static Object _convert(Object src, final Class trgClass, RefInteger ratin if (src instanceof ObjectWrap) { src = ((ObjectWrap) src).getEmbededObject(); - return _convert(src, trgClass, rating); + return _convertOrSentinel(src, trgClass, rating); } // component as class @@ -465,10 +496,9 @@ else if (Reflector.isInstaneOf(trgClass, Pojo.class, true) && src instanceof Map } } if (trgClass.isPrimitive()) { - // return convert(src,srcClass,toReferenceClass(trgClass)); - return _convert(src, toReferenceClass(trgClass), rating); + return _convertOrSentinel(src, toReferenceClass(trgClass), rating); } - throw new ApplicationException("can't convert [" + Caster.toClassName(src) + "] to [" + Caster.toClassName(trgClass) + "]"); + return UNCONVERTIBLE; } public static Object componentToClass(PageContext pc, Component src) throws PageException { 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 2aedf8b4c01..7b972ca3548 100644 --- a/core/src/main/java/lucee/transformer/dynamic/meta/Clazz.java +++ b/core/src/main/java/lucee/transformer/dynamic/meta/Clazz.java @@ -287,18 +287,20 @@ public static Constructor getConstructor(Class clazz, Constructor[] constructors parameterTypes = fm.getArgumentClasses(); Object[] newArgs = new Object[args.length]; for (int y = 0; y < parameterTypes.length; y++) { + Object converted; try { - newArgs[y] = Reflector.convert(args[y], Reflector.toReferenceClass(parameterTypes[y]), rating); + converted = Reflector.convertSafe(args[y], Reflector.toReferenceClass(parameterTypes[y]), rating); } catch (PageException e) { continue outer; } + if (converted == Reflector.UNCONVERTIBLE) continue outer; + newArgs[y] = converted; } if (result == null || rating.toInt() > _rating) { if (rating != null) _rating = rating.toInt(); result = new Pair(fm, newArgs); } - // return new ConstructorInstance(constructors[i],newArgs); } } } @@ -389,12 +391,15 @@ else if (arg instanceof JavaObject) { Object[] newArgs = new Object[args.length]; for (int y = 0; y < parameterTypes.length; y++) { + Object converted; try { - newArgs[y] = Reflector.convert(args[y], Reflector.toReferenceClass(parameterTypes[y]), rating); + converted = Reflector.convertSafe(args[y], Reflector.toReferenceClass(parameterTypes[y]), rating); } catch (PageException e) { continue outer; } + if (converted == Reflector.UNCONVERTIBLE) continue outer; + newArgs[y] = converted; } if (result == null || rating.toInt() > _rating) { if (rating != null) _rating = rating.toInt();