This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 8.5.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push: new f52d3a5 Fix edge case in EL method matching f52d3a5 is described below commit f52d3a5a7d0adb0d2d002868bf65bd5ffa7f5f7c Author: Mark Thomas <ma...@apache.org> AuthorDate: Fri Feb 25 10:20:49 2022 +0000 Fix edge case in EL method matching When resolving methods in EL expressions that use beans and/or static fields, ensure that any custom type conversion is considered when identifying the method to call. --- java/javax/el/BeanELResolver.java | 6 +-- java/javax/el/StaticFieldELResolver.java | 9 ++-- java/javax/el/Util.java | 31 ++++++------- test/javax/el/TestBeanELResolver.java | 78 ++++++++++++++++++++++++++++++++ webapps/docs/changelog.xml | 9 ++++ 5 files changed, 106 insertions(+), 27 deletions(-) diff --git a/java/javax/el/BeanELResolver.java b/java/javax/el/BeanELResolver.java index 3e7e1cf..bf77219 100644 --- a/java/javax/el/BeanELResolver.java +++ b/java/javax/el/BeanELResolver.java @@ -143,12 +143,10 @@ public class BeanELResolver extends ELResolver { String methodName = (String) factory.coerceToType(method, String.class); // Find the matching method - Method matchingMethod = - Util.findMethod(base.getClass(), base, methodName, paramTypes, params); + Method matchingMethod = Util.findMethod(context, base.getClass(), base, methodName, paramTypes, params); Object[] parameters = Util.buildParameters( - matchingMethod.getParameterTypes(), matchingMethod.isVarArgs(), - params); + context, matchingMethod.getParameterTypes(), matchingMethod.isVarArgs(), params); Object result = null; try { diff --git a/java/javax/el/StaticFieldELResolver.java b/java/javax/el/StaticFieldELResolver.java index 717cba7..678724a 100644 --- a/java/javax/el/StaticFieldELResolver.java +++ b/java/javax/el/StaticFieldELResolver.java @@ -93,11 +93,10 @@ public class StaticFieldELResolver extends ELResolver { String methodName = (String) method; if ("<init>".equals(methodName)) { - Constructor<?> match = - Util.findConstructor(clazz, paramTypes, params); + Constructor<?> match = Util.findConstructor(context, clazz, paramTypes, params); Object[] parameters = Util.buildParameters( - match.getParameterTypes(), match.isVarArgs(), params); + context, match.getParameterTypes(), match.isVarArgs(), params); Object result = null; @@ -114,7 +113,7 @@ public class StaticFieldELResolver extends ELResolver { } else { // Static method so base should be null - Method match = Util.findMethod(clazz, null, methodName, paramTypes, params); + Method match = Util.findMethod(context, clazz, null, methodName, paramTypes, params); // Note: On Java 9 and above, the isStatic check becomes // unnecessary because the canAccess() call in Util.findMethod() @@ -126,7 +125,7 @@ public class StaticFieldELResolver extends ELResolver { } Object[] parameters = Util.buildParameters( - match.getParameterTypes(), match.isVarArgs(), params); + context, match.getParameterTypes(), match.isVarArgs(), params); Object result = null; try { diff --git a/java/javax/el/Util.java b/java/javax/el/Util.java index 0b41196..d169839 100644 --- a/java/javax/el/Util.java +++ b/java/javax/el/Util.java @@ -199,7 +199,7 @@ class Util { * This method duplicates code in org.apache.el.util.ReflectionUtil. When * making changes keep the code in sync. */ - static Method findMethod(Class<?> clazz, Object base, String methodName, + static Method findMethod(ELContext context, Class<?> clazz, Object base, String methodName, Class<?>[] paramTypes, Object[] paramValues) { if (clazz == null || methodName == null) { @@ -216,7 +216,7 @@ class Util { List<Wrapper<Method>> wrappers = Wrapper.wrap(methods, methodName); - Wrapper<Method> result = findWrapper(clazz, wrappers, methodName, paramTypes, paramValues); + Wrapper<Method> result = findWrapper(context, clazz, wrappers, methodName, paramTypes, paramValues); return getMethod(clazz, base, result.unWrap()); } @@ -226,7 +226,7 @@ class Util { * making changes keep the code in sync. */ @SuppressWarnings("null") - private static <T> Wrapper<T> findWrapper(Class<?> clazz, List<Wrapper<T>> wrappers, + private static <T> Wrapper<T> findWrapper(ELContext context, Class<?> clazz, List<Wrapper<T>> wrappers, String name, Class<?>[] paramTypes, Object[] paramValues) { Map<Wrapper<T>,MatchResult> candidates = new HashMap<>(); @@ -292,7 +292,7 @@ class Util { noMatch = true; break; } else { - if (isCoercibleFrom(paramValues[j], varType)) { + if (isCoercibleFrom(context, paramValues[j], varType)) { coercibleMatch++; varArgsMatch++; } else { @@ -315,7 +315,7 @@ class Util { noMatch = true; break; } else { - if (isCoercibleFrom(paramValues[i], mParamTypes[i])) { + if (isCoercibleFrom(context, paramValues[i], mParamTypes[i])) { coercibleMatch++; } else { noMatch = true; @@ -511,11 +511,11 @@ class Util { * This method duplicates code in org.apache.el.util.ReflectionUtil. When * making changes keep the code in sync. */ - private static boolean isCoercibleFrom(Object src, Class<?> target) { + private static boolean isCoercibleFrom(ELContext context, Object src, Class<?> target) { // TODO: This isn't pretty but it works. Significant refactoring would // be required to avoid the exception. try { - getExpressionFactory().coerceToType(src, target); + context.convertToType(src, target); } catch (ELException e) { return false; } @@ -582,7 +582,7 @@ class Util { } - static Constructor<?> findConstructor(Class<?> clazz, Class<?>[] paramTypes, + static Constructor<?> findConstructor(ELContext context, Class<?> clazz, Class<?>[] paramTypes, Object[] paramValues) { String methodName = "<init>"; @@ -601,7 +601,7 @@ class Util { List<Wrapper<Constructor<?>>> wrappers = Wrapper.wrap(constructors); - Wrapper<Constructor<?>> wrapper = findWrapper(clazz, wrappers, methodName, paramTypes, paramValues); + Wrapper<Constructor<?>> wrapper = findWrapper(context, clazz, wrappers, methodName, paramTypes, paramValues); Constructor<?> constructor = wrapper.unWrap(); @@ -616,9 +616,7 @@ class Util { } - static Object[] buildParameters(Class<?>[] parameterTypes, - boolean isVarArgs,Object[] params) { - ExpressionFactory factory = getExpressionFactory(); + static Object[] buildParameters(ELContext context, Class<?>[] parameterTypes, boolean isVarArgs,Object[] params) { Object[] parameters = null; if (parameterTypes.length > 0) { parameters = new Object[parameterTypes.length]; @@ -631,22 +629,19 @@ class Util { int varArgIndex = parameterTypes.length - 1; // First argCount-1 parameters are standard for (int i = 0; (i < varArgIndex); i++) { - parameters[i] = factory.coerceToType(params[i], - parameterTypes[i]); + parameters[i] = context.convertToType(params[i], parameterTypes[i]); } // Last parameter is the varargs Class<?> varArgClass = parameterTypes[varArgIndex].getComponentType(); final Object varargs = Array.newInstance(varArgClass, (paramCount - varArgIndex)); for (int i = (varArgIndex); i < paramCount; i++) { - Array.set(varargs, i - varArgIndex, - factory.coerceToType(params[i], varArgClass)); + Array.set(varargs, i - varArgIndex, context.convertToType(params[i], varArgClass)); } parameters[varArgIndex] = varargs; } else { parameters = new Object[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { - parameters[i] = factory.coerceToType(params[i], - parameterTypes[i]); + parameters[i] = context.convertToType(params[i], parameterTypes[i]); } } } diff --git a/test/javax/el/TestBeanELResolver.java b/test/javax/el/TestBeanELResolver.java index ef73a9d..aabd545 100644 --- a/test/javax/el/TestBeanELResolver.java +++ b/test/javax/el/TestBeanELResolver.java @@ -699,6 +699,31 @@ public class TestBeanELResolver { } @Test + public void testInvokeVarargsCoerce22() { + BeanELResolver resolver = new BeanELResolver(); + StandardELContext context = new StandardELContext(ELManager.getExpressionFactory()); + context.addELResolver(new StringToLongNeverFailResolver()); + + Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getNameVarargs", + new Class<?>[] { String.class, String.class, String.class }, + new Object[] { "AA", "BB", "CC" }); + + Assert.assertEquals(BEAN_NAME, result); + } + + @Test(expected=MethodNotFoundException.class) + public void testInvokeVarargsCoerce23() { + BeanELResolver resolver = new BeanELResolver(); + StandardELContext context = new StandardELContext(ELManager.getExpressionFactory()); + + Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getNameVarargs", + new Class<?>[] { String.class, String.class, String.class }, + new Object[] { "AA", "BB", "CC" }); + + Assert.assertEquals(BEAN_NAME, result); + } + + @Test public void testInvokeVarargs01() { BeanELResolver resolver = new BeanELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); @@ -984,4 +1009,57 @@ public class TestBeanELResolver { GET_VALUE, SET_VALUE, GET_TYPE, INVOKE } + + /* + * Custom resolver that will always convert a string to an integer. If the + * provided string is not a valid integer, zero will be returned. + */ + private static class StringToLongNeverFailResolver extends ELResolver { + + @Override + public Object getValue(ELContext context, Object base, Object property) { + return null; + } + + @Override + public Class<?> getType(ELContext context, Object base, Object property) { + return null; + } + + @Override + public void setValue(ELContext context, Object base, Object property, Object value) { + throw new PropertyNotWritableException(); + } + + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + return true; + } + + @Override + public Class<?> getCommonPropertyType(ELContext context, Object base) { + return null; + } + + @Override + public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { + return null; + } + + @Override + public Object convertToType(ELContext context, Object obj, Class<?> type) { + if (Integer.class.equals(type) && obj instanceof String) { + context.setPropertyResolved(true); + Integer result; + try { + result = Integer.valueOf((String) obj); + } catch (NumberFormatException e) { + result = Integer.valueOf(0); + } + return result; + } + return super.convertToType(context, obj, type); + } + + } } diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index b8e84e5..65eb436 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -105,6 +105,15 @@ issues do not "pop up" wrt. others). --> <section name="Tomcat 8.5.77 (schultz)" rtext="In development"> + <subsection name="Jasper"> + <changelog> + <fix> + When resolving methods in EL expressions that use beans and/or static + fields, ensure that any custom type conversion is considered when + identifying the method to call. (markt) + </fix> + </changelog> + </subsection> </section> <section name="Tomcat 8.5.76 (schultz)" rtext="release in progress"> <subsection name="Catalina"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org