Search code examples
graalvmgraaljs

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


I have a Map in Scala/Java I want to be visible in Javascript running on graal.js engine.

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

Later, a Javascript function in graal.js is called with:

inv.invokeFunction(bindFn, args: _*) 

Where bindFn is a compiled function (below) and args is a 1-element list containing my Thing object.

Javascript:

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

The output from thing.foo() worked, but all the others resolved to 'foreign {}' in Javascript. None of them have any values in the Map.

How can I get Map data created on the JVM visible in graal.js Javascript code (preferably natively to Javascript)?


Solution

  • Ok, this actually seems to be a Scala vs Java thing. In Scala I can successfully pass a Map if its contents are converted to graal Value. No issues. I can successfully pass a Scala object (case or non-case) and call methods on that object, BUT... I cannot access data members of the object, even if they're all Value!

    If I create a pure Java class I can access both the data members and methods of that class in graal.

    Not sure why this is... a non-case Scala class should basically be the same as a Java class, but clearly there is some difference important to graal.

    Fortunately I think I can live with the difference for now.