Skip to content

Commit

Permalink
Switch to Truffle PE mode before calling into Enso (#5783)
Browse files Browse the repository at this point in the history
Enter partial evaluation mode via `CallTarget.call` before invoking `InteropLibrary`. Fixes #5782.
  • Loading branch information
JaroslavTulach authored Mar 3, 2023
1 parent 01fc34c commit f64edd8
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 29 deletions.
3 changes: 1 addition & 2 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Runtime.enso
Original file line number Diff line number Diff line change
Expand Up @@ -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<Function>.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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -57,9 +58,11 @@ public class ExecutionService {
private final EnsoContext context;
private final Optional<IdExecutionService> 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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -233,15 +236,15 @@ 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)
throws UnsupportedMessageException, ArityException, UnknownIdentifierException,
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);
}
Expand All @@ -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.");
}
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand All @@ -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.");
}
Expand All @@ -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;
Expand All @@ -449,4 +456,70 @@ public String getExceptionMessage(PanicException panic) {
context.getThreadManager().leave(p);
}
}

@SuppressWarnings("unchecked")
private static <E extends Exception> E raise(Class<E> 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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
6 changes: 3 additions & 3 deletions test/Tests/src/Semantic/Error_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <|
Expand Down

0 comments on commit f64edd8

Please sign in to comment.