Как передать типы и функции в сценарий JSR-223?

Типичный сценарий JSR-223 начинается с ряда суррогатных импортов, таких как этот (JavaScript+Nashorn, выбранный в качестве примера):

// "import" classes and static methods
var Foo = Java.type("my.package.Foo"); // application classes require Java.type() use
var bar = Foo.bar; // static method
var Logger = java.util.logging.Logger; // system classes can be accessed directly
var sin = java.lang.Math.sin; // the same for static methods

// use them
var foo = new Foo();
print(bar());
var log = Logger.getLogger("foo");
print(sin(42));

Я хочу избавиться от этих суррогатов, эмулируя подобную импорту функциональность для скриптов. Это означает, что я хочу иметь глобальные объекты (например, Foo, bar, Logger а также sin в приведенном выше примере), предварительно созданный моим кодом Java. Это должно автоматически сделать общий набор импорта доступным для нескольких сценариев.

Я нашел два способа сделать это с Nashorn:

Способ 1: создать сценарий прелюдии и eval() это перед основным скриптом. Это будет буквально первая половина примера кода выше.

Метод 2: Получить ссылки на классы и методы из ScriptEngine, кэшировать их и использовать для последующих вызовов скриптов:

ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine nashorn = sem.getEngineByName("nashorn");

Object fooClass = nashorn.eval("Java.type('my.package.Foo')"); // instance of jdk.internal.dynalink.beans.StaticClass
Object loggerClass = nashorn.eval("java.util.logging.Logger"); // the same

Object barFunction = nashorn.eval("Java.type('my.package.Foo').bar"); // instance of jdk.internal.dynalink.beans.SimpleDynamicMethod
Object sinFunction = nashorn.eval("java.lang.Math.sin"); // the same

ScriptEngine nashorn1 = sem.getEngineByName("nashorn");

nashorn1.put("Foo", fooClass);
nashorn1.put("bar", barFunction);
nashorn1.put("Logger", loggerClass);
nashorn1.put("sin", sinFunction);
nashorn1.eval("var foo = new Foo(); bar(); var log = Logger.getLogger('foo'); print(sin(42));");

Очевидно, что ни один из этих методов не будет работать для любого другого двигателя JSR-223. Есть ли способ реализовать то же самое портативным способом?

1 ответ

Я понятия не имею, как решить проблему, касающуюся новой операции (как избежать вызовов Java.type()).

Но когда вы компилируете свой скрипт, вы можете назначить методы как лямбды:

ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
Compilable nashorn = (Compilable) scriptEngineManager.getEngineByName( "Nashorn" );

CompiledScript script = nashorn.compile( "print( sin( 3.14 ) ); var log = getLogger( 'foo' );" );

Bindings bindings = new SimpleBindings();
bindings.put( "sin", (DoubleFunction<Double>) Math::sin );
bindings.put( "getLogger", (Function<String,Logger>) Logger::getLogger );

script.eval( bindings );

Для метода void bar() (без аргументов, без возвращаемого значения), вы должны предоставить свой собственный функциональный интерфейс, так как java.util.function Пакет не имеет такого метода.

К сожалению, этот подход не работает для не скомпилированных скриптов. И это не работает для всех типов лямбд (функциональных интерфейсов). Я провел несколько экспериментов, но пока не смог найти образец того, что работает, а что нет. Несомненно, что функциональные интерфейсы, которые определяют метод, генерирующий проверенное исключение, не работают таким образом. Также, если функциональный интерфейс не публичный, а внутренний.

Должно быть очевидно, что в приведенном выше примере кода я пропустил необходимые блоки обработки ошибок и try-catch.

Другие вопросы по тегам