Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sharing / Re-using values across Contexts #631

Closed
nhoughto opened this issue Aug 22, 2018 · 48 comments
Closed

Sharing / Re-using values across Contexts #631

nhoughto opened this issue Aug 22, 2018 · 48 comments
Assignees
Labels
feature javascript tracking This issue has an analogue in an internal issue tracker truffle

Comments

@nhoughto
Copy link

I'm testing the JS language on GraalVM 1.0.0rc5 and getting an exception when trying to access a JS object created in context A but accessed in context B.

org.graalvm.polyglot.PolyglotException: java.lang.IllegalArgumentException: Values cannot be passed from one context to another. The current value originates from context 0x7ba8c737 and the argument originates from context 0x74294adb.
	at com.oracle.truffle.api.vm.PolyglotLanguageContext.toGuestValue(PolyglotLanguageContext.java:566)
	at com.oracle.truffle.api.vm.PolyglotLanguageContext.toGuestValue(PolyglotLanguageContext.java:677)
	at org.graalvm.polyglot.Value.execute(Value.java:312)

My scenario is an object that is expensive to compute is created in context A and would like to be re-used in context B later (and C and D etc), this doesn't appear to be supported? Is there another way to achieve this outcome? It is effectively a long lived JS object, being injected into many shortlived contexts. This is achievable in Nashorn.

The reason for recreating new contexts is to clear any eval'd code between executions, but since the shared object is expensive to compute, it can't be recreated everytime, i'd like to share it. A way to 'clear' an existing context rather than re-created many short-lived contexts would also work..

Simple test code:

       Value obj = null;
        try (Context context = Context.newBuilder("js").build()) {
            obj = context.eval("js", "{a: 1}"); //could be expensive
        }

        try (Context context = Context.newBuilder("js").build()) {

            context.getBindings("js").putMember("jsobj", obj);
            context.eval("js", "JSON.stringify(jsobj)");
        }
@chumer
Copy link
Member

chumer commented Sep 4, 2018

You are hitting two limitations here:

  1. When you close the original context all of its values become invalid.
  2. You currently cannot pass objects from one context to the other, even if the contexts are still active (=not closed).

The only currently possible workaround for both problems is to serialize it into a json string and deserialize it in every new context. But I see that this is not what you want.

We can support 2, but 1 really is by design. If a context is closed some if its environment may be no longer available/allocated, therefore if you call a method on that object it will lead to undefined behavior.

Could you reuse the object like this by keeping the first context alive?

     Value obj = null;
        try (Context context1 = Context.newBuilder("js").build()) {
            obj = context1.eval("js", "{a: 1}"); //could be expensive
            for (...many contexts...) {
                try (Context context2 = Context.newBuilder("js").build()) {
                      context2.getBindings("js").putMember("jsobj", obj);
                      context2.eval("js", "JSON.stringify(jsobj)");
                }
            }
        }

Please note that even if we support 2 the value will only be accessible from one thread at a time. If you try to access that shared JS object from multiple threads it will fail. JS objects can only be used by one thread at a time. Nashorn tended to ignore that fact. Would you need to share that object for multiple contexts on multiple threads?

@nhoughto
Copy link
Author

nhoughto commented Sep 4, 2018

Yeah so a single thread is a reasonable restriction we can work with, especially with JS that naturally lends itself to single threaded execution. My understanding of Nashorn was that it allowed multi threaded access but gave no guarantees about behaviour, and that often meant you got strange non-deterministic behaviour.

Your example would be exactly how we would do it if you did support 2, the expensive object is calculated in its own long running context, and passed into the many later contexts.

Is supporting 2 on the roadmap?

@chumer
Copy link
Member

chumer commented Sep 4, 2018

Yes. 2 is on the roadmap. Cannot quite say yet when we get to it. Do you have a way to workaround it or is it a blocker for you?

Please note that accesses to the shared object of outer context in the inner context will be a bit slower than accesses to normal JS objects, as we need to do a thread-check and change the active context each time the shared object is accessed.

