将全局函数/变量添加到GraalVM JavaScript引擎

3
如何在GraalVM的JavaScript“this”对象中添加自定义全局函数,而不使用Object.bindProperties()方法的Nashorn兼容模式(参见此处:Defining a default/global Java object to Nashorn script engine?)?我找不到使用Context方法的方法,并且Context对象的反射不显示我可以使用的类似bindProperties()的方法。
示例:
我有这些方法:
class GlobalMethods
{
public void a() {...}
public void b() {...}
public void c() {...}
}

我希望a()、b()和c()可以在JavaScript中直接调用而无需作用域/限定符:
context.eval("js", "a()");

感谢提前阅读!
2个回答

1

如果方法没有被重载,使用global.js的Eric的答案是有效的。此外,Context对象的完整设置如下,并包含一些额外必需的选项:

context = Context.newBuilder("js")
          .allowHostAccess(true)
          .allowExperimentalOptions(true)
          .allowAllAccess(true)
          .allowIO(true)                                            // required
          .option("js.nashorn-compat", "false")                     // Must be false
          .option("js.commonjs-require", "true")                    // required
          .option("js.commonjs-require-cwd", "/path/to/globals.js") // required
          .option("js.commonjs-global-properties", "./globals.js")
          .build();

1

对于JS函数,您可以定义一个文件,例如globals.js,在其中初始化全局变量并将其添加到JavaScript全局对象中:

globalThis.someFunction = function() {...}

并设置选项js.commonjs-global-properties,以在上下文创建时加载此文件:

Context context = Context.newBuilder("js")
                         .option("js.commonjs-global-properties", "./globals.js")
                         .build();

为了将Java选项绑定到JS,您需要通过Bindings启用polyglot.js.allowHostAccess

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

或者使用上下文:
Context context = Context.newBuilder("js")
                         .allowHostAccess(true)
                         .build();

context.getBindings("js").putMember("a", GlobalMethods.a);

或者您可以在类定义中使用@HostAccess.Export装饰器:

  @HostAccess.Export
  public void a() {...}

尽管我不确定您是否可以在没有至少一次引用类名的情况下访问静态方法GlobalMethods.a(),但直接传递函数输出可能已足以满足您的需求,或者传递类定义,然后使用evalglobalThis.a = GlobalMethods.a(未经测试)。


谢谢,但我想要全局公开的方法是Java方法,而不是JavaScript。有没有一种方法可以在不使用globals.js的情况下实现这一点? - Lester
抱歉如果我误解了..(执行 context.eval("js", "a()"); 意味着评估名为 a() 的 JavaScript 函数,我没有看到 public void 关键字).. 你仍然可以创建一些绑定,但不确定它是否允许你在 JavaScript 中调用 get a()、b() 和 c() 而无需范围/限定符。 - EricLavault
嗨Eric,我尝试了你的global.js方法,虽然它确实有效(我通过在global.js中定义的JS方法调用Java方法),但它不支持重载(例如globalThis.a不能为GlobalMethods.a()的每个重载版本有多个定义),所以对我来说行不通。我很想使用Graal的新Context API,但似乎我将被困在Nashorn兼容模式中。 - Lester

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接