Skip to content

Commit

Permalink
Use MethodHandles for invoking methods/field getters
Browse files Browse the repository at this point in the history
MethodHandles are faster and safer than Reflection
  • Loading branch information
arteam committed Oct 24, 2021
1 parent 83abe00 commit 9cae313
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,12 @@ private Response handleSingle(@NotNull Request request, @NotNull Object service)
return new ErrorResponse(id, INVALID_PARAMS);
}

Object result = method.getMethod().invoke(service, methodParams);
Object result;
try {
result = method.getMethodHandle().bindTo(service).invokeWithArguments(methodParams);
} catch (Throwable e) {
throw new RuntimeException(e);
}
return new SuccessResponse(id, result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
Expand All @@ -30,6 +32,7 @@
class Reflections {

private static final Logger log = LoggerFactory.getLogger(JsonRpcServer.class);
private static final MethodHandles.Lookup METHOD_HANDLES_LOOKUP = MethodHandles.lookup();

private Reflections() {
}
Expand Down Expand Up @@ -67,7 +70,6 @@ public static <T extends Annotation> T getAnnotation(@Nullable Annotation[] anno
@NotNull
public static ClassMetadata getClassMetadata(@NotNull Class<?> clazz) {
ImmutableMap.Builder<String, MethodMetadata> methodsMetadata = ImmutableMap.builder();

Class<?> searchType = clazz;
// Search through the class hierarchy
while (searchType != null) {
Expand Down Expand Up @@ -98,7 +100,13 @@ public static ClassMetadata getClassMetadata(@NotNull Class<?> clazz) {
}

method.setAccessible(true);
methodsMetadata.put(rpcMethodName, new MethodMetadata(rpcMethodName, method, methodParams));
MethodHandle methodHandle;
try {
methodHandle = METHOD_HANDLES_LOOKUP.unreflect(method);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
methodsMetadata.put(rpcMethodName, new MethodMetadata(rpcMethodName, methodHandle, methodParams));
}
searchType = searchType.getSuperclass();
}
Expand Down Expand Up @@ -152,8 +160,8 @@ private static ImmutableMap<String, ParameterMetadata> getMethodParameters(@NotN

static ErrorDataResolver buildErrorDataResolver(Class<? extends Throwable> throwableClass) {
Class<?> c = throwableClass;
Field dataField = null;
Method dataMethod = null;
MethodHandle dataField = null;
MethodHandle dataMethod = null;
while (c != null) {
for (Field field : c.getDeclaredFields()) {
if (field.isAnnotationPresent(JsonRpcErrorData.class)) {
Expand All @@ -162,7 +170,11 @@ static ErrorDataResolver buildErrorDataResolver(Class<? extends Throwable> throw
"@JsonRpcErrorData annotated property in " + c.getName());
}
field.setAccessible(true);
dataField = field;
try {
dataField = METHOD_HANDLES_LOOKUP.unreflectGetter(field);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
for (Method method : c.getDeclaredMethods()) {
Expand All @@ -180,17 +192,33 @@ static ErrorDataResolver buildErrorDataResolver(Class<? extends Throwable> throw
"@JsonRpcErrorData annotated property in " + c.getName());
}
method.setAccessible(true);
dataMethod = method;
try {
dataMethod = METHOD_HANDLES_LOOKUP.unreflect(method);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
c = c.getSuperclass();
}
if (dataField != null) {
Field finalDataField = dataField;
return t -> Optional.ofNullable(finalDataField.get(t));
MethodHandle finalDataField = dataField;
return t -> {
try {
return Optional.ofNullable(finalDataField.invoke(t));
} catch (Throwable e) {
throw new IllegalStateException(e);
}
};
} else if (dataMethod != null) {
Method finalDataMethod = dataMethod;
return t -> Optional.ofNullable(finalDataMethod.invoke(t));
MethodHandle finalDataMethod = dataMethod;
return t -> {
try {
return Optional.ofNullable(finalDataMethod.invoke(t));
} catch (Throwable e) {
throw new IllegalStateException(e);
}
};
} else {
return t -> Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.google.common.collect.ImmutableMap;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Method;
import java.lang.invoke.MethodHandle;

/**
* Date: 8/1/14
Expand All @@ -20,21 +20,21 @@ public class MethodMetadata {
private final String name;

/**
* Actual method
* Actual method handle
*/
@NotNull
private final Method method;
private final MethodHandle methodHandle;

/**
* Map of method params by RPC name
*/
@NotNull
private final ImmutableMap<String, ParameterMetadata> params;

public MethodMetadata(@NotNull String name, @NotNull Method method,
public MethodMetadata(@NotNull String name, @NotNull MethodHandle methodHandle,
@NotNull ImmutableMap<String, ParameterMetadata> params) {
this.name = name;
this.method = method;
this.methodHandle = methodHandle;
this.params = params;
}

Expand All @@ -44,8 +44,8 @@ public String getName() {
}

@NotNull
public Method getMethod() {
return method;
public MethodHandle getMethodHandle() {
return methodHandle;
}

@NotNull
Expand All @@ -57,7 +57,7 @@ public ImmutableMap<String, ParameterMetadata> getParams() {
public String toString() {
return MoreObjects.toStringHelper(this)
.add("name", name)
.add("method", method)
.add("method", methodHandle)
.add("params", params)
.toString();
}
Expand Down

0 comments on commit 9cae313

Please sign in to comment.