@nhoughto
Copy link
Author

nhoughto commented Sep 4, 2018

We are looking to migrate off Nashorn onto GraalJS, being able to share objects across contexts as described is a showstopper for us, but Nashorn still works (however unloved it is with all its problems).

We would love to get onto Graal soon tho, lots of great stuff on offer =)

@nhoughto
Copy link
Author

Is this fixed in rc9 ? I see in the changelog this:

Primitives, host and Proxy values can now be shared between multiple context and engine instances. They no longer throw an IllegalArgumentException when shared.

@nhoughto
Copy link
Author

nhoughto commented Dec 3, 2018

To answer my own question, yes this is now fixed in rc9. Re-testing with the same repro code above now passes as expected. 👍

@nhoughto nhoughto closed this as completed Dec 3, 2018
@nhoughto
Copy link
Author

nhoughto commented Dec 3, 2018

Nope, totes wrong, just my initial test is slightly wrong, needs to use getMember() instead of the return value, this proper test causes the failure still 😢

Value obj = null;
        try (Context context = Context.newBuilder("js").build()) {
            context.eval("js", "var jsobj = {a: 1}"); //could be expensive
            obj = context.getBindings("js").getMember("jsobj");
        }

        try (Context context = Context.newBuilder("js").build()) {

            context.getBindings("js").putMember("jsobj", obj);
            System.out.println(context.eval("js", "JSON.stringify(jsobj)"));
        }

stack:

Exception in thread "main" java.lang.IllegalArgumentException: The value 'DynamicObject<JSUserObject>@351d00c0' cannot be passed from one context to another. The current context is 0x409bf450 and the argument value originates from context 0x3b22cdd0.
	at com.oracle.truffle.polyglot.PolyglotLanguageContext.migrateValue(PolyglotLanguageContext.java:695)
	at com.oracle.truffle.polyglot.PolyglotLanguageContext.toGuestValue(PolyglotLanguageContext.java:653)
	at com.oracle.truffle.polyglot.PolyglotLanguageContext$ToGuestValueNode.apply(PolyglotLanguageContext.java:542)
	at com.oracle.truffle.polyglot.PolyglotValue$InteropCodeCache$PutMemberNode.executeImpl(PolyglotValue.java:1092)
	at com.oracle.truffle.polyglot.HostRootNode.execute(HostRootNode.java:94)
	at org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.callProxy(OptimizedCallTarget.java:289)
	at org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.callRoot(OptimizedCallTarget.java:278)
	at org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.callBoundary(OptimizedCallTarget.java:265)
	at org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.doInvoke(OptimizedCallTarget.java:247)
	at org.graalvm.compiler.truffle.runtime.GraalTVMCI.callProfiled(GraalTVMCI.java:86)
	at com.oracle.truffle.api.impl.Accessor.callProfiled(Accessor.java:725)
	at com.oracle.truffle.polyglot.VMAccessor.callProfiled(VMAccessor.java:91)
	at com.oracle.truffle.polyglot.PolyglotValue$InteropValue.putMember(PolyglotValue.java:2466)
	at org.graalvm.polyglot.Value.putMember(Value.java:286)
	at Main.main(Main.java:17)
Caused by: Attached Guest Language Frames (1)

@nhoughto nhoughto reopened this Dec 3, 2018
@nhoughto
Copy link
Author

nhoughto commented Dec 3, 2018

Ah so the behaviour described in the recent release notes indicate that just primitive, host and proxy Value objects can be shared. This is somewhat disingenuous as host and proxy objects can be shared but not changed. In my example with a expensive computed object, I want to compute and then change the object through execution in many contexts.

@ArthurStocker
Copy link

Hi,

I just checked the advanced example Mapping to Java methods and fields at GraalVM SDK Java API Reference (org.graalvm.polyglot.Context).

I end up with not being able to access the properties of the JavaRecord object in js. They all are undefined. Did I miss something in the code here ?

import org.graalvm.polyglot.*;

class JavaRecord {
    public int x = 42;
    public double y = 42.0;
    public String name() {
        return "foo";
    }
}


