diff --git a/spring-core/src/main/java/org/springframework/core/DefaultParameterNameDiscoverer.java b/spring-core/src/main/java/org/springframework/core/DefaultParameterNameDiscoverer.java
index b7bee1aea8..18c39c1ad0 100644
--- a/spring-core/src/main/java/org/springframework/core/DefaultParameterNameDiscoverer.java
+++ b/spring-core/src/main/java/org/springframework/core/DefaultParameterNameDiscoverer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,7 @@ package org.springframework.core;
/**
* Default implementation of the {@link ParameterNameDiscoverer} strategy interface,
- * delegating to the Java 8 standard reflection mechanism, with a deprecated fallback
- * to {@link LocalVariableTableParameterNameDiscoverer}.
+ * delegating to the Java 8 standard reflection mechanism.
*
*
If a Kotlin reflection implementation is present,
* {@link KotlinReflectionParameterNameDiscoverer} is added first in the list and
@@ -36,7 +35,6 @@ package org.springframework.core;
*/
public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {
- @SuppressWarnings("removal")
public DefaultParameterNameDiscoverer() {
if (KotlinDetector.isKotlinReflectPresent()) {
addDiscoverer(new KotlinReflectionParameterNameDiscoverer());
@@ -44,12 +42,6 @@ public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDisc
// Recommended approach on Java 8+: compilation with -parameters.
addDiscoverer(new StandardReflectionParameterNameDiscoverer());
-
- // Deprecated fallback to class file parsing for -debug symbols.
- // Does not work on native images without class file resources.
- if (!NativeDetector.inNativeImage()) {
- addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
- }
}
}
diff --git a/spring-core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java b/spring-core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java
deleted file mode 100644
index 5d666a51f4..0000000000
--- a/spring-core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright 2002-2022 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.core;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Executable;
-import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.springframework.asm.ClassReader;
-import org.springframework.asm.ClassVisitor;
-import org.springframework.asm.Label;
-import org.springframework.asm.MethodVisitor;
-import org.springframework.asm.Opcodes;
-import org.springframework.asm.SpringAsmInfo;
-import org.springframework.asm.Type;
-import org.springframework.lang.Nullable;
-import org.springframework.util.ClassUtils;
-
-/**
- * Implementation of {@link ParameterNameDiscoverer} that uses the LocalVariableTable
- * information in the method attributes to discover parameter names. Returns
- * {@code null} if the class file was compiled without debug information.
- *
- *
Uses ObjectWeb's ASM library for analyzing class files. Each discoverer instance
- * caches the ASM discovered information for each introspected Class, in a thread-safe
- * manner. It is recommended to reuse ParameterNameDiscoverer instances as far as possible.
- *
- *
This class is deprecated in the 6.0 generation and scheduled for removal in 6.1
- * since it is effectively superseded by {@link StandardReflectionParameterNameDiscoverer}.
- * For the time being, this discoverer logs a warning every time it actually inspects a
- * class file which is particularly useful for identifying remaining gaps in usage of
- * the standard "-parameters" compiler flag, and also unintended over-inspection of
- * e.g. JDK core library classes (which are not compiled with the "-parameters" flag).
- *
- * @author Adrian Colyer
- * @author Costin Leau
- * @author Juergen Hoeller
- * @author Chris Beams
- * @author Sam Brannen
- * @since 2.0
- * @see StandardReflectionParameterNameDiscoverer
- * @see DefaultParameterNameDiscoverer
- * @deprecated as of 6.0.1, in favor of {@link StandardReflectionParameterNameDiscoverer}
- * (with the "-parameters" compiler flag)
- */
-@Deprecated(since = "6.0.1", forRemoval = true)
-public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer {
-
- private static final Log logger = LogFactory.getLog(LocalVariableTableParameterNameDiscoverer.class);
-
- // marker object for classes that do not have any debug info
- private static final Map NO_DEBUG_INFO_MAP = Collections.emptyMap();
-
- // the cache uses a nested index (value is a map) to keep the top level cache relatively small in size
- private final Map, Map> parameterNamesCache = new ConcurrentHashMap<>(32);
-
-
- @Override
- @Nullable
- public String[] getParameterNames(Method method) {
- Method originalMethod = BridgeMethodResolver.findBridgedMethod(method);
- return doGetParameterNames(originalMethod);
- }
-
- @Override
- @Nullable
- public String[] getParameterNames(Constructor> ctor) {
- return doGetParameterNames(ctor);
- }
-
- @Nullable
- private String[] doGetParameterNames(Executable executable) {
- Class> declaringClass = executable.getDeclaringClass();
- Map map = this.parameterNamesCache.computeIfAbsent(declaringClass, this::inspectClass);
- return (map != NO_DEBUG_INFO_MAP ? map.get(executable) : null);
- }
-
- /**
- * Inspects the target class.
- * Exceptions will be logged, and a marker map returned to indicate the
- * lack of debug information.
- */
- private Map inspectClass(Class> clazz) {
- InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz));
- if (is == null) {
- // We couldn't load the class file, which is not fatal as it
- // simply means this method of discovering parameter names won't work.
- if (logger.isDebugEnabled()) {
- logger.debug("Cannot find '.class' file for class [" + clazz +
- "] - unable to determine constructor/method parameter names");
- }
- return NO_DEBUG_INFO_MAP;
- }
- // We cannot use try-with-resources here for the InputStream, since we have
- // custom handling of the close() method in a finally-block.
- try {
- ClassReader classReader = new ClassReader(is);
- Map map = new ConcurrentHashMap<>(32);
- classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), 0);
- if (logger.isWarnEnabled()) {
- logger.warn("Using deprecated '-debug' fallback for parameter name resolution. Compile the " +
- "affected code with '-parameters' instead or avoid its introspection: " + clazz.getName());
- }
- return map;
- }
- catch (IOException ex) {
- if (logger.isDebugEnabled()) {
- logger.debug("Exception thrown while reading '.class' file for class [" + clazz +
- "] - unable to determine constructor/method parameter names", ex);
- }
- }
- catch (IllegalArgumentException ex) {
- if (logger.isDebugEnabled()) {
- logger.debug("ASM ClassReader failed to parse class file [" + clazz +
- "], probably due to a new Java class file version that isn't supported yet " +
- "- unable to determine constructor/method parameter names", ex);
- }
- }
- finally {
- try {
- is.close();
- }
- catch (IOException ex) {
- // ignore
- }
- }
- return NO_DEBUG_INFO_MAP;
- }
-
-
- /**
- * Helper class that inspects all methods and constructors and then
- * attempts to find the parameter names for the given {@link Executable}.
- */
- private static class ParameterNameDiscoveringVisitor extends ClassVisitor {
-
- private static final String STATIC_CLASS_INIT = "";
-
- private final Class> clazz;
-
- private final Map executableMap;
-
- public ParameterNameDiscoveringVisitor(Class> clazz, Map executableMap) {
- super(SpringAsmInfo.ASM_VERSION);
- this.clazz = clazz;
- this.executableMap = executableMap;
- }
-
- @Override
- @Nullable
- public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
- // exclude synthetic + bridged && static class initialization
- if (!isSyntheticOrBridged(access) && !STATIC_CLASS_INIT.equals(name)) {
- return new LocalVariableTableVisitor(this.clazz, this.executableMap, name, desc, isStatic(access));
- }
- return null;
- }
-
- private static boolean isSyntheticOrBridged(int access) {
- return (((access & Opcodes.ACC_SYNTHETIC) | (access & Opcodes.ACC_BRIDGE)) > 0);
- }
-
- private static boolean isStatic(int access) {
- return ((access & Opcodes.ACC_STATIC) > 0);
- }
- }
-
-
- private static class LocalVariableTableVisitor extends MethodVisitor {
-
- private static final String CONSTRUCTOR = "";
-
- private final Class> clazz;
-
- private final Map executableMap;
-
- private final String name;
-
- private final Type[] args;
-
- private final String[] parameterNames;
-
- private final boolean isStatic;
-
- private boolean hasLvtInfo = false;
-
- /*
- * The nth entry contains the slot index of the LVT table entry holding the
- * argument name for the nth parameter.
- */
- private final int[] lvtSlotIndex;
-
- public LocalVariableTableVisitor(Class> clazz, Map map, String name, String desc, boolean isStatic) {
- super(SpringAsmInfo.ASM_VERSION);
- this.clazz = clazz;
- this.executableMap = map;
- this.name = name;
- this.args = Type.getArgumentTypes(desc);
- this.parameterNames = new String[this.args.length];
- this.isStatic = isStatic;
- this.lvtSlotIndex = computeLvtSlotIndices(isStatic, this.args);
- }
-
- @Override
- public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
- this.hasLvtInfo = true;
- for (int i = 0; i < this.lvtSlotIndex.length; i++) {
- if (this.lvtSlotIndex[i] == index) {
- this.parameterNames[i] = name;
- }
- }
- }
-
- @Override
- public void visitEnd() {
- if (this.hasLvtInfo || (this.isStatic && this.parameterNames.length == 0)) {
- // visitLocalVariable will never be called for static no args methods
- // which doesn't use any local variables.
- // This means that hasLvtInfo could be false for that kind of methods
- // even if the class has local variable info.
- this.executableMap.put(resolveExecutable(), this.parameterNames);
- }
- }
-
- private Executable resolveExecutable() {
- ClassLoader loader = this.clazz.getClassLoader();
- Class>[] argTypes = new Class>[this.args.length];
- for (int i = 0; i < this.args.length; i++) {
- argTypes[i] = ClassUtils.resolveClassName(this.args[i].getClassName(), loader);
- }
- try {
- if (CONSTRUCTOR.equals(this.name)) {
- return this.clazz.getDeclaredConstructor(argTypes);
- }
- return this.clazz.getDeclaredMethod(this.name, argTypes);
- }
- catch (NoSuchMethodException ex) {
- throw new IllegalStateException("Method [" + this.name +
- "] was discovered in the .class file but cannot be resolved in the class object", ex);
- }
- }
-
- private static int[] computeLvtSlotIndices(boolean isStatic, Type[] paramTypes) {
- int[] lvtIndex = new int[paramTypes.length];
- int nextIndex = (isStatic ? 0 : 1);
- for (int i = 0; i < paramTypes.length; i++) {
- lvtIndex[i] = nextIndex;
- if (isWideType(paramTypes[i])) {
- nextIndex += 2;
- }
- else {
- nextIndex++;
- }
- }
- return lvtIndex;
- }
-
- private static boolean isWideType(Type aType) {
- // float is not a wide type
- return (aType == Type.LONG_TYPE || aType == Type.DOUBLE_TYPE);
- }
- }
-
-}
diff --git a/spring-core/src/test/java/org/springframework/core/LocalVariableTableParameterNameDiscovererTests.java b/spring-core/src/test/java/org/springframework/core/LocalVariableTableParameterNameDiscovererTests.java
deleted file mode 100644
index 5a00a7aa9d..0000000000
--- a/spring-core/src/test/java/org/springframework/core/LocalVariableTableParameterNameDiscovererTests.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright 2002-2023 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.core;
-
-import java.awt.Component;
-import java.io.PrintStream;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.Date;
-
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.tests.sample.objects.TestObject;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Adrian Colyer
- */
-class LocalVariableTableParameterNameDiscovererTests {
-
- @SuppressWarnings("removal")
- private final ParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
-
-
- @Test
- void methodParameterNameDiscoveryNoArgs() throws NoSuchMethodException {
- Method getName = TestObject.class.getMethod("getName");
- String[] names = discoverer.getParameterNames(getName);
- assertThat(names).as("should find method info").isNotNull();
- assertThat(names).as("no argument names").isEmpty();
- }
-
- @Test
- void methodParameterNameDiscoveryWithArgs() throws NoSuchMethodException {
- Method setName = TestObject.class.getMethod("setName", String.class);
- String[] names = discoverer.getParameterNames(setName);
- assertThat(names).as("should find method info").isNotNull();
- assertThat(names).as("one argument").hasSize(1);
- assertThat(names[0]).isEqualTo("name");
- }
-
- @Test
- void consParameterNameDiscoveryNoArgs() throws NoSuchMethodException {
- Constructor noArgsCons = TestObject.class.getConstructor();
- String[] names = discoverer.getParameterNames(noArgsCons);
- assertThat(names).as("should find cons info").isNotNull();
- assertThat(names).as("no argument names").isEmpty();
- }
-
- @Test
- void consParameterNameDiscoveryArgs() throws NoSuchMethodException {
- Constructor twoArgCons = TestObject.class.getConstructor(String.class, int.class);
- String[] names = discoverer.getParameterNames(twoArgCons);
- assertThat(names).as("should find cons info").isNotNull();
- assertThat(names).as("one argument").hasSize(2);
- assertThat(names[0]).isEqualTo("name");
- assertThat(names[1]).isEqualTo("age");
- }
-
- @Test
- void staticMethodParameterNameDiscoveryNoArgs() throws NoSuchMethodException {
- Method m = getClass().getMethod("staticMethodNoLocalVars");
- String[] names = discoverer.getParameterNames(m);
- assertThat(names).as("should find method info").isNotNull();
- assertThat(names).as("no argument names").isEmpty();
- }
-
- @Test
- void overloadedStaticMethod() throws Exception {
- Class extends LocalVariableTableParameterNameDiscovererTests> clazz = this.getClass();
-
- Method m1 = clazz.getMethod("staticMethod", Long.TYPE, Long.TYPE);
- String[] names = discoverer.getParameterNames(m1);
- assertThat(names).as("should find method info").isNotNull();
- assertThat(names).as("two arguments").hasSize(2);
- assertThat(names[0]).isEqualTo("x");
- assertThat(names[1]).isEqualTo("y");
-
- Method m2 = clazz.getMethod("staticMethod", Long.TYPE, Long.TYPE, Long.TYPE);
- names = discoverer.getParameterNames(m2);
- assertThat(names).as("should find method info").isNotNull();
- assertThat(names).as("three arguments").hasSize(3);
- assertThat(names[0]).isEqualTo("x");
- assertThat(names[1]).isEqualTo("y");
- assertThat(names[2]).isEqualTo("z");
- }
-
- @Test
- void overloadedStaticMethodInInnerClass() throws Exception {
- Class clazz = InnerClass.class;
-
- Method m1 = clazz.getMethod("staticMethod", Long.TYPE);
- String[] names = discoverer.getParameterNames(m1);
- assertThat(names).as("should find method info").isNotNull();
- assertThat(names).as("one argument").hasSize(1);
- assertThat(names[0]).isEqualTo("x");
-
- Method m2 = clazz.getMethod("staticMethod", Long.TYPE, Long.TYPE);
- names = discoverer.getParameterNames(m2);
- assertThat(names).as("should find method info").isNotNull();
- assertThat(names).as("two arguments").hasSize(2);
- assertThat(names[0]).isEqualTo("x");
- assertThat(names[1]).isEqualTo("y");
- }
-
- @Test
- void overloadedMethod() throws Exception {
- Class extends LocalVariableTableParameterNameDiscovererTests> clazz = this.getClass();
-
- Method m1 = clazz.getMethod("instanceMethod", Double.TYPE, Double.TYPE);
- String[] names = discoverer.getParameterNames(m1);
- assertThat(names).as("should find method info").isNotNull();
- assertThat(names).as("two arguments").hasSize(2);
- assertThat(names[0]).isEqualTo("x");
- assertThat(names[1]).isEqualTo("y");
-
- Method m2 = clazz.getMethod("instanceMethod", Double.TYPE, Double.TYPE, Double.TYPE);
- names = discoverer.getParameterNames(m2);
- assertThat(names).as("should find method info").isNotNull();
- assertThat(names).as("three arguments").hasSize(3);
- assertThat(names[0]).isEqualTo("x");
- assertThat(names[1]).isEqualTo("y");
- assertThat(names[2]).isEqualTo("z");
- }
-
- @Test
- void overloadedMethodInInnerClass() throws Exception {
- Class clazz = InnerClass.class;
-
- Method m1 = clazz.getMethod("instanceMethod", String.class);
- String[] names = discoverer.getParameterNames(m1);
- assertThat(names).as("should find method info").isNotNull();
- assertThat(names).as("one argument").hasSize(1);
- assertThat(names[0]).isEqualTo("aa");
-
- Method m2 = clazz.getMethod("instanceMethod", String.class, String.class);
- names = discoverer.getParameterNames(m2);
- assertThat(names).as("should find method info").isNotNull();
- assertThat(names).as("two arguments").hasSize(2);
- assertThat(names[0]).isEqualTo("aa");
- assertThat(names[1]).isEqualTo("bb");
- }
-
- @Test
- void generifiedClass() throws Exception {
- Class> clazz = GenerifiedClass.class;
-
- Constructor> ctor = clazz.getDeclaredConstructor(Object.class);
- String[] names = discoverer.getParameterNames(ctor);
- assertThat(names).hasSize(1);
- assertThat(names[0]).isEqualTo("key");
-
- ctor = clazz.getDeclaredConstructor(Object.class, Object.class);
- names = discoverer.getParameterNames(ctor);
- assertThat(names).hasSize(2);
- assertThat(names[0]).isEqualTo("key");
- assertThat(names[1]).isEqualTo("value");
-
- Method m = clazz.getMethod("generifiedStaticMethod", Object.class);
- names = discoverer.getParameterNames(m);
- assertThat(names).hasSize(1);
- assertThat(names[0]).isEqualTo("param");
-
- m = clazz.getMethod("generifiedMethod", Object.class, long.class, Object.class, Object.class);
- names = discoverer.getParameterNames(m);
- assertThat(names).hasSize(4);
- assertThat(names[0]).isEqualTo("param");
- assertThat(names[1]).isEqualTo("x");
- assertThat(names[2]).isEqualTo("key");
- assertThat(names[3]).isEqualTo("value");
-
- m = clazz.getMethod("voidStaticMethod", Object.class, long.class, int.class);
- names = discoverer.getParameterNames(m);
- assertThat(names).hasSize(3);
- assertThat(names[0]).isEqualTo("obj");
- assertThat(names[1]).isEqualTo("x");
- assertThat(names[2]).isEqualTo("i");
-
- m = clazz.getMethod("nonVoidStaticMethod", Object.class, long.class, int.class);
- names = discoverer.getParameterNames(m);
- assertThat(names).hasSize(3);
- assertThat(names[0]).isEqualTo("obj");
- assertThat(names[1]).isEqualTo("x");
- assertThat(names[2]).isEqualTo("i");
-
- m = clazz.getMethod("getDate");
- names = discoverer.getParameterNames(m);
- assertThat(names).isEmpty();
- }
-
- @Disabled("Ignored because Ubuntu packages OpenJDK with debug symbols enabled. See SPR-8078.")
- @Test
- void classesWithoutDebugSymbols() throws Exception {
- // JDK classes don't have debug information (usually)
- Class clazz = Component.class;
- String methodName = "list";
-
- Method m = clazz.getMethod(methodName);
- String[] names = discoverer.getParameterNames(m);
- assertThat(names).isNull();
-
- m = clazz.getMethod(methodName, PrintStream.class);
- names = discoverer.getParameterNames(m);
- assertThat(names).isNull();
-
- m = clazz.getMethod(methodName, PrintStream.class, int.class);
- names = discoverer.getParameterNames(m);
- assertThat(names).isNull();
- }
-
-
- public static void staticMethodNoLocalVars() {
- }
-
- public static long staticMethod(long x, long y) {
- long u = x * y;
- return u;
- }
-
- public static long staticMethod(long x, long y, long z) {
- long u = x * y * z;
- return u;
- }
-
- public double instanceMethod(double x, double y) {
- double u = x * y;
- return u;
- }
-
- public double instanceMethod(double x, double y, double z) {
- double u = x * y * z;
- return u;
- }
-
-
- public static class InnerClass {
-
- public int waz = 0;
-
- public InnerClass() {
- }
-
- public InnerClass(String firstArg, long secondArg, Object thirdArg) {
- long foo = 0;
- short bar = 10;
- this.waz = (int) (foo + bar);
- }
-
- public String instanceMethod(String aa) {
- return aa;
- }
-
- public String instanceMethod(String aa, String bb) {
- return aa + bb;
- }
-
- public static long staticMethod(long x) {
- long u = x;
- return u;
- }
-
- public static long staticMethod(long x, long y) {
- long u = x * y;
- return u;
- }
- }
-
-
- public static class GenerifiedClass {
-
- private static long date;
-
- static {
- // some custom static bloc or
- date = new Date().getTime();
- }
-
- public GenerifiedClass() {
- this(null, null);
- }
-
- public GenerifiedClass(K key) {
- this(key, null);
- }
-
- public GenerifiedClass(K key, V value) {
- }
-
- public static long generifiedStaticMethod(P param) {
- return date;
- }
-
- public
void generifiedMethod(P param, long x, K key, V value) {
- // nothing
- }
-
- public static void voidStaticMethod(Object obj, long x, int i) {
- // nothing
- }
-
- public static long nonVoidStaticMethod(Object obj, long x, int i) {
- return date;
- }
-
- public static long getDate() {
- return date;
- }
- }
-
-}