JSR-223: Как связать функцию хоста с переменным значением с ScriptEngine - PullRequest
0 голосов
/ 26 марта 2019

Я пытаюсь запустить Javascript через JSR-223 ScriptEngine под виртуальной машиной Java для вызова предоставленного пользователем кода и передачи ему обратного вызова «в стиле обещания»: то есть функция, которая не принимает аргументов (успешное завершение) без значения); один аргумент (null для успешного завершения без значения или объекта ошибки); или два аргумента (null для успешного завершения, значение).

Я использую GaalVM для этого и помещаю свой обратный вызов в объект привязки Javascript. Как то так:

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);

Проблема в том, что я не могу понять, что поставить в качестве привязки "callback":

Я попробовал простое:

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

В этом случае вызов обратного вызова с двумя аргументами работает нормально, но с одним аргументом (или без аргументов) я получаю ошибку:

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

Я пытался получить массив Object так:

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

Что заставило GraalVM бросить в меня это:

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')

В настоящее время я смотрю на ручную мультидиспетчерскую работу в Javascript - в основном определяю несколько функций хоста обратного вызова, а затем в Javascript проверяю количество аргументов и отправляю соответственно:

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]); "+
  "} })");

Но мне кажется, что это плохая идея.

1 Ответ

1 голос
/ 27 марта 2019
MyCallback callback = (arguments) -> { /*...*/ };

, где

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

будет работать для вас, я думаю.

...