class CommandLineExample
{
    public static void main ( String [] arguments )
    {
        Context context = Context.create();
        Value record = context.asValue(new JavaRecord());
        
        context.eval("js", "(function(record) {console.log(record); console.log(record.x); console.log(record.y); console.log(record.name()); return record.x;})")
                    .execute(record);// .asInt() == 42;
    }
}

@wirthi
Copy link
Member

wirthi commented Feb 26, 2019

Hi @ArthurStocker

sorry for the delayed response.

You cannot access the fields of the JavaRecord class because it is not public. Make that class public, and your code works.

import org.graalvm.polyglot.*;

class CommandLineExample
{
    public static void main ( String [] arguments )
    {
        Context context = Context.create();
        Value record = context.asValue(new JavaRecord());
        
        context.eval("js", "(function(record) {console.log(record); print(record.x); console.log(record.x); console.log(record.y); print('keys: '+Object.keys(record)); console.log(record.name()); return record.x;})")
                    .execute(new JavaRecord());// .asInt() == 42;
    }

    public static class JavaRecord {
        public int x = 42;
        public double y = 42.0;
        public String name() {
            return "foo";
        }
    }
}

Best,
Christian

@ArthurStocker
Copy link

Hi @wirthi
got it. Sorry, should have been able to find it myself.
re Arthur

@thomasreinecke
Copy link

thomasreinecke commented Jun 22, 2019

gents, I'm having the same problem like originally describes as 2), however my scenario is a little more complex. Briefly: I'd like to load a javascript library in context A, pass it into context B as a member and use it there as an "injected" js library. I'm trying to load "whatever" javascript library in my own js code that get embedded into java:

myLib.js - simple js library, however this could be any lib from a CDN or locally held

(function () {
	'use strict';
	function MyLib() {
		var factory = {};
		factory.calcPlus10 = function(inputValue) {
			return inputValue + 10;
		}
		return factory;
	}
})

myGraalVmJsLib.js - my actual js code that gets embedded into Java and thats supposed to use myLib library

({
  functionUseMyLib: function() {
	console.log(myLib);
  }
})

GraalVmTest - java snippet thats trying to embed myGraalVmJsLib.js

public class GraalVmTest {
    public static void main(String[] args) throws IOException, URISyntaxException {
    	Context contextLib = Context.create(); 
	URL resource = Thread.currentThread().getContextClassLoader().getResource("./myLib.js");
	Value jsLib = contextLib.eval("js", new String(Files.readAllBytes(Paths.get(resource.toURI())))); 
	System.out.println("canExecute: "+jsLib.canExecute());
		
    	Context contextEmbed = Context.create(); 
        URL myResource = Thread.currentThread().getContextClassLoader().getResource("./myGraalVmJsLib.js");
        contextEmbed.getBindings("js").putMember("myLib", jsLib );
        Value result = contextEmbed.eval("js", new String(Files.readAllBytes(Paths.get(myResource.toURI()))));
        result.getMember("functionUseMyLib").executeVoid();
    }
}

Running this will produce the following outputs

canExecute: true
Exception in thread "main" java.lang.IllegalArgumentException: The value 'DynamicObject<JSFunction>@4550bb58' cannot be passed from one context to another. The current context is 0x4ec4f3a0 and the argument value originates from context 0x223191a6.
	at com.oracle.truffle.polyglot.PolyglotLanguageContext.migrateValue(PolyglotLanguageContext.java:696)
	at com.oracle.truffle.polyglot.PolyglotLanguageContext.toGuestValue(PolyglotLanguageContext.java:654)
	at com.oracle.truffle.polyglot.PolyglotLanguageContext$ToGuestValueNode.apply(PolyglotLanguageContext.java:543)
	at com.oracle.truffle.polyglot.PolyglotValue$InteropCodeCache$PutMemberNode.executeImpl(PolyglotValue.java:1092)
	at com.oracle.truffle.polyglot.HostRootNode.execute(HostRootNode.java:94)
	at com.oracle.truffle.api.impl.DefaultCallTarget.call(DefaultCallTarget.java:98)
	at com.oracle.truffle.api.impl.TVMCI.callProfiled(TVMCI.java:263)
	at com.oracle.truffle.api.impl.Accessor.callProfiled(Accessor.java:724)
	at com.oracle.truffle.polyglot.VMAccessor.callProfiled(VMAccessor.java:91)
	at com.oracle.truffle.polyglot.PolyglotValue$InteropValue.putMember(PolyglotValue.java:2466)
	at org.graalvm.polyglot.Value.putMember(Value.java:286)
	at GraalVmTest.main(GraalVmTest.java:27)
