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

Execute foreign function and check autoscoped constructor result #10187

Merged
merged 9 commits into from
Jun 6, 2024
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package org.enso.interpreter.node.callable;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.SourceSection;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import org.enso.interpreter.Constants;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.BaseNode.TailStatus;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
import org.enso.interpreter.runtime.EnsoContext;
Expand All @@ -29,6 +35,7 @@
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State;

/**
Expand Down Expand Up @@ -278,7 +285,7 @@ public Object invokeWarnings(
VirtualFrame callerFrame,
State state,
Object[] arguments,
@CachedLibrary(limit = "3") WarningsLibrary warnings) {
@Shared("warnings") @CachedLibrary(limit = "3") WarningsLibrary warnings) {

Warning[] extracted;
Object callable;
Expand Down Expand Up @@ -323,6 +330,42 @@ public Object invokeWarnings(
}
}

@Specialization(
guards = {
"!warnings.hasWarnings(self)",
"!types.hasType(self)",
"!types.hasSpecialDispatch(self)",
"iop.isExecutable(self)",
})
Object doPolyglot(
Object self,
VirtualFrame frame,
State state,
Object[] arguments,
@CachedLibrary(limit = "3") InteropLibrary iop,
@Shared("warnings") @CachedLibrary(limit = "3") WarningsLibrary warnings,
@CachedLibrary(limit = "3") TypesLibrary types,
@Cached ThunkExecutorNode thunkNode) {
var errors = EnsoContext.get(this).getBuiltins().error();
try {
for (int i = 0; i < arguments.length; i++) {
arguments[i] = thunkNode.executeThunk(frame, arguments[i], state, TailStatus.NOT_TAIL);
}
return iop.execute(self, arguments);
} catch (UnsupportedTypeException ex) {
var err = errors.makeUnsupportedArgumentsError(ex.getSuppliedValues(), ex.getMessage());
throw new PanicException(err, this);
} catch (ArityException ex) {
var err =
errors.makeArityError(
ex.getExpectedMinArity(), ex.getExpectedMaxArity(), arguments.length);
throw new PanicException(err, this);
} catch (UnsupportedMessageException ex) {
var err = errors.makeNotInvokable(self);
throw new PanicException(err, this);
}
}

@Fallback
public Object invokeGeneric(
Object callable, VirtualFrame callerFrame, State state, Object[] arguments) {
Expand Down
13 changes: 12 additions & 1 deletion test/Base_Tests/src/Data/Polyglot_Spec.enso
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from Standard.Base import all

import Standard.Base.Errors.Common.Not_Invokable
from Standard.Test import all


Expand Down Expand Up @@ -34,6 +34,14 @@ add_specs suite_builder = suite_builder.group "Polyglot" group_builder->
group_builder.specify "access Integer field from Polyglot object" <|
js_meaning.meaning . should_equal 42

group_builder.specify "Execute JavaScript function" <|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice to also see what happens if I try to specify more or less arguments to the function. Can you do, e.g.,

curried = js_plus 3
curried 5 . should_equal 8

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invoking:

curried = js_plus 3

main =
    curried

foreign js js_plus = """
    return (a, b) => a + b;

yields NaN as 3 + undefined is NaN.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd expect it to behave the same way as if js_plus was an enso function. I.e. main should return a function

Copy link
Member Author

@JaroslavTulach JaroslavTulach Jun 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested in fe14b8d

I'd expect it to behave the same way as if js_plus was an enso function

You might expect it, but there is no chance to achieve such behavior. JavaScript functions don't have a fixed arity. You can invoke them with any number of arguments. If you want that to be changed, then you need to beg for a change of the ECMAScript specification ;-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I didn't think about that

js_plus 3 5 . should_equal 8

group_builder.specify "Cannot Execute JavaScript number" <|
fourty_two = js_meaning.meaning
Test.expect_panic Not_Invokable <|
fourty_two "Cannot invoke"
radeusgd marked this conversation as resolved.
Show resolved Hide resolved

group_builder.specify "use Integer obtained from a call" <|
Java_Integer.parseInt "42" . should_equal 42

Expand All @@ -50,6 +58,9 @@ add_specs suite_builder = suite_builder.group "Polyglot" group_builder->
foreign js js_meaning = """
return { meaning : 6 * 7 };

foreign js js_plus = """
return (a, b) => a + b;

main filter=Nothing =
suite = Test.build suite_builder->
add_specs suite_builder
Expand Down
Loading