diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso index aeefa4fa3b66..86f726cc12af 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso @@ -27,8 +27,7 @@ get_stack_trace = prim_stack = primitive_get_stack_trace stack_with_prims = Vector.from_polyglot_array prim_stack # (First 2) drops the `Runtime.primitive_get_stack_trace` frame and this one - # (Last 1) drops the `org.graalvm.polyglot.Value.execute` frame - stack = stack_with_prims.drop (First 2) . drop (Last 1) + stack = stack_with_prims.drop (First 2) stack.map wrap_primitive_stack_trace_element ## ADVANCED diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetExecutableNameNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetExecutableNameNode.java index 0a91545726b7..506200f63d7c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetExecutableNameNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/GetExecutableNameNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.expression.builtin.interop.generic; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.Node; @@ -25,7 +26,12 @@ public class GetExecutableNameNode extends Node { Text execute(Object function) { try { - return Text.create(stringsLibrary.asString(functionsLibrary.getExecutableName(function))); + var name = functionsLibrary.getExecutableName(function); + if (name == null || !stringsLibrary.isString(name)) { + CompilerDirectives.transferToInterpreter(); + throw CompilerDirectives.shouldNotReachHere("name: " + name + " for " + function); + } + return Text.create(stringsLibrary.asString(name)); } catch (UnsupportedMessageException e) { err.enter(); Builtins builtins = EnsoContext.get(this).getBuiltins(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/GetStackTraceNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/GetStackTraceNode.java index 1f841aea0052..e9b9ed0d44d3 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/GetStackTraceNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/GetStackTraceNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.expression.builtin.runtime; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.BuiltinMethod; @@ -18,14 +19,28 @@ Array execute() { return stackTraceToArray(exception); } + @CompilerDirectives.TruffleBoundary public static Array stackTraceToArray(Throwable exception) { var elements = TruffleStackTrace.getStackTrace(exception); - if (elements == null) return new Array(); - var ret = Array.allocate(elements.size()); + if (elements == null) { + return Array.empty(); + } + int count = 0; for (int i = 0; i < elements.size(); i++) { var element = elements.get(i); - ret.getItems()[i] = element.getGuestObject(); + if (element.getTarget().getRootNode().isInternal()) { + continue; + } + count++; + } + var arr = new Object[count]; + for (int i = 0, at = 0; i < elements.size(); i++) { + var element = elements.get(i); + if (element.getTarget().getRootNode().isInternal()) { + continue; + } + arr[at++] = element.getGuestObject(); } - return ret; + return new Array(arr); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java index f5e8adeaf430..cc4271e0be0f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java @@ -118,12 +118,12 @@ public Object readArrayElement( } public long length() { - return this.getItems().length; + return items.length; } /** @return an empty array */ @Builtin.Method(description = "Creates an empty Array", autoRegister = false) - public static Object empty() { + public static Array empty() { return allocate(0); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java b/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java index 026b6ec6f7d7..7b9cd9608c2f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java @@ -3,15 +3,16 @@ import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.TruffleLogger; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.EventBinding; -import com.oracle.truffle.api.instrumentation.ExecutionEventListener; -import com.oracle.truffle.api.instrumentation.ExecutionEventNode; import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory; import com.oracle.truffle.api.interop.ArityException; 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.interop.UnsupportedTypeException; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.SourceSection; import org.enso.compiler.context.SimpleUpdate; import org.enso.interpreter.instrument.Endpoint; @@ -57,9 +58,11 @@ public class ExecutionService { private final EnsoContext context; private final Optional idExecutionInstrument; private final NotificationHandler.Forwarder notificationForwarder; - private final InteropLibrary interopLibrary = InteropLibrary.getFactory().getUncached(); private final TruffleLogger logger = TruffleLogger.getLogger(LanguageInfo.ID); private final ConnectedLockManager connectedLockManager; + private final ExecuteRootNode execute = new ExecuteRootNode(); + private final CallRootNode call = new CallRootNode(); + private final InvokeMemberRootNode invoke = new InvokeMemberRootNode(); private final Timer timer; @@ -175,7 +178,7 @@ public void execute( onExceptionalCallback)); Object p = context.getThreadManager().enter(); try { - interopLibrary.execute(call); + execute.getCallTarget().call(call); } finally { context.getThreadManager().leave(p); eventNodeFactory.ifPresent(EventBinding::dispose); @@ -233,7 +236,7 @@ public void execute( * Evaluates an expression in the scope of the provided module. * * @param module the module providing a scope for the expression - * @param expression the expression to evluated + * @param expression the expression to evaluated * @return a result of evaluation */ public Object evaluateExpression(Module module, String expression) @@ -241,7 +244,7 @@ public Object evaluateExpression(Module module, String expression) UnsupportedTypeException { Object p = context.getThreadManager().enter(); try { - return interopLibrary.invokeMember(module, MethodNames.Module.EVAL_EXPRESSION, expression); + return invoke.getCallTarget().call(module, expression); } finally { context.getThreadManager().leave(p); } @@ -255,7 +258,8 @@ public Object evaluateExpression(Module module, String expression) */ public String toDisplayString(Object receiver) { try { - return interopLibrary.asString(interopLibrary.toDisplayString(receiver)); + var iop = InteropLibrary.getUncached(); + return iop.asString(iop.toDisplayString(receiver)); } catch (UnsupportedMessageException ignored) { CompilerDirectives.shouldNotReachHere("Message support already checked."); } @@ -273,7 +277,7 @@ public Object callFunction(Object fn, Object argument) throws UnsupportedTypeException, ArityException, UnsupportedMessageException { Object p = context.getThreadManager().enter(); try { - return interopLibrary.execute(fn, argument); + return call.getCallTarget().call(fn, new Object[] { argument }); } finally { context.getThreadManager().leave(p); } @@ -321,7 +325,7 @@ public Object callFunctionWithInstrument( onExceptionalCallback)); Object p = context.getThreadManager().enter(); try { - return interopLibrary.execute(function, arguments); + return call.getCallTarget().call(function, arguments); } finally { context.getThreadManager().leave(p); eventNodeFactory.ifPresent(EventBinding::dispose); @@ -392,9 +396,10 @@ public void modifyModuleSources( * @return the associated language, or {@code null} if it doesn't exist */ public String getLanguage(Object o) { - if (interopLibrary.hasSourceLocation(o)) { + var iop = InteropLibrary.getUncached(o); + if (iop.hasSourceLocation(o)) { try { - var sourceSection = interopLibrary.getSourceLocation(o); + var sourceSection = iop.getSourceLocation(o); var source = sourceSection.getSource(); if (source != null) { return source.getLanguage(); @@ -413,9 +418,10 @@ public String getLanguage(Object o) { * @return the associated source section, or {@code null} if it doesn't exist */ public SourceSection getSourceLocation(Object o) { - if (interopLibrary.hasSourceLocation(o)) { + var iop = InteropLibrary.getUncached(o); + if (iop.hasSourceLocation(o)) { try { - return interopLibrary.getSourceLocation(o); + return iop.getSourceLocation(o); } catch (UnsupportedMessageException ignored) { CompilerDirectives.shouldNotReachHere("Message support already checked."); } @@ -430,17 +436,18 @@ public SourceSection getSourceLocation(Object o) { * @return a human-readable version of its contents. */ public String getExceptionMessage(PanicException panic) { - Object p = context.getThreadManager().enter(); + var iop = InteropLibrary.getUncached(); + var p = context.getThreadManager().enter(); try { - return interopLibrary.asString( - interopLibrary.invokeMember(panic.getPayload(), "to_display_text")); + return iop.asString( + iop.invokeMember(panic.getPayload(), "to_display_text")); } catch (UnsupportedMessageException | ArityException | UnknownIdentifierException | UnsupportedTypeException e) { return TypeToDisplayTextNodeGen.getUncached().execute(panic.getPayload()); } catch (Throwable e) { - if (interopLibrary.isException(e)) { + if (iop.isException(e)) { return TypeToDisplayTextNodeGen.getUncached().execute(panic.getPayload()); } else { throw e; @@ -449,4 +456,70 @@ public String getExceptionMessage(PanicException panic) { context.getThreadManager().leave(p); } } + + @SuppressWarnings("unchecked") + private static E raise(Class type, Exception ex) throws E { + throw (E) ex; + } + + private static final class ExecuteRootNode extends RootNode { + @Node.Child + private InteropLibrary iop = InteropLibrary.getFactory().createDispatched(5); + + ExecuteRootNode() { + super(null); + } + + @Override + public Object execute(VirtualFrame frame) { + try { + if (frame.getArguments()[0] instanceof FunctionCallInstrumentationNode.FunctionCall call) { + return iop.execute(call); + } + throw ArityException.create(1, 1, frame.getArguments().length); + } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException ex) { + throw raise(RuntimeException.class, ex); + } + } + } + + private static final class CallRootNode extends RootNode { + @Node.Child + private InteropLibrary iop = InteropLibrary.getFactory().createDispatched(5); + + CallRootNode() { + super(null); + } + + @Override + public Object execute(VirtualFrame frame) { + try { + var self = frame.getArguments()[0]; + var args = (Object[]) frame.getArguments()[1]; + return iop.execute(self, args); + } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException ex) { + throw raise(RuntimeException.class, ex); + } + } + } + + private static final class InvokeMemberRootNode extends RootNode { + @Node.Child + private InteropLibrary iop = InteropLibrary.getFactory().createDispatched(5); + + InvokeMemberRootNode() { + super(null); + } + + @Override + public Object execute(VirtualFrame frame) { + var module = frame.getArguments()[0]; + var expression = frame.getArguments()[1]; + try { + return iop.invokeMember(module, MethodNames.Module.EVAL_EXPRESSION, expression); + } catch (UnknownIdentifierException | UnsupportedTypeException | ArityException | UnsupportedMessageException ex) { + throw raise(RuntimeException.class, ex); + } + } + } } diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/instrument/execution/ErrorResolver.scala b/engine/runtime/src/main/scala/org/enso/interpreter/instrument/execution/ErrorResolver.scala index 0d3968ed01f8..86357b3d29aa 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/instrument/execution/ErrorResolver.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/instrument/execution/ErrorResolver.scala @@ -40,6 +40,8 @@ object ErrorResolver { x.getEncapsulatingSourceSection match { case null if x.getRootNode == null => None + case null if x.getRootNode.isInternal => + None case null => Some(Api.StackTraceElement(x.getRootNode.getName, None, None, None)) case section => diff --git a/test/Tests/src/Semantic/Error_Spec.enso b/test/Tests/src/Semantic/Error_Spec.enso index c6bf321dc315..61df78283a47 100644 --- a/test/Tests/src/Semantic/Error_Spec.enso +++ b/test/Tests/src/Semantic/Error_Spec.enso @@ -129,11 +129,11 @@ spec = Test.specify "should provide access to Java stack traces" <| stack_1 = Panic.recover Any (do_a_parse "foo") . stack_trace - stack_1.at 2 . name . should_equal "Error_Spec.do_a_parse" + stack_1.at 0 . name . should_equal "Error_Spec.do_a_parse" stack_2 = Panic.catch Any (do_a_parse "foo") caught_panic-> caught_panic.stack_trace - stack_2.at 2 . name . should_equal "Error_Spec.do_a_parse" + stack_2.at 0 . name . should_equal "Error_Spec.do_a_parse" Test.specify "should be able to be rethrown without changing the stack trace" <| caught_panic = Panic.catch Any throw_a_bar_panicking x->x @@ -189,7 +189,7 @@ spec = Panic.throw caught_panic.payload message_1.get . should_equal 'For input string: "foo"' caught_1.catch . should_be_a JException - caught_1.stack_trace.at 2 . name . should_equal "Error_Spec.do_a_parse" + caught_1.stack_trace.at 0 . name . should_equal "Error_Spec.do_a_parse" message_2 = Ref.new "" caught_2 = Panic.recover Any <|