Caused by: Attached Guest Language Frames (1)

Using whatever JS library in my embedded code is a blocker for our project. Is there any way I can achieve this besides the approach I've tried here ? @chumer @wirthi

@nhoughto
Copy link
Author

nhoughto commented Jun 22, 2019 via email

@thomasreinecke
Copy link

@nhoughto I dont need stateful or mutable, thats fine. When you mention "code caching and just load it into every context", can you please make an example ? Thx

@nhoughto
Copy link
Author

nhoughto commented Jun 23, 2019 via email

@thomasreinecke
Copy link

great stuff, that worked. thank you @nhoughto

@nhoughto
Copy link
Author

nhoughto commented Jul 2, 2019

Does 19.1.0 fix this? Seems to be a possibly related reference..

Enabled code sharing across threads using ContextPolicy.SHARED

🤞🏼

@pramodanarase
Copy link

Any update on this? Is there any plan to fix this issue? Is there any workaround to share value across context?

@pramodanarase
Copy link

@wirthi @chumer Any update? Any workaround ? Please share..

@kokhoor
Copy link

kokhoor commented May 31, 2021

+1 sharing value across context should be supported for performance reason and memory optimization.
This way, you can have a global context and reusable objects can be placed in the global context and its value shared across VMs.
Rhino use to have such a shared scope feature which is very useful to avoid overhead of multithread having to keep the same data separately in each context.

@chumer
Copy link
Member

chumer commented Jun 16, 2021

Sorry for letting you wait for such a long time.

Here is a workaround using proxies how you can make JSON like values be sharable across context. Note that the contexts both must not be closed in order for this to work:

    public static void main(String[] args) {
        Engine engine = Engine.create();
        try(Context context1 = Context.newBuilder("js").engine(engine).allowAllAccess(true).build();
            Context context2 = Context.newBuilder("js").engine(engine).allowAllAccess(true).build()) {
            
            Value value1 = context1.eval("js", "({data:42, array:[], object:{data:42},})");
            Value result = context2.eval("js", "e => {return e.object.data;}").execute(createSharable(value1));
            System.out.println(result); // prints 42
        }
    }

    private static Object createSharable(Value v) {
        if (v.isBoolean()) {
            return v.asBoolean();
        } else if (v.isNumber()) {
            return v.as(Number.class);
        } else if (v.isString()) {
            return v.asString();
        } else if (v.isHostObject()) {
            return v.asHostObject();
        }  else if (v.isProxyObject()) {
            return v.asProxyObject();
        } else if (v.hasArrayElements()) {
            return new ProxyArray() {
                @Override
                public void set(long index, Value value) {
                    v.setArrayElement(index, createSharable(value));
                }
                
                @Override
                public long getSize() {
                    return v.getArraySize();
                }
                
                @Override
                public Object get(long index) {
                    return createSharable(v.getArrayElement(index));
                }
                
                @Override
                public boolean remove(long index) {
                    return v.removeArrayElement(index);
                }
            };
        } else if (v.hasMembers()) {
            return new ProxyObject() {
                
                @Override
                public void putMember(String key, Value value) {
                    v.putMember(key, createSharable(value));
                }
                
                @Override
                public boolean hasMember(String key) {
                    return v.hasMember(key);
                }
                
                @Override
                public Object getMemberKeys() {
                    return v.getMemberKeys().toArray(new String[0]);
                }
                
                @Override
                public Object getMember(String key) {
                    return createSharable(v.getMember(key));
                }
                
                @Override
                public boolean removeMember(String key) {
                    return v.removeMember(key);
                }
            };
        } else {
            throw new UnsupportedOperationException("Unsupported value");
        }
    }

