Skip to content

Commit

Permalink
[GR-23725] Interpreted method handles support.
Browse files Browse the repository at this point in the history
PullRequest: graal/7762
  • Loading branch information
loicottet committed Dec 5, 2020
2 parents e8b4a20 + 0908151 commit 83326c6
Show file tree
Hide file tree
Showing 18 changed files with 1,308 additions and 291 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
import static com.oracle.svm.jvmtiagentbase.Support.clearException;
import static com.oracle.svm.jvmtiagentbase.Support.fromCString;
import static com.oracle.svm.jvmtiagentbase.Support.fromJniString;
import static com.oracle.svm.jvmtiagentbase.Support.getBooleanArgument;
import static com.oracle.svm.jvmtiagentbase.Support.getByteArgument;
import static com.oracle.svm.jvmtiagentbase.Support.getCallerClass;
import static com.oracle.svm.jvmtiagentbase.Support.getCallerMethod;
import static com.oracle.svm.jvmtiagentbase.Support.getClassNameOr;
Expand Down Expand Up @@ -145,9 +143,6 @@ final class BreakpointInterceptor {
/** Enables experimental support for instrumenting class lookups via {@code ClassLoader}. */
private static boolean experimentalClassLoaderSupport = false;

/** Enables support for non-interpreted method handles. */
private static boolean methodHandleSupport = false;

/**
* Locations in methods where explicit calls to {@code ClassLoader.loadClass} have been found.
*/
Expand Down Expand Up @@ -749,91 +744,83 @@ private static boolean isLoadClassInvocation(JNIObjectHandle clazz, JNIMethodId
}
}

