在GraalVM CE上运行。
openjdk version "11.0.5" 2019-10-15
OpenJDK Runtime Environment (build 11.0.5+10-jvmci-19.3-b05-LTS)
OpenJDK 64-Bit GraalVM CE 19.3.0 (build 11.0.5+10-jvmci-19.3-b05-LTS, mixed mode, sharing)
案例1:
import org.graalvm.polyglot.Context;
public class Test {
static class Data {
public String name = "HelloWorld";
public String getName() {
return this.name;
}
}
public static void main(String[] args) {
Context context = Context.newBuilder("js").allowHostAccess(true).build();
context.getBindings("js").putMember("d", new Data());
context.eval("js", "var x = d.name");
System.out.println(
context.getBindings("js").getMember("x").asString()
);
}
}
结果:
null
为什么?
据我了解,d
正确通过了:
((Data) context.getBindings("js").getMember("d").as(Data.class)).name
返回"HelloWorld"
。
案例2:
context.eval("js", "d.getName()");
例外
Exception in thread "main" TypeError: invokeMember (getName)
on JavaObject[task.Test$Data@35a3d49f (task.Test$Data)] failed due to:
Unknown identifier: getName
但是getName
是公开的...
怎么了?
答案 0 :(得分:1)
当您使用上下文并向其添加Java对象时,在幕后,TruffleApi中的IntropLibrary将创建一个HostObject并将其与该对象关联。这意味着您不使用对象本身,而是使用包装对象。
调用getMember()方法时,IntropLibrary仅可以访问公共可用的托管对象的字段和方法。由于您的内部类具有默认访问权限(无访问修饰符),因此即使它们是公共的,API也无法找到其成员。 (一个班级的成员不能拥有比其班级本身更广泛的访问权限。)
要解决此问题,您要做的就是将内部类设为公开
import org.graalvm.polyglot.Context;
public class Test {
public static class Data {
public String name = "HelloWorld";
public String getName() {
return this.name;
}
}
public static void main(String[] args) {
Context context = Context.newBuilder("js").allowHostAccess(true).build();
context.getBindings("js").putMember("d", new Data());
context.eval("js", "var x = d.name;");
System.out.println(
context.getBindings("js").getMember("x").asString()
);
}
}
答案 1 :(得分:0)
GraalVM JavaScript默认情况下会强制执行严格的沙箱规则,其中之一是,除非用户明确允许,否则JavaScript代码无法访问主机Java对象。允许您的代码访问 context.eval(“ js”,“ d.getName()”)的最简单方法是传递选项 polyglot.js.allowAllAccess = true >如以下链接所述:
GraalVM JavaScript ScriptEngine implementation
看看示例:
import org.graalvm.polyglot.Context;
public class Test {
static class Data {
public String name = "HelloWorld";
public String getName() {
return this.name;
}
}
public static void main(String[] args) {
Context context = Context.newBuilder("js").allowHostAccess(true).build();
context.getBindings("js").putMember("d", new Data());
context.eval("js", "var x = d.getName()");
System.out.println(
context.getBindings("js").getMember("d").as(Data.class)).name
);
}
}
答案 2 :(得分:0)
您需要使用@HostAccess.Export注释类字段和方法
默认情况下,只有使用@ HostAccess.Export注释的公共类,方法和字段可以用来宾语言访问。构造上下文时,可以使用Context.Builder.allowHostAccess(HostAccess)自定义此策略。
使用JavaScript中的Java对象的示例:
public class JavaRecord { @HostAccess.Export public int x; @HostAccess.Export public String name() { return "foo"; } }
或者,您可以使用GraalVM JSR-223 ScriptEngine
GraalVM JavaScript提供了JSR-223兼容的javax.script.ScriptEngine实现。请注意,此功能是出于遗留原因而提供的,以便可以更轻松地迁移当前基于ScriptEngine的实现。我们强烈建议用户使用org.graalvm.polyglot.Context接口
要通过绑定设置选项,请在初始化引擎的脚本上下文之前使用Bindings.put(,true)。请注意,即使对Bindings#get(String)的调用也可能导致上下文初始化。以下代码显示了如何通过绑定启用polyglot.js.allowHostAccess:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); bindings.put("polyglot.js.allowHostAccess", true); bindings.put("polyglot.js.allowHostClassLookup", (Predicate<String>) s -> true); bindings.put("javaObj", new Object()); engine.eval("(javaObj instanceof Java.type('java.lang.Object'));"); // would not work
没有allowHostAccess和allowHostClassLookup 如果用户致电例如在调用bindings.put(“ polyglot.js.allowHostAccess”,true);之前,使用engine.eval(“ var x = 1;”),因为对eval的任何调用都会强制上下文初始化。
答案 3 :(得分:0)
要以通常的 js 方式完全访问 java 对象,您可以使用 sj4js 库。
此示例取自文档...
site:yoursite.com
并且您可以像访问 java 对象一样访问此对象。
public class TestObject extends JsProxyObject {
// the property of the object
private String name = "";
// the constructor with the property
public TestObject (String name) {
super ();
this.name = name;
// we hvae to initialize the proxy object
// with all properties of this object
init(this);
}
// this is a mandatory override,
// the proxy object needs this method
// to generate new objects if necessary
@Override
protected Object newInstance() {
return new TestClass(this.name);
}
// the setter for the property name
public void setName (String s) {
this.name = s;
}
// the getter for the property name
public String getName () {
return this.name;
}
}