Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Scripting With Nashorn (JSR 223) & Pre-compilation

I am using Nashorn via JSR 223 to execute small snippets of user entered script:

public Invocable buildInvocable(String script) throws ScriptException {
    ScriptEngine engine = new ScriptEngineManager().getEngineByName(ENGINE);
    engine.eval(functions);
    engine.eval(script);
    return (Invocable) engine;
}

The varying user script calls JavaScript functions that are defined in a static, central library (held in the functions String in the code snippet above).

Every time I want to get hold of an Invocable that I can call from my Java I am constantly having to recompile the large library code.

Is there any way to join a previously compiled piece of code in with new code?

like image 591
Kong Avatar asked Mar 31 '14 01:03

Kong


People also ask

What JSR 223?

# Overview. JSR223 (opens new window) (spec (opens new window)) is a standard scripting API for Java Virtual Machine (JVM) languages (opens new window). The JVM languages provide varying levels of support for the JSR223 API and interoperability with the Java runtime.

Which JavaScript is replaced with nashorn?

Java SE 8 will instead ship with a new engine called Oracle Nashorn, which is based on JSR 292 and invokedynamic . It provides better compliance with the ECMA normalized JavaScript specification and better runtime performance through invokedynamic -bound call sites.

When was nashorn removed Java?

With the release of Java 11, Nashorn was deprecated citing challenges to maintenance, and has been removed from JDK 15 onwards.

What is the use of nashorn JavaScript engine?

Nashorn: Nashorn is a JavaScript engine which is introduced in JDK 8. With the help of Nashorn, we can execute JavaScript code at Java Virtual Machine. Nashorn is introduced in JDK 8 to replace existing JavaScript engine i.e. Rhino. Nashorn is far better than Rhino in term of performance.


2 Answers

Put compiled functions into Bindings like:

private static final String FUNCTIONS =
    "function() {" +
    "  return \"Hello\";" +
    "}";

public static void main(String... args) throws Exception {
    ScriptEngine engine = new ScriptEngineManager().getEngineByMimeType("text/javascript");

    // Compile common functions once
    CompiledScript compiled = ((Compilable) engine).compile(FUNCTIONS);
    Object sayHello = compiled.eval();

    // Load users' script each time
    SimpleBindings global = new SimpleBindings();
    global.put("sayHello", sayHello);
    String script = "sayHello()";
    System.out.println(engine.eval(script, global));
}
like image 159
auntyellow Avatar answered Sep 23 '22 09:09

auntyellow


If you need you precompile and call JavaSctipt functions with various arguments you may compile them separately and assemble execution flow in Java. With JavaScript engine Nashorn available in Java8 you can do:

private static final String FUNCTIONS =
  "function hello( arg ) {" +        //<-- passing java.lang.String from Java
    "  return 'Hello ' + arg;" +     //<-- returning string back
    "};" +
    "function sayTime( arg ) {" +   //<-- passing java.util.HashMap from Java
    "  return 'Java time ' + arg.get( 'time' );" +  //<-- returning string back
    "};" +
    "function () {" +                 //<-- this callable "function pointer" is being returned on [step1] below
    "  return { 'hello': hello, 'sayTime': sayTime };" +
    "};";

public static void main(String... args) throws Exception {
  ScriptEngine engine = new ScriptEngineManager().getEngineByName( "Nashorn" );

  CompiledScript compiled = ((Compilable) engine).compile(FUNCTIONS);
  ScriptObjectMirror lastFunction = (ScriptObjectMirror)compiled.eval();   // [step1]

  ScriptObjectMirror functionTable = (ScriptObjectMirror)lastFunction.call( null ); // this method retrieves function table
  String[] functionNames = functionTable.getOwnKeys( true );
  System.out.println( "Function names: " + Arrays.toString( functionNames ) );

  System.out.println( functionTable.callMember( "hello", "Robert" ) ); //<-- calling hello() with String as argiment

  Map map = new HashMap();
  map.put( "time", new Date().toString() ); //<-- preparing hashmap

  System.out.println( functionTable.callMember( "sayTime", map ) );  //<-- calling sayTime() with HashMap as argument
}

You may pass Java objects inside of JavaSctipt, see example with java.util.HashMap above.

Output is:

Function names: [hello, sayTime]
Hello Robert
Java time Fri Jan 12 12:23:15 EST 2018
like image 38
Dmitriy Pichugin Avatar answered Sep 19 '22 09:09

Dmitriy Pichugin