'GraalVM how to return an array from a method to the guest language?

I'm playing around with GraalVM (Truffle) in OpenJDK 17, and would like to know what the correct way is to return values to the guest language from method calls? Right now I'm struggling with passing a String[] array back.

Exmaple:

Java (host)

class Services
{
    Value message;

    @HostAccess.Export
    public String[] getArrayString()
    {
        return new String[]{"s1", "s2", "s3"};
    }

    @HostAccess.Export
    public void setMessage( Value v )
    {
        message = v;
        message.pin();
    }
}

...

String jsScript = " ... " // see below
try ( Context context = Context.newBuilder().allowHostAccess(HostAccess.SCOPED).build() )
{
    Services s = new Services();
    context.getBindings("js").putMember("services", s);
    context.eval("js", jsScript);
}

JavaScript (guest)

var a = services.getArrayString();
b = '';
for ( var c in a ) b += c;
services.setMessage('' + a + ' // ' + b)

The final message value is "[object Object] // " (b is blank), however I expected something like "[object Object] // s1s2s3".

I've also tried the return types Object[] and ArrayList<String>. I'm not sure why I can't access the elements of the array, either I'm not passing the array back correctly, or I'm not accessing it correctly within the JavaScript script. The examples I've found in the GraalVM docs are always about passing values directly from the host to the guest, but I'd like to do it via a method call - how is that done?



Solution 1:[1]

I've finally found a way to pass an array from the host to the guest - using ProxyArray.

https://www.graalvm.org/22.1/reference-manual/embed-languages/#computed-arrays-using-polyglot-proxies

Reading through the Graalvm documentation and experimenting with the very primative examples they provide, I came up with the following changes to my Services class:

    ArrayList<String> array = new ArrayList<String>();

    @HostAccess.Export
    public void prepareList()
    {
        array.add("s1");
        array.add("s2");
        array.add("s3");
    }

    @HostAccess.Export
    public ProxyArray getArray()
    {
        return new ProxyArray() {

            @Override
            public Object get( long index )
            {
                return array.get((int)index);
            }

            @Override
            public void set( long index, Value value )
            {
                // TODO Auto-generated method stub
            }

            @Override
            public long getSize()
            {
                return array.size();
            }
        };
    }

And finally the guest call looks like this:

services.prepareList();
var a = services.getArray();
b = '';
for ( i = 0; i < a.length; i++ ) b += a[i];
services.setMessage('' + a + ' // ' + b)

And the value of the message is then as expected "[object Object] // s1s2s3"

Hope someone finds this useful ... and if you have a better way of returning objects (Array, Hashtable, whatever) post your answers here - thanks.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1