如何将 Map 从 Java 传递到 graal.js?

How can I pass a Map from Java to graal.js?

我在 Scala/Java 中有一个地图,我希望在 graal.js 引擎上的 Javascript 运行 中可见。

case class Thing() {
  def foo() { println("FOO!") }  // just to see if this is callable from js (it is)
  val m = Map("foo" -> "bar", "one" -> 1)
  val d1 = m
  val d2 = m.asJava
  val d3 = toGraalValue(m)
  val d4 = MapProxyObject(m.map { case (k, v) => (k.toString, toGraalValue(v)) })

  def toGraalValue(a: Any): Value =
    a match {
      case s: List[_]   => Value.asValue(ListProxyArray(s.map(toGraalValue).toArray))
      case m: Map[_, _] => Value.asValue(MapProxyObject(m.map { case (k, v) => (k.toString, toGraalValue(v)) }))
      case _            => Value.asValue(a)
    }
}

稍后,graal.js 中的一个 Javascript 函数被调用:

inv.invokeFunction(bindFn, args: _*) 

其中 bindFn 是编译函数(如下),args 是包含我的 Thing 对象的 1 元素列表。

Javascript:

function(thing) {
  console.log(thing.d1);
  console.log(thing.d2);
  console.log(thing.d3);
  console.log(thing.d4);
  console.log(thing.foo());
}

thing.foo() 的输出有效,但所有其他结果在 Javascript 中解析为 'foreign {}'。 None 个在地图中有任何值。

如何让在 JVM 上创建的地图数据在 graal.js Javascript 代码中可见(最好是 Javascript 本身)?

好的,这实际上似乎是 Scala 与 Java 的对比。在 Scala 中,如果 Map 的内容被转换为 graal Value,我可以成功传递它。没有问题。我可以成功地传递一个 Scala 对象(case 或 non-case)并调用该对象的方法,但是......我 不能 访问对象的数据成员,即使他们'物超所值!

如果我创建一个纯 Java class,我可以在 graal 中访问 class 的数据成员和方法。

不确定为什么会这样...non-case Scala class 基本上应该与 Java class 相同,但显然存在一些重要的区别到格拉尔。

幸运的是,我想我现在可以忍受这种差异。

您可以使用 sj4js 库。它支持 Java HashMaps 和 Arrays 这样的类型。

// create a JS hash map
JsHashMap hm = new JsHashMap();
hm.putMember("a", "A");
hm.putMember("b", "B");
hm.putMember("c", "C");

try (JScriptEngine engine = new JScriptEngine()) {

    // add the hm to js
    engine.addObject("test", hm);
    
    // evaluate Js code
    engine.exec("test['b']");
    // B
    
    engine.exec("test.b");
    // B
    
    engine.exec("for (a in test) { console.log(test[a]); }");
    // a, b, c
    
} 

使用 ProxyObject.fromMap 帮助我们将映射从 Java 传递到 graal javascript。请参阅 here 了解更多详情。

一个快速的工作示例。

import java.util.HashMap;
import java.util.Map;

import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyObject;

public class TransformJson {

    public TransformJson() {
        // TODO Auto-generated constructor stub
    }

    static String SOURCE = "function transformJson(prmPayload) {"
            + " console.log('payload = ' + JSON.stringify(prmPayload));" + " console.log('from javascript'); "
            + " return {" + " 'emp' : {" + "   'id' : prmPayload.emp_id" + "   ,'name' : {"
            + "      first : prmPayload.emp_first_name" + "      ,last: prmPayload.emp_last_name" + "}" + "} " + "};}";

    public static void main(String[] args) {
        try (Context context = Context.create()) {
            context.eval(Source.newBuilder("js", SOURCE, "src.js").build());
            Value transformJson = context.getBindings("js").getMember("transformJson");
            Map payload = new HashMap();
            payload.put("emp_id", 1);
            payload.put("emp_first_name", "John");
            payload.put("emp_last_name", "Chambers");
            payload.put("emp_dept_id", 25);
            payload.put("emp_dept_name", "Technology");
            Value returnVal = transformJson.execute(ProxyObject.fromMap(payload));
            System.out.println(returnVal);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}