Also note that both contexts must not be used from multiple threads at the same time. Otherwise you will get another error.

@nhoughto
Copy link
Author

Does this maintain a single instance where updates are visible across both (many) contexts? That was the problem I had with Proxies was that you end up passing by value/cloning and updates aren't visible across all contexts undermining the whole thing.

@chumer
Copy link
Member

chumer commented Jun 16, 2021

@nhoughto In this workaround, objects and lists are not cloned. Proxies are designed to enable exactly that, passing objects without needing to clone. Note that in the example all return values are wrapped in proxies again. So there might be an issue with object identity, if you are relying on that. The proper implementation will do something similar automatically without the identity problem.

@nhoughto
Copy link
Author

ok great, well i think that solves my problem. Extra internet points for you for solving a vintage issue 👍

@chumer chumer reopened this Jun 16, 2021
@chumer
Copy link
Member

chumer commented Jun 16, 2021

I think we should keep this open until we have a better solution than this workaround. There are other use-cases, like using the polyglot API from guest languages directly, where this workaround does not work so easily.

@virustotalop
Copy link

virustotalop commented Jun 17, 2021

@chumer The workaround looks good for a lot of use cases, nice job! I am running into an issue with trying to run a function in javascript from a different context. The exact message I am getting is failed due to: Message not supported. Any idea how I can accomplish that with creating a shareable context? It looks like the function is a MetaObject and I tried to check for it and create a proxy but that didn't work so I scrapped it but now I'm stumped where to go from here. Any suggestions on what to try to do about this particular issue? Sample code below.

private static Context createContext() {
     Context context = Context
     .newBuilder()
     .option("js.nashorn-compat", "true")
     .allowExperimentalOptions(true)
     .allowIO(true)
     .allowCreateThread(true)
     .allowAllAccess(true)
     .build();
     return context;
}

Context context = GraalTest.createContext();
Value printMe = context.eval("js", "function() {console.log('Hello World!');}");
Context newContext = GraalTest.createContext();
Object wrappedPrint = createSharable(printMe);
newContext.getBindings("js").putMember("printIt", wrappedPrint);
newContext.eval("js", "printIt()");

@chumer
Copy link
Member

chumer commented Jun 21, 2021

@virustotalop yes that is expected the workaround only covers members and arrays, no functions. You can extend the workaround arbitrarily to cover more types. The real implementation will of course cover all types automatically.

