简体   繁体   中英

JSR-223: How to bind a variadic host function to ScriptEngine

I'm trying to run Javascript through a JSR-223 ScriptEngine under a Java VM to call user-provided code and pass it a "promise-style" callback: ie a function that accepts either no arguments (successful completion with no value); a single argument ( null for successful completion with no value or an error object); or two arguments ( null for successful completion, a value).

I'm using GaalVM for this and I'm putting my callback into the Javascript binding object. Something like this:

var engine = new ScriptEngineManager().getEngineByName("graal.js");
var binding = engine.getBindings(ScriptContext.ENGINE_SCOPE);
binding.put("exports", engine.eval("new Object()"));
engine.eval(userCode, binding);
binding.put("data", data);
binding.put("callback", callback);
engine.eval("exports.handler(data, callback)", binding);

The problem is that I can't figure out what to put as the "callback" binding:

I've tried the simple:

BiFunction<Object, Object, Object> callback = (err, value) -> { /* ... */ };

in which case calling the callback with two arguments works fine, but with a single argument (or no arguments) I get the error:

org.graalvm.polyglot.PolyglotException: TypeError: EXECUTE on 
  JavaObject[my.package.JavascriptRun$$Lambda$771/0x0000000840844040@4cf04c6a 
  (my.package.JavascriptRun$$Lambda$771/0x0000000840844040)] failed due to: 
  Arity error - expected: 2 actual: 1

I've tried getting an Object array like so:

Function<Object[], Object> callback = (args) -> { /* ... */ };

Which caused GraalVM to throw this at me:

org.graalvm.polyglot.PolyglotException: TypeError: EXECUTE on
   JavaObject[my.package.JavascriptRun$$Lambda$771/0x0000000840844040@855ef90 
  (my.package.JavascriptRun$$Lambda$771/0x0000000840844040)] failed due to:
  java.lang.ClassCastException: class com.oracle.truffle.polyglot.PolyglotMap
  cannot be cast to class [Ljava.lang.Object; 
  (com.oracle.truffle.polyglot.PolyglotMap is in unnamed module of loader 'app';
   [Ljava.lang.Object; is in module java.base of loader 'bootstrap')

I'm currently looking at doing manual multi-dispatch in Javascript - basically defining multiple callback host functions, and then in Javascript check the number of arguments and dispatch accordingly:

engine.eval("exports.handler(data, function() { "+
  "switch(arguments.length) { "+
  "case 0: return callback0(); "+
  "case 1: return callback1(arguments[0]); "+
  "default: return callback2(arguments[0],arguments[1]); "+
  "} })");

But that seems like such a bad idea to me.

MyCallback callback = (arguments) -> { /*...*/ };

where

@FunctionalInterface
public static interface MyCallback {
    Object call(Object... arguments);
}

will work for you, I guess.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM