Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use MethodHandles instead of reflect.Method #4329

Closed
wants to merge 2 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 33 additions & 20 deletions src/main/java/com/fasterxml/jackson/databind/jdk14/JDK14Util.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.fasterxml.jackson.databind.jdk14;

import java.lang.reflect.Method;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Collections;
import java.util.List;

Expand Down Expand Up @@ -41,9 +43,9 @@ public static AnnotatedConstructor findRecordConstructor(AnnotatedClass recordCl
}

static class RecordAccessor {
private final Method RECORD_GET_RECORD_COMPONENTS;
private final Method RECORD_COMPONENT_GET_NAME;
private final Method RECORD_COMPONENT_GET_TYPE;
private final MethodHandle RECORD_GET_RECORD_COMPONENTS;
private final MethodHandle RECORD_COMPONENT_GET_NAME;
private final MethodHandle RECORD_COMPONENT_GET_TYPE;

private final static RecordAccessor INSTANCE;
private final static RuntimeException PROBLEM;
Expand All @@ -62,14 +64,25 @@ static class RecordAccessor {

private RecordAccessor() throws RuntimeException {
try {
RECORD_GET_RECORD_COMPONENTS = Class.class.getMethod("getRecordComponents");
Class<?> c = Class.forName("java.lang.reflect.RecordComponent");
RECORD_COMPONENT_GET_NAME = c.getMethod("getName");
RECORD_COMPONENT_GET_TYPE = c.getMethod("getType");
} catch (Exception e) {
final Class<?> recordComponentClass = Class.forName("java.lang.reflect.RecordComponent");
final MethodHandles.Lookup lookup = MethodHandles.lookup();
final MethodType classReturnMethodType = MethodType.methodType(Class.class);
final MethodType stringReturnMethodType = MethodType.methodType(String.class);
final MethodHandle arrayTypeMethodHandle =
lookup.findVirtual(Class.class, "arrayType", classReturnMethodType);
final Class<?> recordComponentArrayClass = (Class<?>)
arrayTypeMethodHandle.invokeExact(recordComponentClass);
final MethodType recordComponentArrayMethodType = MethodType.methodType(recordComponentArrayClass);
RECORD_GET_RECORD_COMPONENTS = lookup.findVirtual(
Class.class, "getRecordComponents", recordComponentArrayMethodType);
RECORD_COMPONENT_GET_NAME = lookup.findVirtual(
recordComponentClass, "getName", stringReturnMethodType);
RECORD_COMPONENT_GET_TYPE = lookup.findVirtual(
recordComponentClass, "getType", classReturnMethodType);
} catch (Throwable t) {
throw new RuntimeException(String.format(
"Failed to access Methods needed to support `java.lang.Record`: (%s) %s",
e.getClass().getName(), e.getMessage()), e);
"Failed to access Methods needed to support `java.lang.Record`: (%s) %s",
t.getClass().getName(), t.getMessage()), t);
}
}

Expand All @@ -91,10 +104,10 @@ public String[] getRecordFieldNames(Class<?> recordType) throws IllegalArgumentE
for (int i = 0; i < components.length; i++) {
try {
names[i] = (String) RECORD_COMPONENT_GET_NAME.invoke(components[i]);
} catch (Exception e) {
} catch (Throwable t) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be the reason to catch Errors too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a required change unfortunately because invoke throws Throwable. this method shouldnt throw at all though

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Odd, not sure why it does that... never understood why method signature would do that (given Errors need not be declared anyway so why not just throws Exception). Oh well. At least this is for a reason then.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you know that Java reflection API is now implemented using MethodHandle(s)? So by using Java reflection, you are already using MethodHandle(s) under the hood.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what you mean?
https://openjdk.org/jeps/416

I'll close this PR anyway because of the animal sniffer issues.

throw new IllegalArgumentException(String.format(
"Failed to access name of field #%d (of %d) of Record type %s",
i, components.length, ClassUtil.nameOf(recordType)), e);
i, components.length, ClassUtil.nameOf(recordType)), t);
}
}
return names;
Expand All @@ -112,18 +125,18 @@ public RawTypeName[] getRecordFields(Class<?> recordType) throws IllegalArgument
String name;
try {
name = (String) RECORD_COMPONENT_GET_NAME.invoke(components[i]);
} catch (Exception e) {
} catch (Throwable t) {
throw new IllegalArgumentException(String.format(
"Failed to access name of field #%d (of %d) of Record type %s",
i, components.length, ClassUtil.nameOf(recordType)), e);
i, components.length, ClassUtil.nameOf(recordType)), t);
}
Class<?> type;
try {
type = (Class<?>) RECORD_COMPONENT_GET_TYPE.invoke(components[i]);
} catch (Exception e) {
} catch (Throwable t) {
throw new IllegalArgumentException(String.format(
"Failed to access type of field #%d (of %d) of Record type %s",
i, components.length, ClassUtil.nameOf(recordType)), e);
i, components.length, ClassUtil.nameOf(recordType)), t);
}
results[i] = new RawTypeName(type, name);
}
Expand All @@ -134,12 +147,12 @@ protected Object[] recordComponents(Class<?> recordType) throws IllegalArgumentE
{
try {
return (Object[]) RECORD_GET_RECORD_COMPONENTS.invoke(recordType);
} catch (Exception e) {
if (NativeImageUtil.isUnsupportedFeatureError(e)) {
} catch (Throwable t) {
if (NativeImageUtil.isUnsupportedFeatureError(t)) {
return null;
}
throw new IllegalArgumentException("Failed to access RecordComponents of type "
+ClassUtil.nameOf(recordType));
+ ClassUtil.nameOf(recordType));
}
}

Expand Down
Loading