Here is the updated example:

    public static void main(String[] args) {
        Context context = createContext();
        Value printMe = context.eval("js", "function() {console.log('Hello World!');}");
        Context newContext = createContext();
        Object wrappedPrint = createSharable(printMe);
        newContext.getBindings("js").putMember("printIt", wrappedPrint);
        newContext.eval("js", "printIt()");
     }

    private static Object createSharable(Value v) {
        if (v.isNull()) {
            return null;
        } else if (v.isBoolean()) {
            return v.asBoolean();
        } else if (v.isNumber()) {
            return v.as(Number.class);
        } else if (v.isString()) {
            return v.asString();
        } else if (v.isHostObject()) {
            return v.asHostObject();
        }  else if (v.isProxyObject()) {
            return v.asProxyObject();
        } else if (v.hasArrayElements()) {
            return new ProxyArray() {
                @Override
                public void set(long index, Value value) {
                    v.setArrayElement(index, createSharable(value));
                }
                
                @Override
                public long getSize() {
                    return v.getArraySize();
                }
                
                @Override
                public Object get(long index) {
                    return createSharable(v.getArrayElement(index));
                }
                
                @Override
                public boolean remove(long index) {
                    return v.removeArrayElement(index);
                }
            };
        } else if (v.hasMembers()) {
            if (v.canExecute()) {
                if (v.canInstantiate()) {
                    // js functions have members, can be executed and are instantiable
                    return new SharableMembersAndInstantiable(v);
                } else {
                    return new SharableMembersAndExecutable(v);
                }
            } else {
                return new SharableMembers(v);
            }
        } else {
            throw new AssertionError("Uncovered shared value. ");
        }
    }
    
    static class SharableMembers implements ProxyObject {
        final Value v;
        SharableMembers(Value v) {
            this.v = v;
        }
        
        @Override
        public void putMember(String key, Value value) {
            v.putMember(key, createSharable(value));
        }
        
        @Override
        public boolean hasMember(String key) {
            return v.hasMember(key);
        }
        
        @Override
        public Object getMemberKeys() {
            return v.getMemberKeys().toArray(new String[0]);
        }
        
        @Override
        public Object getMember(String key) {
            return createSharable(v.getMember(key));
        }
        
        @Override
        public boolean removeMember(String key) {
            return v.removeMember(key);
        }
    }
    
    static class SharableMembersAndExecutable extends SharableMembers implements ProxyExecutable {

        SharableMembersAndExecutable(Value v) {
            super(v);
        }

        @Override
        public Object execute(Value... arguments) {
            Object[] newArgs = new Value[arguments.length];
            for (int i = 0; i < newArgs.length; i++) {
                newArgs[i] = createSharable(arguments[i]);
            }
            return createSharable(v.execute(newArgs));
        }
        
    }
    
    static class SharableMembersAndInstantiable extends SharableMembersAndExecutable implements ProxyInstantiable {

        SharableMembersAndInstantiable(Value v) {
            super(v);
        }

        @Override
        public Object newInstance(Value... arguments) {
            Object[] newArgs = new Value[arguments.length];
            for (int i = 0; i < newArgs.length; i++) {
                newArgs[i] = createSharable(arguments[i]);
            }
            return createSharable(v.execute(newArgs));
        }
        
    }

@guygoldwasser
Copy link

guygoldwasser commented Jun 22, 2021

B"H
Hi, I got same exception but in regular jsr223 implementation (while first try to merge from Rhino to Graal):
e.g.
I have a js prototype that already compiled into global scope. now I want to delegate to js via java interface which similar to the js.
so i am using the getInterface engine implementation to do the connectivity between my already compiled context and the Java interface i want to use.
Invocable invocable = (Invocable) engine; Observer observer = invocable .getInterface(engine.eval("Observer", engine.getBindings(ScriptContext.GLOBAL_SCOPE)), Observer.class);

BTW, whith Rhino i didnt need to declare the binding scope, as Rhino automatically go to global scope when not find any.

EDIT: I tried same only with Engine scope. and it didnt get this issue. it does get another issue cause need to enable the js to java connection, and this can be done when declaring the context. via jsr223 i dont see how i can control the context variables.

@chenjie100
Copy link

how to share javascript utility code in different context?

@chumer
Copy link
Member

chumer commented Jul 23, 2021

Proper support for value sharing was merged and will land in 21.3.
8d70605

Here is an example how to embed contexts inside guest languages: https://github.com/oracle/graal/blob/master/docs/reference-manual/embedding/embed-languages.md#embed-guest-languages-in-guest-languages

There is some Javadoc on the details:
https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/Context.Builder.html#allowValueSharing-boolean-

Also with the capability to disable it again.

There should be a CE build arriving soon where you can try this:
https://github.com/graalvm/graalvm-ce-dev-builds/releases/

@chumer chumer closed this as completed Jul 23, 2021
@pramodanarase
Copy link

@chumer - Will it solve the issue : Value form context1 - thread1 will be shared in context2 - thread2 if conext1 is closed??
Otherwise need to maintain all the open context which leads to high memory usage or leaks.

@chumer
Copy link
Member

chumer commented Jul 28, 2021

