This is an automated email from the ASF dual-hosted git repository.
rmaucher pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/10.1.x by this push:
new 59535629df Fix xreflection stack overflow issue found by code review
59535629df is described below
commit 59535629df7d53dde5e2097364bdd1a492bfe3f2
Author: remm <[email protected]>
AuthorDate: Mon Aug 31 10:38:54 2026 +0200
Fix xreflection stack overflow issue found by code review
Stack overflow due to bad fallback to generic getProperty (reproduced).
Missing capitalize for "is" property.
Additional fixes from commit review: generic getProperty could be
invoked first, missing default interface methods, auto boxing.
Tested with a harness and the Tomcat classes, the results now match
IntrospectionUtils, except for exceptions (no generated try/catch).
Additional fixes coauthored with OpenCode.
---
.../ObjectReflectionPropertyInspector.java | 69 +++++----
.../util/xreflection/ReflectionProperty.java | 6 +-
.../tomcat/util/xreflection/SetPropertyClass.java | 159 ++++++++++++++++-----
webapps/docs/changelog.xml | 10 ++
4 files changed, 169 insertions(+), 75 deletions(-)
diff --git
a/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java
b/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java
index 5641ceefe2..2b5d1dcec1 100644
---
a/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java
+++
b/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java
@@ -110,7 +110,8 @@ public final class ObjectReflectionPropertyInspector {
// types of properties that IntrospectionUtils.setProperty supports
private static final Set<Class<?>> ALLOWED_TYPES =
Collections.unmodifiableSet(
- new LinkedHashSet<>(Arrays.asList(Boolean.TYPE, Integer.TYPE,
Long.TYPE, String.class, InetAddress.class)));
+ new LinkedHashSet<>(Arrays.asList(Boolean.TYPE, Boolean.class,
Integer.TYPE, Integer.class,
+ Long.TYPE, Long.class, String.class, InetAddress.class)));
private static final Map<Class<?>,SetPropertyClass> classes = new
LinkedHashMap<>();
/**
@@ -158,7 +159,7 @@ public final class ObjectReflectionPropertyInspector {
static Method findGetter(Class<?> declaringClass, String propertyName) {
for (String getterName : Arrays.asList("get" +
IntrospectionUtils.capitalize(propertyName),
- "is" + propertyName)) {
+ "is" + IntrospectionUtils.capitalize(propertyName))) {
try {
Method method = declaringClass.getMethod(getterName);
if (!Modifier.isPrivate(method.getModifiers())) {
@@ -168,14 +169,6 @@ public final class ObjectReflectionPropertyInspector {
// Ignore
}
}
- try {
- Method method = declaringClass.getMethod("getProperty",
String.class, String.class);
- if (!Modifier.isPrivate(method.getModifiers())) {
- return method;
- }
- } catch (NoSuchMethodException e) {
- // Ignore
- }
return null;
}
@@ -189,14 +182,6 @@ public final class ObjectReflectionPropertyInspector {
} catch (NoSuchMethodException e) {
// Ignore
}
- try {
- Method method = declaringClass.getMethod("setProperty",
String.class, String.class);
- if (!Modifier.isPrivate(method.getModifiers())) {
- return method;
- }
- } catch (NoSuchMethodException e) {
- // Ignore
- }
return null;
}
@@ -217,24 +202,16 @@ public final class ObjectReflectionPropertyInspector {
SetPropertyClass spc = getOrCreateSetPropertyClass(clazz);
final Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
- if (isAllowedSetMethod(method)) {
- String propertyName =
decapitalize(method.getName().substring(3));
- Class<?> propertyType = method.getParameterTypes()[0];
- Method getter = findGetter(clazz, propertyName);
- Method setter = findSetter(clazz, propertyName, propertyType);
- ReflectionProperty property =
- new ReflectionProperty(spc.getClazz().getName(),
propertyName, propertyType, setter, getter);
- spc.addProperty(property);
- } else if (isAllowedGetMethod(method)) {
- boolean startsWithIs = method.getName().startsWith("is");
- String propertyName =
decapitalize(method.getName().substring(startsWithIs ? 2 : 3));
- Class<?> propertyType = method.getReturnType();
- Method getter = findGetter(clazz, propertyName);
- Method setter = findSetter(clazz, propertyName, propertyType);
- ReflectionProperty property =
- new ReflectionProperty(spc.getClazz().getName(),
propertyName, propertyType, setter, getter);
- spc.addProperty(property);
+ addPropertyForMethod(spc, clazz, method);
+ }
+ // Default methods on implemented interfaces are not returned by
+ // getDeclaredMethods() but are visible to IntrospectionUtils, so they
+ // need to be processed as well
+ for (Method method : clazz.getMethods()) {
+ if (!method.getDeclaringClass().isInterface()) {
+ continue;
}
+ addPropertyForMethod(spc, clazz, method);
}
final Field[] fields = clazz.getDeclaredFields();
@@ -257,4 +234,26 @@ public final class ObjectReflectionPropertyInspector {
return spc;
}
}
+
+
+ private static void addPropertyForMethod(SetPropertyClass spc, Class<?>
clazz, Method method) {
+ if (isAllowedSetMethod(method)) {
+ String propertyName = decapitalize(method.getName().substring(3));
+ Class<?> propertyType = method.getParameterTypes()[0];
+ Method getter = findGetter(clazz, propertyName);
+ Method setter = findSetter(clazz, propertyName, propertyType);
+ ReflectionProperty property =
+ new ReflectionProperty(spc.getClazz().getName(),
propertyName, propertyType, setter, getter);
+ spc.addProperty(property);
+ } else if (isAllowedGetMethod(method)) {
+ boolean startsWithIs = method.getName().startsWith("is");
+ String propertyName =
decapitalize(method.getName().substring(startsWithIs ? 2 : 3));
+ Class<?> propertyType = method.getReturnType();
+ Method getter = findGetter(clazz, propertyName);
+ Method setter = findSetter(clazz, propertyName, propertyType);
+ ReflectionProperty property =
+ new ReflectionProperty(spc.getClazz().getName(),
propertyName, propertyType, setter, getter);
+ spc.addProperty(property);
+ }
+ }
}
diff --git a/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java
b/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java
index 852206f3b2..aaf2bb8389 100644
--- a/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java
+++ b/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java
@@ -86,13 +86,13 @@ final class ReflectionProperty implements
Comparable<ReflectionProperty> {
if (getPropertyType() == String.class) {
return valueVarName;
}
- if (getPropertyType() == Boolean.TYPE) {
+ if (getPropertyType() == Boolean.TYPE || getPropertyType() ==
Boolean.class) {
return "Boolean.valueOf(" + valueVarName + ")";
}
- if (getPropertyType() == Long.TYPE) {
+ if (getPropertyType() == Long.TYPE || getPropertyType() == Long.class)
{
return "Long.valueOf(" + valueVarName + ")";
}
- if (getPropertyType() == Integer.TYPE) {
+ if (getPropertyType() == Integer.TYPE || getPropertyType() ==
Integer.class) {
return "Integer.valueOf(" + valueVarName + ")";
}
if (getPropertyType() == InetAddress.class) {
diff --git a/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java
b/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java
index 24de0ad0ee..aeb9df8ce9 100644
--- a/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java
+++ b/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java
@@ -261,7 +261,24 @@ public final class SetPropertyClass implements
Comparable<SetPropertyClass> {
.append('}')
.append(System.lineSeparator());
- // we have a generic setProperty(String, String) method, invoke it
+ // invoke the parent first so that a specific setter on a superclass
+ // is preferred over the generic setProperty method on this class
+ if (getParent() != null) {
+ String parentInvocation =
getParent().generateParentSetPropertyForMethodInvocation();
+ code.append(ReflectionLessCodeGenerator.getIndent(2))
+ .append("if (")
+ .append(parentInvocation, 0, parentInvocation.length() - 1)
+ .append(") {")
+ .append(System.lineSeparator())
+ .append(ReflectionLessCodeGenerator.getIndent(3))
+ .append("return true;")
+ .append(System.lineSeparator())
+ .append(ReflectionLessCodeGenerator.getIndent(2))
+ .append('}')
+ .append(System.lineSeparator());
+ }
+
+ // we have a generic setProperty method, invoke it
if (getGenericSetPropertyMethod() != null) {
ReflectionProperty p = new ReflectionProperty(
clazz.getName(),
@@ -270,26 +287,31 @@ public final class SetPropertyClass implements
Comparable<SetPropertyClass> {
getGenericSetPropertyMethod(),
null
);
- code.append(ReflectionLessCodeGenerator.getIndent(2))
- .append("if (")
- .append(SETP_VAR_NAME)
- .append(") {")
- .append(System.lineSeparator())
- .append(ReflectionLessCodeGenerator.getIndent(3))
- .append(generateSetPropertyMethod(p))
- .append(System.lineSeparator())
- .append(ReflectionLessCodeGenerator.getIndent(3))
- .append("return true;")
- .append(System.lineSeparator())
- .append(ReflectionLessCodeGenerator.getIndent(2))
- .append('}')
- .append(System.lineSeparator());
+ code.append(ReflectionLessCodeGenerator.getIndent(2))
+ .append("if (")
+ .append(SETP_VAR_NAME)
+ .append(") {")
+ .append(System.lineSeparator());
+ if (getGenericSetPropertyMethod().getReturnType() == Boolean.TYPE)
{
+ code.append(ReflectionLessCodeGenerator.getIndent(3))
+ .append("return ")
+ .append(generateSetPropertyMethod(p))
+ .append(System.lineSeparator());
+ } else {
+ code.append(ReflectionLessCodeGenerator.getIndent(3))
+ .append(generateSetPropertyMethod(p))
+ .append(System.lineSeparator())
+ .append(ReflectionLessCodeGenerator.getIndent(3))
+ .append("return true;")
+ .append(System.lineSeparator());
+ }
+ code.append(ReflectionLessCodeGenerator.getIndent(2))
+ .append('}')
+ .append(System.lineSeparator());
}
- // invoke parent or return false
code.append(ReflectionLessCodeGenerator.getIndent(2))
- .append("return ")
- .append(getSetPropertyForExitStatement())
+ .append("return false;")
.append(System.lineSeparator())
.append(ReflectionLessCodeGenerator.getIndent(1))
.append('}');
@@ -298,15 +320,6 @@ public final class SetPropertyClass implements
Comparable<SetPropertyClass> {
//@formatter:on
}
- private String getSetPropertyForExitStatement() {
-
- return (getParent() != null) ?
- // invoke the parent if we have one
- getParent().generateParentSetPropertyForMethodInvocation() :
- // if we invoke setProperty, return true, return false
otherwise
- getGenericSetPropertyMethod() != null ? "true;" : "false;";
- }
-
/**
* Generate a switch case statement that invokes the setProperty method for
* this class.
@@ -455,6 +468,38 @@ public final class SetPropertyClass implements
Comparable<SetPropertyClass> {
return "null;";
}
+ /**
+ * Return the names of the properties for which a specific getter method
+ * (a public zero-argument <code>get*</code> or <code>is*</code> method)
+ * is available on this class or one of its superclasses. Only names that
+ * {@code IntrospectionUtils.getProperty} can reconstruct from the getter
+ * name are included.
+ *
+ * @return the property names with a specific getter
+ */
+ private Set<String> getSpecificGetterPropertyNames() {
+ Set<String> names = new TreeSet<>();
+ for (Method method : clazz.getMethods()) {
+ if (method.getParameterTypes().length != 0) {
+ continue;
+ }
+ String methodName = method.getName();
+ String rest;
+ if (methodName.startsWith("get") && methodName.length() > 3) {
+ rest = methodName.substring(3);
+ } else if (methodName.startsWith("is") && methodName.length() > 2)
{
+ rest = methodName.substring(2);
+ } else {
+ continue;
+ }
+ String propertyName =
ObjectReflectionPropertyInspector.decapitalize(rest);
+ if (IntrospectionUtils.capitalize(propertyName).equals(rest)) {
+ names.add(propertyName);
+ }
+ }
+ return names;
+ }
+
/**
* Generate a complete getProperty method for this class with switch-case
@@ -516,7 +561,10 @@ public final class SetPropertyClass implements
Comparable<SetPropertyClass> {
.append('}')
.append(System.lineSeparator());
- // we have a generic getProperty(String, String) method, invoke it
+ // we have a generic getProperty(String) method, invoke it for property
+ // names that do not have a specific getter on this class or a
+ // superclass (IntrospectionUtils only falls back to the generic
+ // method when no specific getter is found)
if (getGenericGetPropertyMethod() != null) {
ReflectionProperty p = new ReflectionProperty(
clazz.getName(),
@@ -525,15 +573,52 @@ public final class SetPropertyClass implements
Comparable<SetPropertyClass> {
null,
getGenericGetPropertyMethod()
);
- code.append(ReflectionLessCodeGenerator.getIndent(2))
- .append("if (result == null) {")
- .append(System.lineSeparator())
- .append(ReflectionLessCodeGenerator.getIndent(3))
- .append(generateGetPropertyMethod(p))
- .append(System.lineSeparator())
- .append(ReflectionLessCodeGenerator.getIndent(2))
- .append('}')
- .append(System.lineSeparator());
+ Set<String> getterNames = getSpecificGetterPropertyNames();
+ if (getterNames.isEmpty()) {
+ code.append(ReflectionLessCodeGenerator.getIndent(2))
+ .append("if (result == null) {")
+ .append(System.lineSeparator())
+ .append(ReflectionLessCodeGenerator.getIndent(3))
+ .append(generateGetPropertyMethod(p))
+ .append(System.lineSeparator())
+ .append(ReflectionLessCodeGenerator.getIndent(2))
+ .append('}')
+ .append(System.lineSeparator());
+ } else {
+ code.append(ReflectionLessCodeGenerator.getIndent(2))
+ .append("if (result == null) {")
+ .append(System.lineSeparator())
+ .append(ReflectionLessCodeGenerator.getIndent(3))
+ .append("switch (")
+ .append(NAME_VAR_NAME)
+ .append(") {")
+ .append(System.lineSeparator());
+ for (String name : getterNames) {
+ code.append(ReflectionLessCodeGenerator.getIndent(4))
+ .append("case \"")
+ .append(name)
+ .append("\":")
+ .append(System.lineSeparator());
+ }
+ code.append(ReflectionLessCodeGenerator.getIndent(4))
+ .append("break;")
+ .append(System.lineSeparator())
+ .append(ReflectionLessCodeGenerator.getIndent(4))
+ .append("default:")
+ .append(System.lineSeparator())
+ .append(ReflectionLessCodeGenerator.getIndent(5))
+ .append(generateGetPropertyMethod(p))
+ .append(System.lineSeparator())
+ .append(ReflectionLessCodeGenerator.getIndent(4))
+ .append("break;")
+ .append(System.lineSeparator())
+ .append(ReflectionLessCodeGenerator.getIndent(3))
+ .append('}')
+ .append(System.lineSeparator())
+ .append(ReflectionLessCodeGenerator.getIndent(2))
+ .append('}')
+ .append(System.lineSeparator());
+ }
}
code.append(ReflectionLessCodeGenerator.getIndent(2))
.append("return result;")
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 86eaf48f01..87ea221fe7 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -162,6 +162,16 @@
</fix>
</changelog>
</subsection>
+ <subsection name="Coyote">
+ <changelog>
+ <fix>
+ xreflection generated code stack overflow issue. (remm)
+ </fix>
+ <fix>
+ Align xreflection better with IntrospectionUtils. (remm)
+ </fix>
+ </changelog>
+ </subsection>
<subsection name="Cluster">
<changelog>
<fix>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]