private static boolean resolveMemberName(JNIEnvironment jni, Breakpoint bp) {
if (!methodHandleSupport) {
return false;
}

JNIObjectHandle factory = getObjectArgument(0);
byte refKind = getByteArgument(1);
JNIObjectHandle unresolvedName = getObjectArgument(2);
JNIObjectHandle lookupClass = getObjectArgument(3);
boolean speculativeResolve = getBooleanArgument(4);
private static boolean findMethodHandle(JNIEnvironment jni, Breakpoint bp) {
JNIObjectHandle callerClass = getDirectCallerClass();
JNIObjectHandle lookup = getObjectArgument(0);
JNIObjectHandle declaringClass = getObjectArgument(1);
JNIObjectHandle methodName = getObjectArgument(2);
JNIObjectHandle methodType = getObjectArgument(3);

JNIObjectHandle resolvedName = Support.callObjectMethodBLLZ(jni, factory, bp.method, refKind, unresolvedName, lookupClass, speculativeResolve);
JNIObjectHandle result = Support.callObjectMethodLLL(jni, lookup, bp.method, declaringClass, methodName, methodType);
if (clearException(jni)) {
resolvedName = nullHandle();
}
if (resolvedName.equal(nullHandle())) {
return false;
result = nullHandle();
}

boolean isMethod = Support.callBooleanMethod(jni, resolvedName, agent.handles().javaLangInvokeMemberNameIsMethod);
if (clearException(jni)) {
isMethod = false;
}
boolean isConstructor = Support.callBooleanMethod(jni, resolvedName, agent.handles().javaLangInvokeMemberNameIsConstructor);
if (clearException(jni)) {
isConstructor = false;
}
boolean isField = Support.callBooleanMethod(jni, resolvedName, agent.handles().javaLangInvokeMemberNameIsField);
if (clearException(jni)) {
isField = false;
}
return methodMethodHandle(jni, declaringClass, callerClass, methodName, getParamTypes(jni, methodType), result);
}

JNIObjectHandle declaringClass = Support.callObjectMethod(jni, resolvedName, agent.handles().javaLangInvokeMemberNameGetDeclaringClass);
if (clearException(jni)) {
declaringClass = nullHandle();
}
JNIObjectHandle declaringClassNameHandle = Support.callObjectMethod(jni, declaringClass, agent.handles().javaLangClassGetName);
if (clearException(jni)) {
declaringClassNameHandle = nullHandle();
}
JNIObjectHandle nameHandle = Support.callObjectMethod(jni, resolvedName, agent.handles().javaLangInvokeMemberNameGetName);
if (clearException(jni)) {
nameHandle = nullHandle();
}
JNIObjectHandle paramTypesHandle = Support.callObjectMethod(jni, resolvedName, agent.handles().javaLangInvokeMemberNameGetParameterTypes);
private static boolean findSpecialHandle(JNIEnvironment jni, Breakpoint bp) {
JNIObjectHandle callerClass = getDirectCallerClass();
JNIObjectHandle lookup = getObjectArgument(0);
JNIObjectHandle declaringClass = getObjectArgument(1);
JNIObjectHandle methodName = getObjectArgument(2);
JNIObjectHandle methodType = getObjectArgument(3);
JNIObjectHandle specialCaller = getObjectArgument(4);

JNIObjectHandle result = Support.callObjectMethodLLLL(jni, lookup, bp.method, declaringClass, methodName, methodType, specialCaller);
if (clearException(jni)) {
paramTypesHandle = nullHandle();
result = nullHandle();
}

JNIObjectHandle result = nullHandle();
String function;
String declaringClassName = fromJniString(jni, declaringClassNameHandle);
return methodMethodHandle(jni, declaringClass, callerClass, methodName, getParamTypes(jni, methodType), result);
}

private static boolean methodMethodHandle(JNIEnvironment jni, JNIObjectHandle declaringClass, JNIObjectHandle callerClass, JNIObjectHandle nameHandle, JNIObjectHandle paramTypesHandle,
JNIObjectHandle result) {
String name = fromJniString(jni, nameHandle);
Object paramTypes = getClassArrayNames(jni, paramTypesHandle);
Object[] args;
if (isMethod) {
result = Support.callObjectMethodLL(jni, declaringClass, agent.handles().javaLangClassGetDeclaredMethod, nameHandle, paramTypesHandle);
function = "getDeclaredMethod";
args = new Object[]{name, paramTypes};
} else if (isConstructor) {
result = Support.callObjectMethodL(jni, declaringClass, agent.handles().javaLangClassGetDeclaredConstructor, paramTypesHandle);
function = "getDeclaredConstructor";
args = new Object[]{paramTypes};
} else if (isField) {
result = Support.callObjectMethodL(jni, declaringClass, agent.handles().javaLangClassGetDeclaredField, nameHandle);
function = "getDeclaredField";
args = new Object[]{name};
} else {
function = null;
args = new Object[0];
}
if (function != null && clearException(jni)) {
traceBreakpoint(jni, declaringClass, nullHandle(), callerClass, "findMethodHandle", result.notEqual(nullHandle()), name, paramTypes);
return true;
}

private static boolean findConstructorHandle(JNIEnvironment jni, Breakpoint bp) {
JNIObjectHandle callerClass = getDirectCallerClass();
JNIObjectHandle lookup = getObjectArgument(0);
JNIObjectHandle declaringClass = getObjectArgument(1);
JNIObjectHandle methodType = getObjectArgument(2);

JNIObjectHandle result = Support.callObjectMethodLL(jni, lookup, bp.method, declaringClass, methodType);
if (clearException(jni)) {
result = nullHandle();
}
JNIObjectHandle resultDeclaringClass = Support.callObjectMethod(jni, result, agent.handles().javaLangReflectMemberGetDeclaringClass);

Object paramTypes = getClassArrayNames(jni, getParamTypes(jni, methodType));
traceBreakpoint(jni, declaringClass, nullHandle(), callerClass, "findConstructorHandle", result.notEqual(nullHandle()), paramTypes);
return true;
}

private static JNIObjectHandle getParamTypes(JNIEnvironment jni, JNIObjectHandle methodType) {
JNIObjectHandle paramTypesHandle = Support.callObjectMethod(jni, methodType, agent.handles().getJavaLangInvokeMethodTypeParameterArray(jni));
if (clearException(jni)) {
resultDeclaringClass = nullHandle();
paramTypesHandle = nullHandle();
}
return paramTypesHandle;
}

/* Lambdas are intrinsified and java.lang.invoke functions are added during compilation */
boolean ignore = declaringClassName == null || name == null || declaringClassName.startsWith("java.lang.invoke") ||
declaringClassName.contains("$$Lambda$") || name.contains("$anonfun$") || name.startsWith("lambda$");
if (!ignore) {
traceBreakpoint(jni, declaringClass, resultDeclaringClass, lookupClass, function, result.notEqual(nullHandle()), args);
private static boolean findFieldHandle(JNIEnvironment jni, Breakpoint bp) {
JNIObjectHandle callerClass = getDirectCallerClass();
JNIObjectHandle lookup = getObjectArgument(0);
JNIObjectHandle declaringClass = getObjectArgument(1);
JNIObjectHandle fieldName = getObjectArgument(2);
JNIObjectHandle fieldType = getObjectArgument(3);

JNIObjectHandle result = Support.callObjectMethodLLL(jni, lookup, bp.method, declaringClass, fieldName, fieldType);
if (clearException(jni)) {
result = nullHandle();
}

String name = fromJniString(jni, fieldName);
traceBreakpoint(jni, declaringClass, nullHandle(), callerClass, "findFieldHandle", result.notEqual(nullHandle()), name);
return true;
}

Expand Down Expand Up @@ -1030,12 +1017,11 @@ private static void installBreakpointIfClassLoader(JNIEnvironment jni, JNIObject
JvmtiEnv.class, JNIEnvironment.class, JNIObjectHandle.class, JNIObjectHandle.class);

public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, TraceWriter writer, NativeImageAgent nativeImageTracingAgent,
boolean exptlClassLoaderSupport, boolean supportMethodHandles) {
boolean exptlClassLoaderSupport) {

BreakpointInterceptor.traceWriter = writer;
BreakpointInterceptor.agent = nativeImageTracingAgent;
BreakpointInterceptor.experimentalClassLoaderSupport = exptlClassLoaderSupport;
BreakpointInterceptor.methodHandleSupport = supportMethodHandles;

JvmtiCapabilities capabilities = UnmanagedMemory.calloc(SizeOf.get(JvmtiCapabilities.class));
check(jvmti.getFunctions().GetCapabilities().invoke(jvmti, capabilities));
Expand Down Expand Up @@ -1271,8 +1257,37 @@ private interface BreakpointHandler {
optionalBrk("jdk/internal/misc/Unsafe", "objectFieldOffset", "(Ljava/lang/reflect/Field;)J", BreakpointInterceptor::objectFieldOffset),
optionalBrk("jdk/internal/misc/Unsafe", "objectFieldOffset", "(Ljava/lang/Class;Ljava/lang/String;)J", BreakpointInterceptor::objectFieldOffsetByName),

optionalBrk("java/lang/invoke/MemberName$Factory", "resolve", "(BLjava/lang/invoke/MemberName;Ljava/lang/Class;Z)Ljava/lang/invoke/MemberName;",
BreakpointInterceptor::resolveMemberName),
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findStatic",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
BreakpointInterceptor::findMethodHandle),
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findVirtual",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
BreakpointInterceptor::findMethodHandle),
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findConstructor",
"(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
BreakpointInterceptor::findConstructorHandle),
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findSpecial",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;",
BreakpointInterceptor::findSpecialHandle),
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findGetter",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;",
BreakpointInterceptor::findFieldHandle),
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findSetter",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;",
BreakpointInterceptor::findFieldHandle),
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findStaticGetter",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;",
BreakpointInterceptor::findFieldHandle),
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findStaticSetter",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;",
BreakpointInterceptor::findFieldHandle),
/* VarHandles were introduced in Java 9 */
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findVarHandle",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/VarHandle;",
BreakpointInterceptor::findFieldHandle),
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findStaticVarHandle",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/VarHandle;",
BreakpointInterceptor::findFieldHandle),
};

