Dynamically inline MethodHandles

Overview

HandleWrapper

MethodHandles can be as fast as direct invocation, as long as they are static final fields. Then they can be inlined. But what if we want to use them dynamically? There's definitely simpler solutions than this one, but I wanted to learn a thing or 2 about bytecode, so here I use ASM to create classes at runtime that contain the MethodHandle as a static final field. These "HandleWrappers" are almost as fast as direct invocation.

Benchmark                                        Mode  Cnt  Score   Error  Units
QuickBenchmark.benchmarkDirect                   avgt   10  3,601 ± 0,015  ns/op
QuickBenchmark.benchmarkStaticFinalMethodHandle  avgt   10  3,619 ± 0,027  ns/op
QuickBenchmark.benchmarkWrapper                  avgt   10  3,839 ± 0,043  ns/op

Usage

Assume we want to call following method:

public class SomeClass {
    private int someMethod(Object someArg, String someOtherArg) { ... }
}

First we need to obtain a MethodHandle for the method:

Method method = SomeClass.class.getDeclaredMethod("someMethod", Object.class, String.class);
method.setAccessible(true);
MethodHandle handle = MethodHandles.lookup().unreflect(method);

Now we can use the WrapperFactory to wrap the Handle and use it:

HandleWrapper wrapper = WrapperFactory.wrap(handle, SomeClass.class, false, int.class, Object.class, String.class);
int result = (int) wrapper.invoke(objectOfSomeClass, someArg, "someOtherArg");

You can also wrap your MethodHandle in an interface, the interfaces methods can target all public methods of the MethodHandle class, they just need to have the same signature, and if they target a SignaturePolymorphic method like invoke or invokeExact they need to have the same Signature as the target method (If the method is not static the first argument needs to be an Object of the targeted class).

public interface CustomInterface {
    int accessSomeMethod(SomeClass target, Object someArg, String someOtherArg);
}

Now declare which methods from the interface link to which MethodHandle method and call the WrapperFactory.

Method from = CustomInterface.class.getDeclaredMethod(accessSomeMethod ...);
Method to   = MethodHandle.class.getDeclaredMethod("invoke", Object[].class);
Map<Method, Method> links = new HashMap<>();
links.put(from, to);

CustomInterface customWrapper = WrapperFactory.wrap(CustomInterface.class, links, handle);
int result = customWrapper.accessSomeMethod(target, someArg, "someOtherArg");

License

The contents of this project are licensed under the MIT license.

You might also like...
Releases(1.0)
Owner
null
Dynamically filters JPA entities with a simple query syntax. Provides JPA/Hibernate predicates and Spring Data specifications.

Spring Filter You need a way to dynamically filter entities without any effort? Just add me to your pom.xml. Your API will gain a full featured search

Turkraft 142 Dec 13, 2022
A plugin for the ja-netfilter, it can replace strings dynamically.

plugin-mymap A plugin for the ja-netfilter, it can replace strings dynamically. Use the mvn clean package command to compile and use mymap-v1.0.1-jar-

zfkun 356 Dec 12, 2022
JHook - A tool that can dynamically modify Java classes at runtime.

JHook A tool that can dynamically modify Java classes at runtime. Demo Tested on Java 1.8 - Java 17, just support JDK package com.binklac.jhook.test;

VeroFess 11 Dec 23, 2022
A library for IDEs and Code Editors to compile java projects faster dynamically

A library for IDEs and Code Editors to compile java projects faster dynamically

omega ui 2 Feb 22, 2022