mirror of
https://github.com/enso-org/enso.git
synced 2024-09-11 21:27:15 +03:00
Catch Polyglot Exceptions (#1200)
`Panic.recover` catches host exceptions wrapping them in `Polyglot_Error` atom constructor.
This commit is contained in:
parent
a2d3b9fe01
commit
1fbf3ad692
@ -1,9 +1,14 @@
|
||||
package org.enso.interpreter.node.expression.builtin.error;
|
||||
|
||||
import com.oracle.truffle.api.TruffleException;
|
||||
import com.oracle.truffle.api.dsl.CachedContext;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.error.RuntimeError;
|
||||
@ -13,14 +18,32 @@ import org.enso.interpreter.runtime.state.Stateful;
|
||||
type = "Panic",
|
||||
name = "catch",
|
||||
description = "Executes an action and converts any Panic thrown by it into an Error")
|
||||
public class CatchPanicNode extends Node {
|
||||
public abstract class CatchPanicNode extends Node {
|
||||
private @Child ThunkExecutorNode thunkExecutorNode = ThunkExecutorNode.build();
|
||||
|
||||
Stateful execute(@MonadicState Object state, Object _this, Thunk action) {
|
||||
static CatchPanicNode build() {
|
||||
return CatchPanicNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Stateful execute(@MonadicState Object state, Object _this, Thunk action);
|
||||
|
||||
@Specialization
|
||||
Stateful doExecute(
|
||||
@MonadicState Object state,
|
||||
Object _this,
|
||||
Thunk action,
|
||||
@CachedContext(Language.class) Context ctx) {
|
||||
try {
|
||||
return thunkExecutorNode.executeThunk(action, state, false);
|
||||
} catch (PanicException e) {
|
||||
return new Stateful(state, new RuntimeError(e.getExceptionObject()));
|
||||
} catch (Throwable e) {
|
||||
if (ctx.getEnvironment().isHostException(e)) {
|
||||
Object cause = ((TruffleException) e).getExceptionObject();
|
||||
return new Stateful(
|
||||
state, new RuntimeError(ctx.getBuiltins().error().makePolyglotError(cause)));
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ public class Error {
|
||||
private final AtomConstructor inexhaustivePatternMatchError;
|
||||
private final AtomConstructor uninitializedState;
|
||||
private final AtomConstructor noSuchMethodError;
|
||||
private final AtomConstructor polyglotError;
|
||||
|
||||
/**
|
||||
* Creates and registers the relevant constructors.
|
||||
@ -43,12 +44,17 @@ public class Error {
|
||||
.initializeFields(
|
||||
new ArgumentDefinition(0, "target", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "method_name", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
polyglotError =
|
||||
new AtomConstructor("Polyglot_Error", scope)
|
||||
.initializeFields(
|
||||
new ArgumentDefinition(0, "cause", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
|
||||
scope.registerConstructor(syntaxError);
|
||||
scope.registerConstructor(compileError);
|
||||
scope.registerConstructor(inexhaustivePatternMatchError);
|
||||
scope.registerConstructor(uninitializedState);
|
||||
scope.registerConstructor(noSuchMethodError);
|
||||
scope.registerConstructor(polyglotError);
|
||||
}
|
||||
|
||||
/** @return the builtin {@code Syntax_Error} atom constructor. */
|
||||
@ -81,4 +87,14 @@ public class Error {
|
||||
public Atom makeNoSuchMethodError(Object target, String name) {
|
||||
return noSuchMethodError.newInstance(target, Text.create(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the runtime representation of a {@code Polyglot_Error}.
|
||||
*
|
||||
* @param cause the cause of the error.
|
||||
* @return a runtime representation of the polyglot error.
|
||||
*/
|
||||
public Atom makePolyglotError(Object cause) {
|
||||
return polyglotError.newInstance(cause);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.enso.interpreter.runtime.callable.atom;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.dsl.Bind;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.interop.*;
|
||||
@ -120,6 +121,7 @@ public class Atom implements TruffleObject {
|
||||
|
||||
@ExportMessage
|
||||
static class InvokeMember {
|
||||
|
||||
static UnresolvedSymbol buildSym(AtomConstructor cons, String name) {
|
||||
return UnresolvedSymbol.build(name, cons.getDefinitionScope());
|
||||
}
|
||||
@ -130,11 +132,9 @@ public class Atom implements TruffleObject {
|
||||
Atom receiver,
|
||||
String member,
|
||||
Object[] arguments,
|
||||
@Cached(value = "receiver.getConstructor()", allowUncached = true)
|
||||
AtomConstructor cachedConstructor,
|
||||
@Cached(value = "member", allowUncached = true) String cachedMember,
|
||||
@Cached(value = "buildSym(cachedConstructor, cachedMember)", allowUncached = true)
|
||||
UnresolvedSymbol cachedSym,
|
||||
@Cached(value = "receiver.getConstructor()") AtomConstructor cachedConstructor,
|
||||
@Cached(value = "member") String cachedMember,
|
||||
@Cached(value = "buildSym(cachedConstructor, cachedMember)") UnresolvedSymbol cachedSym,
|
||||
@CachedLibrary("cachedSym") InteropLibrary symbols)
|
||||
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
|
||||
Object[] args = new Object[arguments.length + 1];
|
||||
@ -142,6 +142,18 @@ public class Atom implements TruffleObject {
|
||||
System.arraycopy(arguments, 0, args, 1, arguments.length);
|
||||
return symbols.execute(cachedSym, args);
|
||||
}
|
||||
|
||||
@Specialization(replaces = "doCached")
|
||||
static Object doUncached(
|
||||
Atom receiver,
|
||||
String member,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "1") InteropLibrary symbols)
|
||||
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
|
||||
UnresolvedSymbol symbol = buildSym(receiver.getConstructor(), member);
|
||||
return doCached(
|
||||
receiver, member, arguments, receiver.getConstructor(), member, symbol, symbols);
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
|
@ -133,5 +133,28 @@ class ErrorsTest extends InterpreterTest {
|
||||
val code = "main = 10.catch (x -> x + 1)"
|
||||
eval(code) shouldEqual 10
|
||||
}
|
||||
|
||||
"catch polyglot errors" in {
|
||||
val code =
|
||||
"""from Builtins import all
|
||||
|polyglot java import java.lang.Long
|
||||
|
|
||||
|main =
|
||||
| caught = Panic.recover (Long.parseLong (Array.new_1 "oops"))
|
||||
| IO.println caught
|
||||
| cause = caught.catch <| case _ of
|
||||
| Polyglot_Error err -> err
|
||||
| _ -> "fail"
|
||||
| IO.println cause
|
||||
| message = cause.getMessage (Array.new 0)
|
||||
| IO.println message
|
||||
|""".stripMargin
|
||||
eval(code)
|
||||
consumeOut shouldEqual List(
|
||||
"""(Error: (Polyglot_Error java.lang.NumberFormatException: For input string: "oops"))""",
|
||||
"""java.lang.NumberFormatException: For input string: "oops"""",
|
||||
"""For input string: "oops""""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user