As the attached example program demonstrates, `StackWalker.getCallerClass()` can throw an `UnsupportedOperationException` if called reflectively. Currently this only happens if we invoke `StackWalker.getCallerClass()` recursively reflectively, but this issue will become more prominent once we fix JDK-8285447.
```
import java.lang.reflect.Method;
import java.util.List;
import java.util.ArrayList;
public class ReflectiveGetCallerClassTest {
private static StackWalker WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
private static Method gcc, inv;
static {
try {
inv = Method.class.getDeclaredMethod("invoke", Object.class, Object[].class);
gcc = ReflectiveGetCallerClassTest.class.getDeclaredMethod("getCallerClass");
} catch (SecurityException se) {
// This test can't run if a security manager prohibits "getStackWalkerWithClassReference"
System.err.println(se);
System.exit(0);
} catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
public static void getCallerClass() {
System.out.println(WALKER.getCallerClass());
}
// Create a list of Object[] of the form:
// { m, first }
// { m, { m, first } }
// { m, { m, { m, first } } }
static List<Object[]> prepareArgs(Object[] first, Method m, int depth) {
List<Object[]> l = new ArrayList<Object[]>(depth + 1);
l.add(first);
while (depth-- > 0) {
l.add(new Object[] { m, l.get(l.size() - 1) });
}
return l;
}
public static void main(String[] args) throws Throwable {
// gcc is ReflectiveGetCallerClassTest::getCallerClass()
// inv is Method::invoke()
for (Object[] params : prepareArgs(new Object[] { gcc, new Object[] { null, null } }, inv, 10)) {
inv.invoke(inv, inv, params);
}
}
}
```
```
import java.lang.reflect.Method;
import java.util.List;
import java.util.ArrayList;
public class ReflectiveGetCallerClassTest {
private static StackWalker WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
private static Method gcc, inv;
static {
try {
inv = Method.class.getDeclaredMethod("invoke", Object.class, Object[].class);
gcc = ReflectiveGetCallerClassTest.class.getDeclaredMethod("getCallerClass");
} catch (SecurityException se) {
// This test can't run if a security manager prohibits "getStackWalkerWithClassReference"
System.err.println(se);
System.exit(0);
} catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
public static void getCallerClass() {
System.out.println(WALKER.getCallerClass());
}
// Create a list of Object[] of the form:
// { m, first }
// { m, { m, first } }
// { m, { m, { m, first } } }
static List<Object[]> prepareArgs(Object[] first, Method m, int depth) {
List<Object[]> l = new ArrayList<Object[]>(depth + 1);
l.add(first);
while (depth-- > 0) {
l.add(new Object[] { m, l.get(l.size() - 1) });
}
return l;
}
public static void main(String[] args) throws Throwable {
// gcc is ReflectiveGetCallerClassTest::getCallerClass()
// inv is Method::invoke()
for (Object[] params : prepareArgs(new Object[] { gcc, new Object[] { null, null } }, inv, 10)) {
inv.invoke(inv, inv, params);
}
}
}
```
- duplicates
-
JDK-8210375 StackWalker::getCallerClass throws UnsupportedOperationException
- Closed
- relates to
-
JDK-8157464 Disallow StackWalker.getCallerClass() be called by caller-sensitive method
- Closed
-
JDK-8285447 StackWalker minimal batch size should be optimized for getCallerClass
- Resolved
- links to
-
Review openjdk/jdk/14773