'How to change MethodHandle arguments after being inserted?
Suppose you have MethodHandle
and some arguments have been specified, how to change those arguments after being set?
import static java.lang.invoke.MethodType.*;
import static java.lang.invoke.MethodHandles.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
public class SomeTest {
public static void main(String[] args) throws Throwable {
MethodHandle methodHandle = MethodHandles.lookup().findVirtual(SomeTest.class,
"someMethod", methodType(void.class, String.class));
methodHandle = MethodHandles.insertArguments(methodHandle, 1, "Hi");
// this invoke calls with "Hi", which is fine
methodHandle.invoke(new SomeTest());
// here, how to change the arguments to be e.g. "Hello" instead of "Hi"
methodHandle.invoke(new SomeTest());
}
public void someMethod(String a) {
System.out.println("Called with " + a);
}
}
I have tried to use MethodHandles.filterArguments()
....
methodHandle = MethodHandles.filterArguments(methodHandle, 1,
MethodHandles.lookup().findStatic(SomeTest.class, "returnSomething",
methodType(String.class)));
methodHandle.invoke(new SomeTest());
}
public static String returnSomething() {
return "Hello";
}
but I get an exception:
Exception in thread "main" java.lang.IllegalArgumentException: too many filters
at java.lang.invoke.MethodHandleStatics.newIllegalArgumentException(MethodHandleStatics.java:139)
at java.lang.invoke.MethodHandles.filterArgumentsCheckArity(MethodHandles.java:2623)
at java.lang.invoke.MethodHandles.filterArguments(MethodHandles.java:2595)
at test.test.SomeTest.main(SomeTest.java:22)
Solution 1:[1]
2 methods:
Reuse your original method handle and bind it to another string:
MethodHandle methodHandle = MethodHandles.lookup().findVirtual( SomeTest.class, "someMethod", methodType(void.class, String.class) ); MethodHandle hi = MethodHandles.insertArguments(methodHandle, 1, "Hi"); MethodHandle hello = MethodHandles.insertArguments(methodHandle, 1, "Hello"); hi.invoke(new SomeTest()); // "Hi" hello.invoke(new SomeTest()); // "Hello"
Bind the second argument to a getter of a class member, which you manipulate. You have to filter the arguments with an "exactInvoker" that will perform the getter to actually get the String value. See:
public class SomeTest { public static class StringHolder { public String toPrint; StringHolder(String toPrint) { this.toPrint = toPrint; } } public static void main(String[] args) throws Throwable { MethodHandle toPrintGetter = MethodHandles.lookup().findGetter( StringHolder.class, "toPrint", String.class); MethodHandle someMethod = MethodHandles.lookup().findVirtual( SomeTest.class, "someMethod", MethodType.methodType(void.class, String.class) ); StringHolder holder = new StringHolder("Hi"); someMethod = MethodHandles.filterArguments( someMethod, 1, MethodHandles.exactInvoker(MethodType.methodType(String.class)) ); MethodHandle stringPrinter = MethodHandles.insertArguments( someMethod, 1, toPrintGetter.bindTo(holder) ); stringPrinter.invokeExact(new SomeTest()); // prints "Hi" holder.toPrint = "Hello"; stringPrinter.invokeExact(new SomeTest()); // prints "Hello" } public void someMethod(String a) { System.out.println("Called with " + a); } }
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Brice |