0

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

Greg
  • 10,696
  • 22
  • 68
  • 98

3 Answers3

3

Using ProxyObject.fromMap helps us pass the map from Java to the graal javascript. Refer here for more details.

A quick working example.

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();
        }
    }
}
Vikram
  • 170
  • 1
  • 12
1

You can use the sj4js library. It supports types such a Java HashMaps and 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
    
} 
0

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.

Greg
  • 10,696
  • 22
  • 68
  • 98