diff --git a/src/java.base/share/classes/java/lang/StackFrameInfo.java b/src/java.base/share/classes/java/lang/StackFrameInfo.java
index 4f3cc1e980c..3666a834218 100644
--- a/src/java.base/share/classes/java/lang/StackFrameInfo.java
+++ b/src/java.base/share/classes/java/lang/StackFrameInfo.java
@@ -29,6 +29,7 @@ import jdk.internal.misc.SharedSecrets;
import static java.lang.StackWalker.Option.*;
import java.lang.StackWalker.StackFrame;
+import java.lang.invoke.MethodType;
class StackFrameInfo implements StackFrame {
private final static JavaLangInvokeAccess JLIA =
@@ -78,6 +79,17 @@ class StackFrameInfo implements StackFrame {
return JLIA.getName(memberName);
}
+ @Override
+ public MethodType getMethodType() {
+ walker.ensureAccessEnabled(RETAIN_CLASS_REFERENCE);
+ return JLIA.getMethodType(memberName);
+ }
+
+ @Override
+ public String getDescriptor() {
+ return JLIA.getMethodDescriptor(memberName);
+ }
+
@Override
public int getByteCodeIndex() {
// bci not available for native methods
diff --git a/src/java.base/share/classes/java/lang/StackWalker.java b/src/java.base/share/classes/java/lang/StackWalker.java
index 7e4bc1ebf88..a079ac53384 100644
--- a/src/java.base/share/classes/java/lang/StackWalker.java
+++ b/src/java.base/share/classes/java/lang/StackWalker.java
@@ -26,10 +26,12 @@ package java.lang;
import jdk.internal.reflect.CallerSensitive;
-import java.util.*;
+import java.lang.invoke.MethodType;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
-import java.util.function.Predicate;
import java.util.stream.Stream;
/**
@@ -96,7 +98,7 @@ public final class StackWalker {
* @since 9
* @jvms 2.6
*/
- public static interface StackFrame {
+ public interface StackFrame {
/**
* Gets the binary name
* of the declaring class of the method represented by this stack frame.
@@ -127,6 +129,47 @@ public final class StackWalker {
*/
public Class> getDeclaringClass();
+ /**
+ * Returns the {@link MethodType} representing the parameter types and
+ * the return type for the method represented by this stack frame.
+ *
+ * @implSpec
+ * The default implementation throws {@code UnsupportedOperationException}.
+ *
+ * @return the {@code MethodType} for this stack frame
+ *
+ * @throws UnsupportedOperationException if this {@code StackWalker}
+ * is not configured with {@link Option#RETAIN_CLASS_REFERENCE
+ * Option.RETAIN_CLASS_REFERENCE}.
+ *
+ * @since 10
+ */
+ public default MethodType getMethodType() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns the descriptor of the method represented by
+ * this stack frame as defined by
+ * The Java Virtual Machine Specification.
+ *
+ * @implSpec
+ * The default implementation throws {@code UnsupportedOperationException}.
+ *
+ * @return the descriptor of the method represented by
+ * this stack frame
+ *
+ * @see MethodType#fromMethodDescriptorString(String, ClassLoader)
+ * @see MethodType#toMethodDescriptorString()
+ * @jvms 4.3.3 Method Descriptor
+ *
+ * @since 10
+ */
+ public default String getDescriptor() {
+ throw new UnsupportedOperationException();
+ }
+
+
/**
* Returns the index to the code array of the {@code Code} attribute
* containing the execution point represented by this stack frame.
diff --git a/src/java.base/share/classes/java/lang/invoke/MemberName.java b/src/java.base/share/classes/java/lang/invoke/MemberName.java
index 86a625b2eed..c67f5fb3d89 100644
--- a/src/java.base/share/classes/java/lang/invoke/MemberName.java
+++ b/src/java.base/share/classes/java/lang/invoke/MemberName.java
@@ -162,6 +162,29 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
return (MethodType) type;
}
+ /** Return the descriptor of this member, which
+ * must be a method or constructor.
+ */
+ String getMethodDescriptor() {
+ if (type == null) {
+ expandFromVM();
+ if (type == null) {
+ return null;
+ }
+ }
+ if (!isInvocable()) {
+ throw newIllegalArgumentException("not invocable, no method type");
+ }
+
+ // Get a snapshot of type which doesn't get changed by racing threads.
+ final Object type = this.type;
+ if (type instanceof String) {
+ return (String) type;
+ } else {
+ return getMethodType().toMethodDescriptorString();
+ }
+ }
+
/** Return the actual type under which this method or constructor must be invoked.
* For non-static methods or constructors, this is the type with a leading parameter,
* a reference to declaring class. For static methods, it is the same as the declared type.
diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
index 963e0cd24a8..348a15921f0 100644
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
@@ -1785,6 +1785,18 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
return memberName.getName();
}
+ @Override
+ public MethodType getMethodType(Object mname) {
+ MemberName memberName = (MemberName)mname;
+ return memberName.getMethodType();
+ }
+
+ @Override
+ public String getMethodDescriptor(Object mname) {
+ MemberName memberName = (MemberName)mname;
+ return memberName.getMethodDescriptor();
+ }
+
@Override
public boolean isNative(Object mname) {
MemberName memberName = (MemberName)mname;
diff --git a/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java b/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java
index c710c6543ef..0d283644868 100644
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java
@@ -39,6 +39,18 @@ public interface JavaLangInvokeAccess {
*/
String getName(Object mname);
+ /**
+ * Returns the {@code MethodType} for the given MemberName.
+ * Used by {@see StackFrameInfo}.
+ */
+ MethodType getMethodType(Object mname);
+
+ /**
+ * Returns the descriptor for the given MemberName.
+ * Used by {@see StackFrameInfo}.
+ */
+ String getMethodDescriptor(Object mname);
+
/**
* Returns {@code true} if the given MemberName is a native method. Used by
* {@see StackFrameInfo}.
diff --git a/test/jdk/java/lang/StackWalker/Basic.java b/test/jdk/java/lang/StackWalker/Basic.java
index 8dc7627a90d..e7b8070cf46 100644
--- a/test/jdk/java/lang/StackWalker/Basic.java
+++ b/test/jdk/java/lang/StackWalker/Basic.java
@@ -29,8 +29,9 @@
*/
import java.lang.StackWalker.StackFrame;
+import java.lang.invoke.MethodType;
import java.util.List;
-import java.util.Objects;
+import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.StackWalker.Option.*;
@@ -74,6 +75,37 @@ public class Basic {
found);
}
+ @Test
+ public static void testMethodSignature() throws Exception {
+ List frames = new StackBuilder(16, 16).build();
+ Map methodTypes = StackBuilder.methodTypes();
+ for (StackFrame f : frames) {
+ MethodType type = methodTypes.get(f.getMethodName());
+ if (type != null) {
+ System.out.format("%s.%s %s%n", f.getClassName(), f.getMethodName(),
+ f.getDescriptor());
+
+ String descriptor = f.getDescriptor();
+ if (!descriptor.equals(type.toMethodDescriptorString())) {
+ throw new RuntimeException("Expected: " + type.toMethodDescriptorString()
+ + " got: " + f.getDescriptor());
+ }
+
+ if (!f.getMethodType().equals(type)) {
+ throw new RuntimeException("Expected: " + type
+ + " got: " + f.getMethodType());
+ }
+
+ // verify descriptor returned by getDescriptor() before and after
+ // getMethodType() is called
+ if (!descriptor.equals(f.getDescriptor())) {
+ throw new RuntimeException("Mismatched: " + descriptor
+ + " got: " + f.getDescriptor());
+ }
+ }
+ }
+ }
+
private final int depth;
Basic(int depth) {
this.depth = depth;
@@ -132,7 +164,7 @@ public class Basic {
}
}
- class StackBuilder {
+ static class StackBuilder {
private final int stackDepth;
private final int limit;
private int depth = 0;
@@ -150,15 +182,17 @@ public class Basic {
trace("m1");
m2();
}
- void m2() {
+ List m2() {
trace("m2");
m3();
+ return null;
}
- void m3() {
+ int m3() {
trace("m3");
- m4();
+ m4(null);
+ return 0;
}
- void m4() {
+ void m4(Object o) {
trace("m4");
int remaining = stackDepth-depth-1;
if (remaining >= 4) {
@@ -184,6 +218,13 @@ public class Basic {
if (verbose)
System.out.format("%2d: %s%n", depth, methodname);
}
+
+ static Map methodTypes() throws Exception {
+ return Map.of("m1", MethodType.methodType(void.class),
+ "m2", MethodType.methodType(List.class),
+ "m3", MethodType.methodType(int.class),
+ "m4", MethodType.methodType(void.class, Object.class));
+ }
}
}
diff --git a/test/jdk/java/lang/StackWalker/SanityTest.java b/test/jdk/java/lang/StackWalker/SanityTest.java
index 538c6616114..25deb0cf87d 100644
--- a/test/jdk/java/lang/StackWalker/SanityTest.java
+++ b/test/jdk/java/lang/StackWalker/SanityTest.java
@@ -79,4 +79,24 @@ public class SanityTest {
throw new RuntimeException("NPE expected");
} catch (NullPointerException e) {}
}
+
+
+ @Test
+ public static void testUOEFromGetDeclaringClass() {
+ try {
+ StackWalker sw = StackWalker.getInstance();
+ sw.forEach(StackWalker.StackFrame::getDeclaringClass);
+ throw new RuntimeException("UOE expected");
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
+ public static void testUOEFromGetMethodType() {
+ try {
+ StackWalker sw = StackWalker.getInstance();
+ sw.forEach(StackWalker.StackFrame::getMethodType);
+ throw new RuntimeException("UOE expected");
+ } catch (UnsupportedOperationException expected) {}
+ }
}