Background:
Since Nashorn is being dropped in JDK15, I am looking for an alternative for an application I am working on. I am currently only using for the dynamic execution of some user-definable formatting snippets within a java swing desktop application.
I don't particularly want to add another library dependency to my app (like rhino). I would be willing to use nashorn as an additional dependency if it is available (this would save me from having to rewrite code, and ensure compatibility with existing js snippets). I haven't seen that it is available anywhere except as something that was related to minecraft.
I won't switch to Graal vm.
The problem:
I was considering using JShell (though not javascript, much of the formatting code is very similar), but the performance is abysmal the way I am calling it:
try(JShell js = JShell.create())
{
js.eval("public int add(int a, int b) { return a + b; }");
for(int i = 0; i < 100; i++)
{
List<SnippetEvent> eval = js.eval("add(5,6)");
eval.forEach(se -> {
System.out.println(se.value());
});
}
}
The for loop in the that code is taking ~6 seconds to run (compared to ~11 microseconds in nashorn). This will not be fast enough for my application.
Is there a way to get the class bytecode back out of JSell so I can use reflection to execute the method directly instead of calling 'eval' again?
Is there a way to get a 'method handle' to a method I create in JShell?
Is there any way of making a function where the behavior is defined in JShell, but can be called with high performance from 'normal' java?
I stumbled across the same performance issue today and I think I found a solution. It boils down to the following four steps:
LocalExecutionControlProvider
such that they share the same class loaderThe following code snippet implements this approach for your example. On my machine, it takes approx. 350ms for the initial function construction via JShell and approx. 50ms for the 10_000 subsequent function calls.
package app;
import jdk.jshell.JShell;
import jdk.jshell.execution.LocalExecutionControlProvider;
import org.junit.jupiter.api.Test;
import java.util.function.BiFunction;
public class Debug {
public static BiFunction<Integer, Integer, Integer> function = null;
@Test
public void debug() {
JShell jShell = JShell.builder()
.in(System.in).out(System.out).err(System.err)
.executionEngine(new LocalExecutionControlProvider(), null)
.build();
jShell.eval("app.Debug.function = (a,b) -> a+b;");
BiFunction<Integer, Integer, Integer> theFunction = Debug.function;
for (int a = 0; a < 100; a++) {
for (int b = 0; b < 100; b++) {
assert theFunction.apply(a, b) == a + b;
}
}
}
}