8186050: StackFrame should provide the method signature

Reviewed-by: alanb, bchristi, forax, plevart
This commit is contained in:
Mandy Chung 2017-09-29 11:33:08 -07:00
parent f2a9034adf
commit 5d986605ca
7 changed files with 172 additions and 9 deletions

View File

@ -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

View File

@ -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 <a href="ClassLoader.html#name">binary name</a>
* 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 <i>descriptor</i> of the method represented by
* this stack frame as defined by
* <cite>The Java Virtual Machine Specification</cite>.
*
* @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.

View File

@ -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.

View File

@ -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;

View File

@ -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}.

View File

@ -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<StackFrame> frames = new StackBuilder(16, 16).build();
Map<String, MethodType> 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<String, MethodType> 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));
}
}
}

View File

@ -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) {}
}
}