GraalJS: Cannot bind to proxy object
The following JS code works in Google Chrome:
console.log("########### Direct call in JS #############")
console.log("window.document.createElement:", window.document.createElement("p"));
console.log("document.createElement.apply:", window.document.createElement.apply(window.document, ["p"]));
console.log("########################")
console.log("\n")
console.log("########## Call via Bind/Call ##############")
var boundCall = Object.bind.apply(Object.call, [Object.bind, Object.call]);
var boundCall2 = boundCall.apply(window, [Object.bind]);
var createElementMethod = boundCall2.apply(window, [document.createElement, window.document]);
console.log(createElementMethod("p"));
console.log(createElementMethod.apply(window.document, ["p"]));
console.log("########################")
And produces

But when I use GraaJS v20.2.0 with proxy Document objec: https://github.com/REASY/graalvm-js-bind-issue/blob/8402fbb326125db62414a4348ed63cbc70fee6f6/src/main/java/com/examples/graalvm/blink/Document.java#L9t to execute that code https://github.com/REASY/graalvm-js-bind-issue/blob/main/src/main/scala/com/examples/graalvm/BindExample.scala#L20-L23, I get the exception org.graalvm.polyglot.PolyglotException: TypeError: com.examples.graalvm.blink.Document$1@103441bb is not a function:
########### Direct call in JS #############
hasMember: createElement
hasMember: createElement
getMember: createElement
execute(). Received [p]
window.document.createElement: bar
hasMember: createElement
getMember: createElement
execute(). Received [p]
document.createElement.apply: bar
########################
########## Call via Bind/Call ##############
hasMember: createElement
getMember: createElement
[error] (run-main-0) org.graalvm.polyglot.PolyglotException: TypeError: com.examples.graalvm.blink.Document$1@103441bb is not a function
[error] org.graalvm.polyglot.PolyglotException: TypeError: com.examples.graalvm.blink.Document$1@103441bb is not a function
[error] at <js>.:program(bind_example.js:9)
[error] at org.graalvm.polyglot.Context.eval(Context.java:345)
[error] at com.examples.graalvm.BindExample$.execute(BindExample.scala:23)
[error] at com.examples.graalvm.BindExample$.main(BindExample.scala:29)
[error] at com.examples.graalvm.BindExample.main(BindExample.scala)
[error] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[error] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[error] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[error] at java.lang.reflect.Method.invoke(Method.java:498)
[error] stack trace is suppressed; run last Compile / bgRunMain for the full output
[error] Nonzero exit code: 1
[error] (Compile / runMain) Nonzero exit code: 1
[error] Total time: 1 s, completed Oct 6, 2020 12:13:39 AM

- allowAllAccess = true
- allowExperimentalOptions = true
- js.experimental-foreign-object-prototype = true
GraalVM version: 20.2.0 JVM: Oracle JDK 1.8.0_251 Repo: https://github.com/REASY/graalvm-js-bind-issue
Hi @REASY
thanks for your report. I can confirm this is not working at the moment.
~~The problem on your side should be easy to fix. You are using a ProxyObject in https://github.com/REASY/graalvm-js-bind-issue/blob/main/src/main/java/com/examples/graalvm/blink/Document.java and from the error message above I take that's what you try to bind. This will fail just like binding a plain JavaScript object would fail: you have to bind something that represents a function. Solution is simple here: just implement ProxyExecutable additionally.~~
(edit: just saw you actually return a ProxyExecutable from your proxy).
This, however, does not suffice to bind the proxy, as we have not implemented this case currently. The current interop protocol does not allow to specify the this when calling, so we cannot implement bind with correct semantics at the moment. We could just call the function and ignore the bound this, but that could be very surprising to the users.
We are currently investigating our options.
Best, Christian
Hi, @wirthi
Thanks for the reply. Yes, for now I'm wrapping the code by JS function and it works, something like this:
val jsWindow = ctx.eval("js", "window;")
// Document has method `createElement0`
jsWindow.putMember("document", new Document)
// Wrap
ctx.eval(Source.newBuilder("js", "document.createElement=function(){ return document.createElement0(arguments); };",
"init.js").build)
Hi @REASY
the workaround seems fine. But why did you try to bind it then in the first place? The effect of binding is to change the this, and your workaround is fine without that, so why bind in the first place? Was that just convenience to a function outside the scope of document?
Thanks, Christian
@wirthi it is some existing JS code, I just experienced that behavior in case of GraalJS