diff --git a/engine/runtime/src/main/java/org/enso/interpreter/Constants.java b/engine/runtime/src/main/java/org/enso/interpreter/Constants.java index 6cf28d21b6..e27aa7ff54 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/Constants.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/Constants.java @@ -16,5 +16,6 @@ public class Constants { public static final String FUNCTION_INTEROP_LIBRARY = "10"; public static final String THUNK_EXECUTOR_NODE = "10"; public static final String EVAL_NODE = "10"; + public static final int BUILTIN_INTEROP_DISPATCH = 3; } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Execute0Node.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Execute0Node.java new file mode 100644 index 0000000000..e3206a0823 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Execute0Node.java @@ -0,0 +1,74 @@ +package org.enso.interpreter.node.expression.builtin.interop.generic; + +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.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.state.Stateful; + +@NodeInfo( + shortName = "Polyglot.execute0", + description = "Executes a zero-argument polyglot function.") +public class Execute0Node extends BuiltinRootNode { + private Execute0Node(Language language) { + super(language); + } + + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + new Execute0Node(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "callable", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + /** + * Executes the node. + * + * @param frame current execution frame. + * @return the result of converting input into a string. + */ + @Override + public Stateful execute(VirtualFrame frame) { + Object[] args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); + Object callable = args[1]; + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + try { + Object res = library.execute(callable); + return new Stateful(state, res); + } catch (UnsupportedMessageException | ArityException | UnsupportedTypeException e) { + err.enter(); + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Polyglot.execute0"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Execute1Node.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Execute1Node.java new file mode 100644 index 0000000000..7978588335 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Execute1Node.java @@ -0,0 +1,76 @@ +package org.enso.interpreter.node.expression.builtin.interop.generic; + +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.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.state.Stateful; + +@NodeInfo( + shortName = "Polyglot.execute1", + description = "Executes a one-argument polyglot function.") +public class Execute1Node extends BuiltinRootNode { + private Execute1Node(Language language) { + super(language); + } + + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + new Execute1Node(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "callable", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(2, "arg1", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + /** + * Executes the node. + * + * @param frame current execution frame. + * @return the result of converting input into a string. + */ + @Override + public Stateful execute(VirtualFrame frame) { + Object[] args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); + Object callable = args[1]; + Object arg1 = args[2]; + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + try { + Object res = library.execute(callable, arg1); + return new Stateful(state, res); + } catch (UnsupportedMessageException | ArityException | UnsupportedTypeException e) { + err.enter(); + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Polyglot.execute1"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Execute2Node.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Execute2Node.java new file mode 100644 index 0000000000..b012b84092 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Execute2Node.java @@ -0,0 +1,75 @@ +package org.enso.interpreter.node.expression.builtin.interop.generic; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.*; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.state.Stateful; + +@NodeInfo( + shortName = "Polyglot.execute2", + description = "Executes a two-arguments polyglot function.") +public class Execute2Node extends BuiltinRootNode { + private Execute2Node(Language language) { + super(language); + } + + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + new Execute2Node(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "callable", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(2, "arg1", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(3, "arg2", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + /** + * Executes the node. + * + * @param frame current execution frame. + * @return the result of converting input into a string. + */ + @Override + public Stateful execute(VirtualFrame frame) { + Object[] args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); + Object callable = args[1]; + Object arg1 = args[2]; + Object arg2 = args[3]; + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + try { + Object res = library.execute(callable, arg1, arg2); + return new Stateful(state, res); + } catch (UnsupportedMessageException | ArityException | UnsupportedTypeException e) { + err.enter(); + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Polyglot.execute2"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetArrayElementNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetArrayElementNode.java new file mode 100644 index 0000000000..8363afad84 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetArrayElementNode.java @@ -0,0 +1,82 @@ +package org.enso.interpreter.node.expression.builtin.interop.generic; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.nodes.UnexpectedResultException; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.error.RuntimeError; +import org.enso.interpreter.runtime.state.Stateful; +import org.enso.interpreter.runtime.type.TypesGen; + +@NodeInfo( + shortName = "Polyglot.get_array_element", + description = "Gets an element by index from a polyglot array.") +public class GetArrayElementNode extends BuiltinRootNode { + private GetArrayElementNode(Language language) { + super(language); + } + + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + new GetArrayElementNode(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "array", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(2, "index", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + /** + * Executes the node. + * + * @param frame current execution frame. + * @return the result of converting input into a string. + */ + @Override + public Stateful execute(VirtualFrame frame) { + Object array = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[1]; + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + try { + long index = + TypesGen.expectLong( + Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[2]); + + Object res = library.readArrayElement(array, index); + return new Stateful(state, res); + } catch (UnsupportedMessageException + | InvalidArrayIndexException + | UnexpectedResultException e) { + err.enter(); + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Polyglot.get_array_element"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetArraySizeNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetArraySizeNode.java new file mode 100644 index 0000000000..9bcfd6ac27 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetArraySizeNode.java @@ -0,0 +1,70 @@ +package org.enso.interpreter.node.expression.builtin.interop.generic; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.error.RuntimeError; +import org.enso.interpreter.runtime.state.Stateful; + +@NodeInfo(shortName = "Polyglot.get_array_size", description = "Gets the size of a polyglot array.") +public class GetArraySizeNode extends BuiltinRootNode { + private GetArraySizeNode(Language language) { + super(language); + } + + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + new GetArraySizeNode(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "array", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + /** + * Executes the node. + * + * @param frame current execution frame. + * @return the result of converting input into a string. + */ + @Override + public Stateful execute(VirtualFrame frame) { + Object obj = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[1]; + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + try { + long size = library.getArraySize(obj); + return new Stateful(state, size); + } catch (UnsupportedMessageException e) { + err.enter(); + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Polyglot.get_array_size"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetMemberNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetMemberNode.java new file mode 100644 index 0000000000..9c23d93ccf --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetMemberNode.java @@ -0,0 +1,80 @@ +package org.enso.interpreter.node.expression.builtin.interop.generic; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.nodes.UnexpectedResultException; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.state.Stateful; +import org.enso.interpreter.runtime.type.TypesGen; + +@NodeInfo( + shortName = "Polyglot.get_member", + description = "Gets a member by name from a polyglot object.") +public class GetMemberNode extends BuiltinRootNode { + private GetMemberNode(Language language) { + super(language); + } + + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + new GetMemberNode(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "object", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(2, "name", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + /** + * Executes the node. + * + * @param frame current execution frame. + * @return the result of converting input into a string. + */ + @Override + public Stateful execute(VirtualFrame frame) { + Object obj = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[1]; + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + try { + String name = + TypesGen.expectString( + Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[2]); + Object res = library.readMember(obj, name); + return new Stateful(state, res); + } catch (UnsupportedMessageException + | UnknownIdentifierException + | UnexpectedResultException e) { + err.enter(); + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Polyglot.get_member"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetMembersNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetMembersNode.java new file mode 100644 index 0000000000..17b3e44340 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetMembersNode.java @@ -0,0 +1,72 @@ +package org.enso.interpreter.node.expression.builtin.interop.generic; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.error.RuntimeError; +import org.enso.interpreter.runtime.state.Stateful; + +@NodeInfo( + shortName = "Polyglot.get_members", + description = "Returns a polyglot array of the object's member names.") +public class GetMembersNode extends BuiltinRootNode { + private GetMembersNode(Language language) { + super(language); + } + + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + new GetMembersNode(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "object", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + /** + * Executes the node. + * + * @param frame current execution frame. + * @return the result of converting input into a string. + */ + @Override + public Stateful execute(VirtualFrame frame) { + Object arg = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[1]; + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + try { + Object members = library.getMembers(arg); + return new Stateful(state, members); + } catch (UnsupportedMessageException e) { + err.enter(); + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Polyglot.get_members"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Instantiate0Node.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Instantiate0Node.java new file mode 100644 index 0000000000..180fa59d22 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Instantiate0Node.java @@ -0,0 +1,74 @@ +package org.enso.interpreter.node.expression.builtin.interop.generic; + +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.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.state.Stateful; + +@NodeInfo( + shortName = "Polyglot.instantiate0", + description = "Instantiates a zero-argument polyglot constructor.") +public class Instantiate0Node extends BuiltinRootNode { + private Instantiate0Node(Language language) { + super(language); + } + + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + new Instantiate0Node(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "constructor", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + /** + * Executes the node. + * + * @param frame current execution frame. + * @return the result of converting input into a string. + */ + @Override + public Stateful execute(VirtualFrame frame) { + Object[] args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); + Object constructor = args[1]; + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + try { + Object res = library.instantiate(constructor); + return new Stateful(state, res); + } catch (UnsupportedMessageException | ArityException | UnsupportedTypeException e) { + err.enter(); + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Polyglot.instantiate0"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Instantiate1Node.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Instantiate1Node.java new file mode 100644 index 0000000000..e126ee4014 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Instantiate1Node.java @@ -0,0 +1,76 @@ +package org.enso.interpreter.node.expression.builtin.interop.generic; + +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.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.state.Stateful; + +@NodeInfo( + shortName = "Polyglot.instantiate1", + description = "Instantiates a one-argument polyglot constructor.") +public class Instantiate1Node extends BuiltinRootNode { + private Instantiate1Node(Language language) { + super(language); + } + + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + new Instantiate1Node(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "constructor", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(2, "arg1", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + /** + * Executes the node. + * + * @param frame current execution frame. + * @return the result of converting input into a string. + */ + @Override + public Stateful execute(VirtualFrame frame) { + Object[] args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); + Object cons = args[1]; + Object arg1 = args[2]; + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + try { + Object res = library.instantiate(cons, arg1); + return new Stateful(state, res); + } catch (UnsupportedMessageException | ArityException | UnsupportedTypeException e) { + err.enter(); + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Polyglot.instantiate1"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Instantiate2Node.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Instantiate2Node.java new file mode 100644 index 0000000000..eabf446af0 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/Instantiate2Node.java @@ -0,0 +1,78 @@ +package org.enso.interpreter.node.expression.builtin.interop.generic; + +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.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.state.Stateful; + +@NodeInfo( + shortName = "Polyglot.instantiate2", + description = "Instantiates a two-arguments polyglot constructor.") +public class Instantiate2Node extends BuiltinRootNode { + private Instantiate2Node(Language language) { + super(language); + } + + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + new Instantiate2Node(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "constructor", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(2, "arg1", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(3, "arg2", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + /** + * Executes the node. + * + * @param frame current execution frame. + * @return the result of converting input into a string. + */ + @Override + public Stateful execute(VirtualFrame frame) { + Object[] args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); + Object cons = args[1]; + Object arg1 = args[2]; + Object arg2 = args[3]; + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + try { + Object res = library.instantiate(cons, arg1, arg2); + return new Stateful(state, res); + } catch (UnsupportedMessageException | ArityException | UnsupportedTypeException e) { + err.enter(); + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Polyglot.instantiate2"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/AddToClassPathNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/AddToClassPathNode.java new file mode 100644 index 0000000000..6dc53d8eaf --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/AddToClassPathNode.java @@ -0,0 +1,63 @@ +package org.enso.interpreter.node.expression.builtin.interop.java; + +import com.oracle.truffle.api.dsl.CachedContext; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.nodes.UnexpectedResultException; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.state.Stateful; +import org.enso.interpreter.runtime.type.TypesGen; + +import java.io.File; + +@NodeInfo(shortName = "Java.add_to_class_path", description = "Adds a path to the host class path.") +public abstract class AddToClassPathNode extends BuiltinRootNode { + AddToClassPathNode(Language language) { + super(language); + } + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + AddToClassPathNodeGen.create(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "path", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + @Specialization + Stateful doExecute(VirtualFrame frame, @CachedContext(Language.class) Context context) { + try { + String arg = + TypesGen.expectString( + Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[1]); + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + context.getEnvironment().addToHostClassPath(context.getTruffleFile(new File(arg))); + return new Stateful(state, context.getBuiltins().unit()); + } catch (UnexpectedResultException e) { + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Java.add_to_class_path"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/LookupClassNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/LookupClassNode.java new file mode 100644 index 0000000000..fd7dc167a5 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/LookupClassNode.java @@ -0,0 +1,61 @@ +package org.enso.interpreter.node.expression.builtin.interop.java; + +import com.oracle.truffle.api.dsl.CachedContext; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.nodes.UnexpectedResultException; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.state.Stateful; +import org.enso.interpreter.runtime.type.TypesGen; + +@NodeInfo(shortName = "Java.lookup_class", description = "Looks up a Java symbol.") +public abstract class LookupClassNode extends BuiltinRootNode { + LookupClassNode(Language language) { + super(language); + } + + /** + * Creates a function wrapping this node. + * + * @param language the current language instance + * @return a function wrapping this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + LookupClassNodeGen.create(language), + CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE), + new ArgumentDefinition(1, "name", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + @Specialization + Stateful doExecute(VirtualFrame frame, @CachedContext(Language.class) Context ctx) { + try { + String arg = + TypesGen.expectString( + Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[1]); + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + Object res = ctx.getEnvironment().lookupHostSymbol(arg); + return new Stateful(state, res); + } catch (UnexpectedResultException e) { + throw new PanicException(e.getMessage(), this); + } + } + + /** + * Returns a language-specific name for this node. + * + * @return the name of this node + */ + @Override + public String getName() { + return "Java.lookup_class"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/io/NanoTimeNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/io/NanoTimeNode.java new file mode 100644 index 0000000000..fc248b358d --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/io/NanoTimeNode.java @@ -0,0 +1,52 @@ +package org.enso.interpreter.node.expression.builtin.io; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.NodeInfo; +import org.enso.interpreter.Language; +import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.callable.function.FunctionSchema; +import org.enso.interpreter.runtime.state.Stateful; + +@NodeInfo(shortName = "IO.nano_time", description = "Gets the nanosecond resolution system time.") +public final class NanoTimeNode extends BuiltinRootNode { + private NanoTimeNode(Language language) { + super(language); + } + + /** + * Creates a {@link Function} object wrapping this object. + * + * @param language the current {@link Language} instance + * @return a {@link Function} object wrapping the behavior of this node + */ + public static Function makeFunction(Language language) { + return Function.fromBuiltinRootNode( + new NanoTimeNode(language), + FunctionSchema.CallStrategy.ALWAYS_DIRECT, + new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE)); + } + + @Override + public Stateful execute(VirtualFrame frame) { + Object state = Function.ArgumentsHelper.getState(frame.getArguments()); + return new Stateful(state, getNanoTime()); + } + + @CompilerDirectives.TruffleBoundary + private long getNanoTime() { + return System.nanoTime(); + } + + /** + * Gets the source-level name of this node. + * + * @return the source-level name of the node + */ + @Override + public String getName() { + return "IO.nano_time"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Builtins.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Builtins.java index e649067c5c..333286d48a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Builtins.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Builtins.java @@ -9,7 +9,10 @@ import org.enso.interpreter.node.expression.builtin.error.CatchPanicNode; import org.enso.interpreter.node.expression.builtin.error.PanicNode; import org.enso.interpreter.node.expression.builtin.error.ThrowErrorNode; import org.enso.interpreter.node.expression.builtin.function.ExplicitCallFunctionNode; +import org.enso.interpreter.node.expression.builtin.interop.generic.*; +import org.enso.interpreter.node.expression.builtin.io.NanoTimeNode; import org.enso.interpreter.node.expression.builtin.io.PrintNode; +import org.enso.interpreter.node.expression.builtin.interop.java.*; import org.enso.interpreter.node.expression.builtin.number.AddNode; import org.enso.interpreter.node.expression.builtin.number.DivideNode; import org.enso.interpreter.node.expression.builtin.number.ModNode; @@ -83,6 +86,8 @@ public class Builtins { AtomConstructor error = new AtomConstructor("Error", scope).initializeFields(); AtomConstructor state = new AtomConstructor("State", scope).initializeFields(); + AtomConstructor java = new AtomConstructor("Java", scope).initializeFields(); + scope.registerConstructor(unit); scope.registerConstructor(any); scope.registerConstructor(number); @@ -100,7 +105,11 @@ public class Builtins { scope.registerConstructor(syntaxError); scope.registerConstructor(compileError); + scope.registerConstructor(java); + scope.registerConstructor(createPolyglot(language)); + scope.registerMethod(io, "println", PrintNode.makeFunction(language)); + scope.registerMethod(io, "nano_time", NanoTimeNode.makeFunction(language)); scope.registerMethod(panic, "throw", PanicNode.makeFunction(language)); scope.registerMethod(panic, "recover", CatchPanicNode.makeFunction(language)); @@ -126,6 +135,28 @@ public class Builtins { scope.registerMethod(text, "+", ConcatNode.makeFunction(language)); scope.registerMethod(any, "to_text", AnyToTextNode.makeFunction(language)); scope.registerMethod(any, "json_serialize", JsonSerializeNode.makeFunction(language)); + + scope.registerMethod(java, "add_to_class_path", AddToClassPathNode.makeFunction(language)); + scope.registerMethod(java, "lookup_class", LookupClassNode.makeFunction(language)); + } + + private AtomConstructor createPolyglot(Language language) { + AtomConstructor polyglot = new AtomConstructor("Polyglot", scope).initializeFields(); + scope.registerMethod(polyglot, "execute0", Execute0Node.makeFunction(language)); + scope.registerMethod(polyglot, "execute1", Execute1Node.makeFunction(language)); + scope.registerMethod(polyglot, "execute2", Execute2Node.makeFunction(language)); + + scope.registerMethod(polyglot, "instantiate0", Instantiate0Node.makeFunction(language)); + scope.registerMethod(polyglot, "instantiate1", Instantiate1Node.makeFunction(language)); + scope.registerMethod(polyglot, "instantiate2", Instantiate2Node.makeFunction(language)); + + scope.registerMethod(polyglot, "get_member", GetMemberNode.makeFunction(language)); + scope.registerMethod(polyglot, "get_members", GetMembersNode.makeFunction(language)); + + scope.registerMethod(polyglot, "get_array_size", GetArraySizeNode.makeFunction(language)); + scope.registerMethod( + polyglot, "get_array_element", GetArrayElementNode.makeFunction(language)); + return polyglot; } /** diff --git a/engine/runtime/src/test/java/org/enso/example/TestClass.java b/engine/runtime/src/test/java/org/enso/example/TestClass.java new file mode 100644 index 0000000000..7f7256669c --- /dev/null +++ b/engine/runtime/src/test/java/org/enso/example/TestClass.java @@ -0,0 +1,29 @@ +package org.enso.example; + +import java.util.function.Function; + +/** A class used for testing Java Interop from Enso code */ +public class TestClass { + + private final Function function; + + public TestClass(Function function) { + this.function = function; + } + + public TestClass() { + this(x -> x); + } + + public static long add(long a, long b) { + return a + b; + } + + public long callFunctionAndIncrement(long argument) { + return function.apply(argument) + 1; + } + + public void method1() {} + + public void method2() {} +} diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/InterpreterTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/InterpreterTest.scala index 123ab0c67f..0ee433e024 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/InterpreterTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/InterpreterTest.scala @@ -76,6 +76,7 @@ trait InterpreterRunner { val ctx = Context .newBuilder(LanguageInfo.ID) .allowExperimentalOptions(true) + .allowAllAccess(true) .out(output) .build() lazy val executionContext = new PolyglotContext(ctx) diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/JavaInteropTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/JavaInteropTest.scala new file mode 100644 index 0000000000..b9e1ea7c6b --- /dev/null +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/JavaInteropTest.scala @@ -0,0 +1,51 @@ +package org.enso.interpreter.test.semantic + +import org.enso.interpreter.test.InterpreterTest + +class JavaInteropTest extends InterpreterTest { + "Java interop" should "allow calling static methods from java classes" in { + val code = + """ + |main = + | class = Java.lookup_class "org.enso.example.TestClass" + | method = Polyglot.get_member class "add" + | Polyglot.execute2 method 1 2 + |""".stripMargin + + eval(code) shouldEqual 3 + } + + "Java interop" should "allow instantiating objects and calling methods on them" in { + val code = + """ + |main = + | class = Java.lookup_class "org.enso.example.TestClass" + | instance = Polyglot.instantiate1 class (x -> x * 2) + | method = Polyglot.get_member instance "callFunctionAndIncrement" + | Polyglot.execute1 method 10 + |""".stripMargin + eval(code) shouldEqual 21 + } + + "Java interop" should "allow listing available members of an object" in { + val code = + """ + |main = + | class = Java.lookup_class "org.enso.example.TestClass" + | instance = Polyglot.instantiate0 class + | members = Polyglot.get_members instance + | IO.println (Polyglot.get_array_size members) + | IO.println (Polyglot.get_array_element members 0) + | IO.println (Polyglot.get_array_element members 1) + | IO.println (Polyglot.get_array_element members 2) + |""".stripMargin + eval(code) + val count :: methods = consumeOut + count shouldEqual "3" + methods.toSet shouldEqual Set( + "method1", + "method2", + "callFunctionAndIncrement" + ) + } +}