Injected members from the host language to arrive to guest language as guest language types

泄露秘密 提交于 2020-01-02 17:50:38

问题


This question is somewhat related to: GraalVM - Using Polyglot Value without a context

In my application, the code snippets run in the guest languages should not need to know that the arguments injected (through members of the bindings) are Java arguments. Instead, for people developing in the guest language, arguments should look like just another argument of the guest language itself.

For example, I would like for an array injected from my Java host language to a JS guest script, in this fashion:

Value guestLanguageBindings = context.getBindings(scriptData.getLanguage().toString());

guestLanguageBindings.putMember(argumentName, argumentValue);

to "arrive" to the guest language as a JS array, not as a java.util.ArrayList as it is happening right now.

Currently I got around this problem by converting every non primitive type (I noticed that String, int, etc. arrive to JS as JS "types") to JSON and converting back in the guest language.

This works, but I wonder if there is a more appropriate way to do it or if indeed using the bindings is the right way to go?

Thanks!


回答1:


This works, but I wonder if there is a more appropriate way to do it or if indeed using the bindings is the right way to go?

As you have noticed when you put in Java objects into a polyglot language they will look like Java objects and not like JavaScript objects to the user. In order to make them mimic guest language objects you may use the Polyglot Proxy API.

Example for JS objects (backed by 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());
}

Example for JS arrays (backed by Java array):

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

Example for functions (backed by 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();
}

You may also implement ProxyObject and ProxyArray directly to customize the behavior, e.g. if you want to provide a read-only object or array.

Here is another Proxy Example: http://www.graalvm.org/docs/graalvm-as-a-platform/embed/#computed-arrays-using-polyglot-proxies

Proxy Javadoc: http://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/proxy/package-summary.html




回答2:


The answer provided by @Christian Humer suggesting to use ProxyArray is a good one, and is explanation on how to put together a ProxyArray is correct and well presented.

However, my requirement was to be able to present the array argument (set using the top level bindings) as if it was a guest language data type. ProxyArray only get me halfway there, with the resulting data type being a Java type and not for example a JS type.

In order to fully achieve the above, since I am in control of the Java host side, I have created up-front a JS array using the Context, on the host side, and copied the content of the Java ArrayList in it. When calling the guest code, I simply set the JS array in the bindings, which is already a fully featured JS array.

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

This approach was suggested to me on Github when I filed the bug report mentioned in the comments above.



来源:https://stackoverflow.com/questions/51707039/injected-members-from-the-host-language-to-arrive-to-guest-language-as-guest-lan

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!