graaljs: Cannot assign object data in Java script code. TypeError: writeArrayElement on foreign object failed due to: Message not supported.

Hello

In GraalVM CE 19.2.0, I have jsonObject build in Java using ProxyObject and passed to GraalVM Context. Then I want to update in Java script guest code and got an exception.

...
        //Java on host
        try (Context jsContext = 
                Context.newBuilder("js").allowHostAccess(HostAccess.EXPLICIT).build()) {
                jsContext.getBindings("js").putMember("jsonObject", initDataProxy);
                jsContext.eval("js", jsCode);
            }
        }
...
        //Java script on guest
        for (var key in jsonObject) {
          jsonObject[key] = {'a':'b'};
        }
...

Here exception message:

Exception in thread "main" TypeError: writeArrayElement on foreign object failed due to: Message not supported.
	at <js> :program(Unnamed:1:1557-1583)
	at org.graalvm.polyglot.Context.eval(Context.java:370)
	at com.researchforgood.survey.surveyengine.service.JSProxyBuilderTest.performanceTest(JSProxyBuilderTest.java:174)
	at com.researchforgood.survey.surveyengine.service.JSProxyBuilderTest.main(JSProxyBuilderTest.java:265)

As you see I cannot assign object data in JS code. Am I doing something wrong and this is documented somehow? How can I assign data?

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 17 (5 by maintainers)

Commits related to this issue

Most upvoted comments

Hi @pavlomorozov

I can reproduce your problem. We are investigating.

Best, Christian

The root of the problem is that JavaScript (unlike the interop API) does not distinguish numeric and string properties. object[42] and object['42'] are the same in JavaScript but there are two methods in the interop API (writeMember() and writeArrayElement()) that can be mapped to this JavaScript operation.

Our current rule/heuristics (described by @woess above): “if the key is a string, we send writeMember() and if it’s a number, we send writeArrayElement()” does not work well when the key is (or can be converted to) a number but the corresponding object does not have array elements (like a typical ProxyObject). I think that we should modify this rule such that writeArrayElement() is called on an object that actually hasArrayElements() only. Otherwise the key should be converted to string and writeMember() should be used instead. This change would ensure that proxyArray[42] would trigger writeArrayElement() still but both proxyObject[42] and proxyObject['42'] would work as expected.