Внедрение членов с языка хоста для прибытия на гостевой язык в качестве гостевого языка
Этот вопрос несколько связан с: GraalVM - Использование значения Polyglot без контекста
В моем приложении фрагменты кода, выполняемые на гостевых языках, не должны знать, что введенные аргументы (через элементы привязок) являются аргументами Java. Вместо этого для людей, разрабатывающих на гостевом языке, аргументы должны выглядеть как еще один аргумент самого гостевого языка.
Например, я хотел бы, чтобы массив, внедренный из моего языка хоста Java в гостевой скрипт JS, таким образом:
Value guestLanguageBindings = context.getBindings(scriptData.getLanguage().toString());
guestLanguageBindings.putMember(argumentName, argumentValue);
"прибыть" на гостевой язык в виде массива JS, а не в виде java.util.ArrayList, как это происходит сейчас.
В настоящее время я справился с этой проблемой, преобразовав каждый не примитивный тип (я заметил, что String, int и т. Д. Поступают в JS как "типы" JS) в JSON и преобразовал обратно в гостевой язык.
Это работает, но мне интересно, есть ли более подходящий способ сделать это, или действительно ли использование привязок - правильный путь?
Спасибо!
2 ответа
Ответ, предоставленный @Christian Humer, предлагающим использовать ProxyArray, является хорошим и объясняет, как собрать ProxyArray, правильно и хорошо представлен.
Однако мое требование состояло в том, чтобы иметь возможность представить аргумент массива (установленный с использованием привязок верхнего уровня), как если бы это был тип данных гостевого языка. ProxyArray помогает мне только на полпути, и результирующий тип данных является Java-типом, а не, например, JS-типом.
Чтобы полностью выполнить вышесказанное, поскольку я управляю стороной хоста Java, я заранее создал массив JS, используя Context, на стороне хоста, и скопировал в него содержимое Java ArrayList. При вызове гостевого кода я просто устанавливаю массив JS в привязках, который уже является полнофункциональным массивом JS.
Value jsArray = context.eval("js", "new Array();");
jsArray.setArrayElement(0, 1001); //array will grow automatically, JS semantics
jsArray.setArrayElement(1, 1002);
jsArray.setArrayElement(2, 1003);
guestLanguageBindings.putMember("variable", jsArray);
context.eval("js", "print(variable);");
Этот подход был предложен мне на Github, когда я подал отчет об ошибке, упомянутый в комментариях выше.
Это работает, но мне интересно, есть ли более подходящий способ сделать это, или действительно ли использование привязок - правильный путь?
Как вы заметили, когда вы помещаете объекты Java в язык полиглота, они будут выглядеть как объекты Java, а не как объекты JavaScript для пользователя. Чтобы имитировать объекты гостевого языка, вы можете использовать Polyglot Proxy API.
Пример для объектов JS (поддержанный HashMap):
try (Context context = Context.create("js")) {
Map<String, Object> backingMap = new HashMap<>();
backingMap.put("foo", "bar");
context.getBindings("js").putMember("hostObject", ProxyObject.fromMap(backingMap));
assert "bar".equals(context.eval("js", "hostObject.foo").asString());
backingMap.put("foo", "baz");
assert "baz".equals(context.eval("js", "hostObject.foo").asString());
}
Пример для массивов JS (поддерживается массивом Java):
try (Context context = Context.create("js")) {
Object[] backingArray = new Object[42];
backingArray[0] = 42;
context.getBindings("js").putMember("hostObject", ProxyArray.fromArray(backingArray));
assert 42 == context.eval("js", "hostObject[0]").asInt();
backingArray[0] = 43;
assert 43 == context.eval("js", "hostObject[0]").asInt();
}
Пример для функций (при поддержке Lambda):
try (Context context = Context.create("js")) {
ProxyExecutable executable = (arguments) -> arguments[0];
context.getBindings("js").putMember("hostObject",executable);
assert 42 == context.eval("js", "hostObject(42)").asInt();
assert 43 == context.eval("js", "hostObject(43)").asInt();
}
Вы также можете напрямую реализовать ProxyObject и ProxyArray для настройки поведения, например, если вы хотите предоставить объект или массив только для чтения.
Вот еще один пример прокси: http://www.graalvm.org/docs/graalvm-as-a-platform/embed/
Прокси Javadoc: http://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/proxy/package-summary.html