@pramodanarase as long as an object from another context is still referenced it might consume the memory of the original context even if it is closed. We are not eagerly breaking references currently, that would be pretty inefficient in the current architecture. You should avoid references between contexts when other contexts are closed anyway, as any access to such objects can fail.

@virustotalop
Copy link

@chumer Thank you that helped a lot. I did notice one issue that I'm not sure whether it will affect the official implementation but the Value arrays seemed to cause a ArrayStoreException like in oracle/graaljs#36. Once those are swapped out for generic Object arrays the issue seems to be fixed. I figured this would help anyone who is trying to run the workaround temporarily.

@abedinihamzeh
Copy link

abedinihamzeh commented Apr 10, 2023

I'm testing the JS language on GraalVM and getting an exception when trying to run

public class App2
{
    
    static String JS_CODE = """
            
            
            function a(){ 
                print("Hello world"); 
            }
            function b(){
                a();
            }
            
            b(); 
            
            """;


    public static void main(String[] args) {
        try (Context context = Context.newBuilder("js").allowAllAccess(true)
                .allowHostClassLookup(s -> true)
                .allowHostAccess(HostAccess.ALL)
                .build();) {
            Value value = context.eval("js", JS_CODE);
            System.err.println( value.canExecute());
             value.execute();
        }
    }

and I got this exception:
Unsupported operation Value.execute(Object...) for 'undefined'(language: JavaScript, type: undefined). You can ensure that the operation is supported using Value.canExecute().
at org.graalvm.truffle/com.oracle.truffle.polyglot.PolyglotEngineException.unsupported(PolyglotEngineException.java:147)
at org.graalvm.truffle/com.oracle.truffle.polyglot.PolyglotValueDispatch.unsupported(PolyglotValueDispatch.java:1257)
at org.graalvm.truffle/com.oracle.truffle.polyglot.PolyglotValueDispatch.executeUnsupported(PolyglotValueDispatch.java:595)
at org.graalvm.truffle/com.oracle.truffle.polyglot.PolyglotValueDispatch$InteropValue$AbstractExecuteNode.executeShared(PolyglotValueDispatch.java:4277)
at org.graalvm.truffle/com.oracle.truffle.polyglot.PolyglotValueDispatch$InteropValue$ExecuteNoArgsNode.executeImpl(PolyglotValueDispatch.java:4375)
at org.graalvm.truffle/com.oracle.truffle.polyglot.HostToGuestRootNode.execute(HostToGuestRootNode.java:124)
at jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.executeRootNode(OptimizedCallTarget.java:709)
at jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.profiledPERoot(OptimizedCallTarget.java:632)
at jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.callBoundary(OptimizedCallTarget.java:565)
at jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.doInvoke(OptimizedCallTarget.java:549)
at jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime.GraalRuntimeSupport.callProfiled(GraalRuntimeSupport.java:256)
at org.graalvm.truffle/com.oracle.truffle.polyglot.PolyglotValueDispatch$InteropValue.execute(PolyglotValueDispatch.java:2401)
at org.graalvm.sdk/org.graalvm.polyglot.Value.execute(Value.java:878)
at org.example.App2.main(App2.java:33)
Caused by: Attached Guest Language Frames (1)

@wirthi
Copy link
Member

wirthi commented Apr 11, 2023

Hi @abedinihamzeh

I believe you should open a new ticket for questions like that (or ask on our Slack channel, see https://www.graalvm.org/slack-invitation/)

Unsupported operation Value.execute(Object...) for 'undefined' ...

You cannot execute undefined, which seems reasonable. Your JavaScript code returns the result of the call b(), which returns the result of the call of a(), which does not return anything, i.e., undefined. And undefined cannot be executed. Change the last line of your JavaScript code from b(); to b; and it might do exactly what you intend to.

Or use something like Value b = context.getBindings("js").getMember("b"); b.execute() on the Java side.

Best,
Christian

mxro added a commit to javadelight/delight-graaljs-sandbox that referenced this issue Jul 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature javascript tracking This issue has an analogue in an internal issue tracker truffle
Projects
None yet
Development

No branches or pull requests