private static final BreakpointSpecification CLASSLOADER_LOAD_CLASS_BREAKPOINT_SPECIFICATION = optionalBrk("java/lang/ClassLoader", "loadClass",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
List<String> callerFilterFiles = new ArrayList<>();
List<String> accessFilterFiles = new ArrayList<>();
boolean experimentalClassLoaderSupport = true;
boolean methodHandleSupport = false;
boolean build = false;
int configWritePeriod = -1; // in seconds
int configWritePeriodInitialDelay = 1; // in seconds
Expand Down Expand Up @@ -172,10 +171,6 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
build = true;
} else if (token.startsWith("build=")) {
build = Boolean.parseBoolean(getTokenValue(token));
} else if (token.equals("method-handle-support")) {
methodHandleSupport = true;
} else if (token.startsWith("method-handle-support")) {
methodHandleSupport = Boolean.parseBoolean(getTokenValue(token));
} else {
System.err.println(MESSAGE_PREFIX + "unsupported option: '" + token + "'. Please read BuildConfiguration.md.");
return 1;
Expand Down Expand Up @@ -254,7 +249,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c

accessAdvisor = createAccessAdvisor(builtinHeuristicFilter, callerFilter, accessFilter);
try {
BreakpointInterceptor.onLoad(jvmti, callbacks, traceWriter, this, experimentalClassLoaderSupport, methodHandleSupport);
BreakpointInterceptor.onLoad(jvmti, callbacks, traceWriter, this, experimentalClassLoaderSupport);
} catch (Throwable t) {
System.err.println(MESSAGE_PREFIX + t);
return 3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@

import static com.oracle.svm.jni.JNIObjectHandles.nullHandle;

import com.oracle.svm.jni.nativeapi.JNIFieldId;
import org.graalvm.word.WordFactory;

import com.oracle.svm.jni.nativeapi.JNIEnvironment;
import com.oracle.svm.jni.nativeapi.JNIFieldId;
import com.oracle.svm.jni.nativeapi.JNIMethodId;
import com.oracle.svm.jni.nativeapi.JNIObjectHandle;
import com.oracle.svm.jvmtiagentbase.JNIHandleSet;
Expand All @@ -49,12 +51,7 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet {

final JNIObjectHandle javaLangClassLoader;

final JNIMethodId javaLangInvokeMemberNameGetDeclaringClass;
final JNIMethodId javaLangInvokeMemberNameGetName;
final JNIMethodId javaLangInvokeMemberNameGetParameterTypes;
final JNIMethodId javaLangInvokeMemberNameIsMethod;
final JNIMethodId javaLangInvokeMemberNameIsConstructor;
final JNIMethodId javaLangInvokeMemberNameIsField;
private JNIMethodId javaLangInvokeMethodTypeParameterArray = WordFactory.nullPointer();

private JNIMethodId javaUtilResourceBundleGetBundleImplSLCC;

Expand Down Expand Up @@ -86,14 +83,14 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet {
javaUtilEnumerationNextElement = getMethodId(env, javaUtilEnumeration, "nextElement", "()Ljava/lang/Object;", false);

javaLangClassLoader = newClassGlobalRef(env, "java/lang/ClassLoader");
}

JNIObjectHandle javaLangInvokeMemberName = findClass(env, "java/lang/invoke/MemberName");
javaLangInvokeMemberNameGetDeclaringClass = getMethodId(env, javaLangInvokeMemberName, "getDeclaringClass", "()Ljava/lang/Class;", false);
javaLangInvokeMemberNameGetName = getMethodId(env, javaLangInvokeMemberName, "getName", "()Ljava/lang/String;", false);
javaLangInvokeMemberNameGetParameterTypes = getMethodId(env, javaLangInvokeMemberName, "getParameterTypes", "()[Ljava/lang/Class;", false);
javaLangInvokeMemberNameIsMethod = getMethodId(env, javaLangInvokeMemberName, "isMethod", "()Z", false);
javaLangInvokeMemberNameIsConstructor = getMethodId(env, javaLangInvokeMemberName, "isConstructor", "()Z", false);
javaLangInvokeMemberNameIsField = getMethodId(env, javaLangInvokeMemberName, "isField", "()Z", false);
JNIMethodId getJavaLangInvokeMethodTypeParameterArray(JNIEnvironment env) {
if (javaLangInvokeMethodTypeParameterArray.isNull()) {
JNIObjectHandle javaLangInvokeMethodType = newClassGlobalRef(env, "java/lang/invoke/MethodType");
javaLangInvokeMethodTypeParameterArray = getMethodId(env, javaLangInvokeMethodType, "parameterArray", "()[Ljava/lang/Class;", false);
}
return javaLangInvokeMethodTypeParameterArray;
}

JNIMethodId tryGetJavaUtilResourceBundleGetBundleImplSLCC(JNIEnvironment env) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ public void processEntry(Map<String, ?> entry) {
unsafeAccess = true;
// fall through
case "getDeclaredField":
case "findFieldHandle":
memberKind = ConfigurationMemberKind.DECLARED;
// fall through
case "getField": {
Expand All @@ -162,6 +163,7 @@ public void processEntry(Map<String, ?> entry) {
}

case "getDeclaredMethod":
case "findMethodHandle":
memberKind = ConfigurationMemberKind.DECLARED;
// fall through
case "getMethod": {
Expand All @@ -179,6 +181,7 @@ public void processEntry(Map<String, ?> entry) {
}

case "getDeclaredConstructor":
case "findConstructorHandle":
memberKind = ConfigurationMemberKind.DECLARED; // fall through
case "getConstructor": {
List<String> parameterTypes = singleElement(args);
Expand Down
Loading

0 comments on commit 83326c6

Please sign in to comment.