diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Text/Regex/Engine/Default.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Text/Regex/Engine/Default.enso index 74eed9fe0439..3415b34afdf7 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Text/Regex/Engine/Default.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Text/Regex/Engine/Default.enso @@ -122,7 +122,7 @@ type Engine internal_pattern = maybe_java_pattern.map_error case _ of Polyglot_Error err -> - if Java.is_instance err PatternSyntaxException . not then err else + if err.has_type PatternSyntaxException . not then err else Regex.Syntax_Error err.getMessage other -> other @@ -823,8 +823,8 @@ type Match handle_error : Any -> (Text | Integer) -> Any handle_error error id = case error of Polyglot_Error err -> - is_ioob = Java.is_instance err IndexOutOfBoundsException - is_iae = Java.is_instance err IllegalArgumentException + is_ioob = err.has_type IndexOutOfBoundsException + is_iae = err.has_type IllegalArgumentException maps_to_no_such_group = is_ioob || is_iae if maps_to_no_such_group.not then err else diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso index eeb592489570..c727dffb36cb 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso @@ -65,8 +65,8 @@ new year (month = 1) (day = 1) = https://github.com/enso-org/enso/pull/3559 Then this should be switched to use `Panic.catch_java`. Panic.recover Any (Date.internal_new year month day) . catch Any e-> case e of - Polyglot_Error err -> Error.throw (Time.Time_Error err) - ex -> Error.throw (Time.Time_Error ex) + Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage) + ex -> ex ## ALIAS Date from Text @@ -137,8 +137,8 @@ parse text pattern=Nothing = Text -> Date.internal_parse text pattern _ -> Panic.throw (Time.Time_Error "An invalid pattern was provided.") result . map_error <| case _ of - Polyglot_Error err -> Time.Time_Error err - ex -> Time.Time_Error ex + Polyglot_Error err -> Time.Time_Error err.getMessage + ex -> ex type Date diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso index 09af7617ddf1..7f506b253b63 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso @@ -362,7 +362,7 @@ type Panic False -> Panic.throw caught_panic True -> case caught_panic.payload of Polyglot_Error java_exception -> - case Java.is_instance java_exception panic_type of + case java_exception.has_type panic_type of True -> handler caught_panic False -> Panic.throw caught_panic _ -> Panic.throw caught_panic @@ -392,7 +392,7 @@ type Panic catch_java panic_type ~action handler = Panic.catch_primitive action caught_panic-> case caught_panic.payload of Polyglot_Error java_exception -> - case (panic_type == Any) || (Java.is_instance java_exception panic_type) of + case (panic_type == Any) || (java_exception.has_type panic_type) of True -> handler java_exception False -> Panic.throw caught_panic _ -> Panic.throw caught_panic diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso index 9c82b6d664f0..3effcadc0020 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso @@ -955,10 +955,10 @@ handle_java_exceptions file ~action = Converts a Java `IOException` into its Enso counterpart. wrap_io_exception file io_exception = - if Java.is_instance io_exception NoSuchFileException then Error.throw (File_Not_Found file) else - if Java.is_instance io_exception FileAlreadyExistsException then Error.throw (File_Already_Exists_Error file) else - if Java.is_instance io_exception AccessDeniedException then Error.throw (Io_Error file "You do not have permission to access the file") else - Error.throw (Io_Error file "An IO error has occurred: "+io_exception.getMessage) + if io_exception.has_type NoSuchFileException then Error.throw (File_Not_Found file) else + if io_exception.has_type FileAlreadyExistsException then Error.throw (File_Already_Exists_Error file) else + if io_exception.has_type AccessDeniedException then Error.throw (Io_Error file "You do not have permission to access the file") else + Error.throw (Io_Error file "An IO error has occurred: "+io_exception.to_text) ## PRIVATE diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetPolyglotLanguageNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetPolyglotLanguageNode.java index b5e1efada047..45052a96aa70 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetPolyglotLanguageNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetPolyglotLanguageNode.java @@ -4,6 +4,7 @@ import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.builtin.Error; import org.enso.interpreter.runtime.data.text.Text; @BuiltinMethod( diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java index 54b568a0f047..11b3e002d45d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java @@ -93,7 +93,7 @@ public Builtins(Context context) { builtinMethodNodes = readBuiltinMethodsMetadata(scope); registerBuiltinMethods(builtinTypes, scope, language); - error = new Error(this); + error = new Error(this, context); ordering = new Ordering(this); system = new System(this); number = new Number(this); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java index 4e9251991cf8..4893ef95b9eb 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java @@ -11,11 +11,21 @@ import org.enso.interpreter.runtime.data.text.Text; import static com.oracle.truffle.api.CompilerDirectives.transferToInterpreterAndInvalidate; +import com.oracle.truffle.api.exception.AbstractTruffleException; +import com.oracle.truffle.api.interop.ArityException; +import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; +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.library.CachedLibrary; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; +import org.enso.interpreter.runtime.Context; /** Container for builtin Error types */ public class Error { - + private final Context context; private final BuiltinAtomConstructor syntaxError; private final BuiltinAtomConstructor typeError; private final BuiltinAtomConstructor compileError; @@ -43,7 +53,8 @@ public class Error { private static final Text divideByZeroMessage = Text.create("Cannot divide by zero."); /** Creates builders for error Atom Constructors. */ - public Error(Builtins builtins) { + public Error(Builtins builtins, Context context) { + this.context = context; syntaxError = new BuiltinAtomConstructor(builtins, SyntaxError.class); typeError = new BuiltinAtomConstructor(builtins, TypeError.class); compileError = new BuiltinAtomConstructor(builtins, CompileError.class); @@ -133,15 +144,8 @@ public Atom makeTypeError(Object expected, Object actual, String name) { * @param cause the cause of the error. * @return a runtime representation of the polyglot error. */ - public Atom makePolyglotError(Object cause) { - return polyglotError.newInstance(switch (cause) { - case TruffleObject truffle -> truffle; - case Throwable any -> any.getMessage(); - default -> { - CompilerDirectives.transferToInterpreter(); - throw new IllegalStateException("" + cause); - } - }); + public Atom makePolyglotError(Throwable cause) { + return polyglotError.newInstance(WrapPlainException.wrap(cause, context)); } /** @@ -215,4 +219,112 @@ public Atom makeModuleDoesNotExistError(String name) { public Atom makeNotInvokableError(Object target) { return notInvokableError.newInstance(target); } + + /** Represents plain Java exception as a {@link TruffleObject}. + */ + @ExportLibrary(InteropLibrary.class) + static final class WrapPlainException extends AbstractTruffleException { + private final Throwable original; + + private WrapPlainException(Throwable cause) { + super(cause.getMessage(), cause, AbstractTruffleException.UNLIMITED_STACK_TRACE, null); + this.original = cause; + } + + private WrapPlainException(AbstractTruffleException prototype, Throwable original) { + super(prototype); + this.original = original; + } + + static AbstractTruffleException wrap(Throwable cause, Context ctx) { + var env = ctx.getEnvironment(); + if (env.isHostException(cause)) { + var orig = env.asHostException(cause); + return new WrapPlainException((AbstractTruffleException) cause, orig); + } else if (cause instanceof AbstractTruffleException truffleEx) { + return truffleEx; + } else { + return new WrapPlainException(cause); + } + } + + @ExportMessage + boolean hasExceptionMessage() { + return getMessage() != null; + } + + @ExportMessage + public Object getExceptionMessage() { + return Text.create(getMessage()); + } + + @ExportMessage + String toDisplayString(boolean sideEffects) { + return original.toString(); + } + + @ExportMessage + Object getMembers(boolean includeInternal) { + return Array.empty(); + } + + @ExportMessage + boolean hasMembers() { + return true; + } + + @ExportMessage + boolean isMemberInvocable(String member) { + return + "has_type".equals(member) || + "getMessage".equals(member); + } + + @ExportMessage + Object invokeMember(String name, Object[] args, @CachedLibrary(limit="2") InteropLibrary iop) throws ArityException, UnknownIdentifierException, UnsupportedTypeException, UnsupportedMessageException { + if ("has_type".equals(name)) { + if (args.length != 1) { + throw ArityException.create(1,1, args.length); + } + Object meta; + if (iop.isString(args[0])) { + meta = args[0]; + } else { + try { + meta = iop.getMetaQualifiedName(args[0]); + } catch (UnsupportedMessageException e) { + meta = args[0]; + } + } + if (!iop.isString(meta)) { + throw UnsupportedTypeException.create(args, "Provide class or fully qualified name of class to check"); + } + + return hasType(iop.asString(meta), original.getClass()); + } + if ("getMessage".equals(name)) { + return getExceptionMessage(); + } + throw UnknownIdentifierException.create(name); + } + + @CompilerDirectives.TruffleBoundary + private static boolean hasType(String fqn, Class type) { + if (type == null) { + return false; + } + if (type.getName().equals(fqn)) { + return true; + } + if (hasType(fqn, type.getSuperclass())) { + return true; + } + for (Class interfaceType : type.getInterfaces()) { + if (hasType(fqn, interfaceType)) { + return true; + } + } + return false; + } + } }