mirror of
https://github.com/enso-org/enso.git
synced 2024-12-27 20:33:30 +03:00
The library developer should be able to handle specific types of Panics while passing through others (#3344)
Implements https://www.pivotaltracker.com/story/show/181569176 Also ensures that Dataflow Errors have proper stack traces (earlier they did not point at the right location).
This commit is contained in:
parent
466f1e29c3
commit
cc7333812d
@ -71,6 +71,8 @@
|
||||
also compute mode, percentile, minimum, maximum.][3318]
|
||||
- [Implemented `Text.location_of` and `Text.location_of_all` methods.][3324]
|
||||
- [Replaced `Table.group_by` with `Table.aggregate`][3339]
|
||||
- [Implemented `Panic.catch` and helper functions for handling errors. Added a
|
||||
type parameter to `Panic.recover` to recover specific types of errors.][3344]
|
||||
|
||||
[debug-shortcuts]:
|
||||
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
|
||||
@ -113,6 +115,7 @@
|
||||
[3324]: https://github.com/enso-org/enso/pull/3324
|
||||
[3327]: https://github.com/enso-org/enso/pull/3327
|
||||
[3339]: https://github.com/enso-org/enso/pull/3339
|
||||
[3344]: https://github.com/enso-org/enso/pull/3344
|
||||
|
||||
#### Enso Compiler
|
||||
|
||||
|
@ -38,10 +38,8 @@ from_pairs contents =
|
||||
example_parse = Json.parse '{ "a": 1 }'
|
||||
parse : Text -> Json ! Parse_Error
|
||||
parse json_text =
|
||||
r = Panic.recover (Internal.parse_helper json_text)
|
||||
r.catch e-> case e of
|
||||
Polyglot_Error err -> Error.throw (Parse_Error err.getMessage)
|
||||
p -> Panic.throw p
|
||||
Panic.catch Polyglot_Error (Internal.parse_helper json_text) caught_panic->
|
||||
Error.throw (Parse_Error caught_panic.payload.cause.getMessage)
|
||||
|
||||
## Represents a JSON structure.
|
||||
type Json
|
||||
@ -104,7 +102,7 @@ type Json
|
||||
json.into format
|
||||
into : Any -> Any ! Marshalling_Error
|
||||
into type_descriptor =
|
||||
Panic.recover (Internal.into_helper type_descriptor this)
|
||||
Panic.recover Any (Internal.into_helper type_descriptor this)
|
||||
|
||||
## Returns this Json object.
|
||||
|
||||
|
@ -4,6 +4,7 @@ polyglot java import java.lang.Long
|
||||
polyglot java import java.lang.Double
|
||||
polyglot java import java.lang.Math
|
||||
polyglot java import java.lang.String
|
||||
polyglot java import java.lang.NumberFormatException
|
||||
|
||||
## ALIAS Inverse Sine
|
||||
|
||||
@ -268,7 +269,7 @@ Number.to_json = Json.Number this
|
||||
Integer.parse "20220216"
|
||||
Integer.parse : Text -> Text -> Integer ! Parse_Error
|
||||
Integer.parse text (radix=10) =
|
||||
Panic.recover (Long.parseLong text radix) . catch _->
|
||||
Panic.catch NumberFormatException (Long.parseLong text radix) _->
|
||||
Error.throw (Parse_Error text)
|
||||
|
||||
## ALIAS From Text
|
||||
@ -285,7 +286,7 @@ Integer.parse text (radix=10) =
|
||||
Decimal.parse "7.6"
|
||||
Decimal.parse : Text -> Decimal ! Parse_Error
|
||||
Decimal.parse text =
|
||||
Panic.recover (Double.parseDouble text) . catch _->
|
||||
Panic.catch NumberFormatException (Double.parseDouble text) _->
|
||||
Error.throw (Parse_Error text)
|
||||
|
||||
## UNSTABLE
|
||||
|
@ -261,7 +261,7 @@ type Matcher
|
||||
Regex_Matcher
|
||||
|
||||
## PRIVATE
|
||||
match_criteria_implementation matcher objects criteria reorder=False name_mapper=(x->x) on_problems=Report_Warning = Panic.recover <|
|
||||
match_criteria_implementation matcher objects criteria reorder=False name_mapper=(x->x) on_problems=Report_Warning = Panic.recover Any <|
|
||||
[matcher, objects, criteria, reorder, name_mapper, on_problems] . each Panic.rethrow
|
||||
|
||||
# match_matrix . at i . at j specifies whether objects.at i matches criteria.at j
|
||||
|
@ -120,7 +120,7 @@ type Engine
|
||||
options_bitmask = here.from_enso_options all_options
|
||||
unicode_regex = UnicodeRegex.new
|
||||
|
||||
maybe_java_pattern = Panic.recover <|
|
||||
maybe_java_pattern = Panic.recover Any <|
|
||||
Java_Pattern.compile (unicode_regex.transform expression) options_bitmask
|
||||
|
||||
internal_pattern = maybe_java_pattern.map_error case _ of
|
||||
@ -617,7 +617,7 @@ type Match
|
||||
match.group "letters"
|
||||
group : Integer | Text -> Text | Nothing ! Regex.No_Such_Group_Error
|
||||
group id =
|
||||
Panic.recover (this.internal_match.group id) . map_error (here.handle_error _ id)
|
||||
Panic.recover Any (this.internal_match.group id) . map_error (here.handle_error _ id)
|
||||
|
||||
## Gets a vector containing the results of _all_ of the capturing groups in
|
||||
the pattern, replacing the value of groups that did not participate in
|
||||
@ -703,7 +703,7 @@ type Match
|
||||
match.start 0
|
||||
start : Integer | Text -> Integer | Nothing ! Regex.No_Such_Group_Error
|
||||
start id =
|
||||
result = Panic.recover (this.internal_match.start id)
|
||||
result = Panic.recover Any (this.internal_match.start id)
|
||||
no_errors = result.map_error (here.handle_error _ id)
|
||||
if no_errors == -1 then Nothing else no_errors
|
||||
|
||||
@ -731,7 +731,7 @@ type Match
|
||||
match.end 0
|
||||
end : Integer | Text -> Integer | Nothing ! Regex.No_Such_Group_Error
|
||||
end id =
|
||||
result = Panic.recover (this.internal_match.end id)
|
||||
result = Panic.recover Any (this.internal_match.end id)
|
||||
no_errors = result.map_error (here.handle_error _ id)
|
||||
if no_errors == -1 then Nothing else no_errors
|
||||
|
||||
@ -854,7 +854,7 @@ type Option
|
||||
- opts: The enso-side options to configure the regex.
|
||||
from_enso_options : Vector.Vector (Option | Global_Option.Option) -> Integer
|
||||
from_enso_options opts =
|
||||
java_flags = Panic.recover <| opts.flat_map case _ of
|
||||
java_flags = Panic.recover Any <| opts.flat_map case _ of
|
||||
Literal_Pattern -> [Java_Pattern.LITERAL]
|
||||
Unix_Lines -> [Java_Pattern.UNIX_LINES]
|
||||
Global_Option.Case_Insensitive -> [Java_Pattern.CASE_INSENSITIVE]
|
||||
|
@ -56,7 +56,7 @@ now = Time ZonedDateTime.now
|
||||
example_new = Time.new 1986 8 5
|
||||
new : Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Zone -> Time ! Time_Error
|
||||
new year (month = 1) (day = 1) (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) (zone = Zone.system) =
|
||||
Panic.recover (Time (ZonedDateTime.of year month day hour minute second nanosecond zone.internal_zone_id)) . catch e-> case e of
|
||||
Panic.recover Any (Time (ZonedDateTime.of year month day hour minute second nanosecond zone.internal_zone_id)) . catch e-> case e of
|
||||
Polyglot_Error err -> Error.throw (Time_Error err.getMessage)
|
||||
x -> x
|
||||
|
||||
@ -141,7 +141,7 @@ new year (month = 1) (day = 1) (hour = 0) (minute = 0) (second = 0) (nanosecond
|
||||
Date.parse "06 of May 2020 at 04:30AM" "dd 'of' MMMM yyyy 'at' hh:mma"
|
||||
parse : Text -> Text | Nothing -> Locale -> Time ! Time_Error
|
||||
parse text pattern=Nothing locale=Locale.default =
|
||||
result = Panic.recover <| case pattern of
|
||||
result = Panic.recover Any <| case pattern of
|
||||
Nothing -> Time_Utils.parse_time text
|
||||
Text -> Time_Utils.parse_time_format text pattern locale.java_locale
|
||||
_ -> Panic.throw (Time_Error "An invalid pattern was provided.")
|
||||
|
@ -60,7 +60,7 @@ today = here.now
|
||||
|
||||
new : Integer -> Integer -> Integer -> Date ! Time.Time_Error
|
||||
new year (month = 1) (day = 1) =
|
||||
Panic.recover (Date (LocalDate.of year month day)) . catch e-> case e of
|
||||
Panic.recover Any (Date (LocalDate.of year month day)) . catch e-> case e of
|
||||
Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage)
|
||||
x -> x
|
||||
|
||||
@ -129,7 +129,7 @@ new year (month = 1) (day = 1) =
|
||||
Time.Time_Error _ -> Date.new 2000 1 1
|
||||
parse : Text -> (Text | Nothing) -> Date ! Time.Time_Error
|
||||
parse text pattern=Nothing =
|
||||
result = Panic.recover <| case pattern of
|
||||
result = Panic.recover Any <| case pattern of
|
||||
Nothing -> LocalDate.parse text
|
||||
Text -> LocalDate.parse text (DateTimeFormatter.ofPattern pattern)
|
||||
_ -> Panic.throw (Time.Time_Error "An invalid pattern was provided.")
|
||||
|
@ -48,7 +48,7 @@ now = Time_Of_Day LocalTime.now
|
||||
example_epoch = Time_Of_Day.new hour=9 minute=30
|
||||
new : Integer -> Integer -> Integer -> Integer -> Time_Of_Day ! Time.Time_Error
|
||||
new (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) =
|
||||
Panic.recover (Time_Of_Day (LocalTime.of hour minute second nanosecond)) . catch e-> case e of
|
||||
Panic.recover Any (Time_Of_Day (LocalTime.of hour minute second nanosecond)) . catch e-> case e of
|
||||
Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage)
|
||||
x -> x
|
||||
|
||||
@ -115,7 +115,7 @@ new (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) =
|
||||
example_parse = Time_Of_Day.parse "4:30AM" "h:mma"
|
||||
parse : Text -> Text | Nothing -> Locale -> Time_Of_Day ! Time.Time_Error
|
||||
parse text pattern=Nothing locale=Locale.default =
|
||||
result = Panic.recover <| case pattern of
|
||||
result = Panic.recover Any <| case pattern of
|
||||
Nothing -> LocalTime.parse text
|
||||
Text ->
|
||||
formatter = DateTimeFormatter.ofPattern pattern
|
||||
|
@ -233,7 +233,7 @@ type Vector
|
||||
[0, 1, 2].sum
|
||||
sum : Any ! (Empty_Error | No_Such_Method_Error)
|
||||
sum =
|
||||
result = Panic.recover <| this.reduce (+)
|
||||
result = Panic.recover Any <| this.reduce (+)
|
||||
result.map_error x->case x of
|
||||
No_Such_Method_Error _ _ -> x
|
||||
Empty_Error -> x
|
||||
@ -938,7 +938,7 @@ type Vector
|
||||
|
||||
More details on the HashCode / HashMap ticket https://www.pivotaltracker.com/story/show/181027272.
|
||||
|
||||
recovered = Panic.recover
|
||||
recovered = Panic.recover Any
|
||||
builder = here.new_builder
|
||||
this.fold Map.empty existing->
|
||||
item->
|
||||
|
@ -1,6 +1,7 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Runtime.Extensions as Runtime_Extensions
|
||||
import Standard.Builtins
|
||||
from Standard.Base.Runtime.Extensions as Runtime_Extensions import Stack_Trace_Element
|
||||
|
||||
## ADVANCED
|
||||
UNSTABLE
|
||||
@ -125,14 +126,31 @@ Error.map_error f = this.catch (x -> Error.throw (f x))
|
||||
## ADVANCED
|
||||
UNSTABLE
|
||||
|
||||
Returns the attached stack trace of the error. The ordering of the resulting
|
||||
vector is such that the top stack frame is the first element.
|
||||
Error.get_stack_trace : Vector.Vector Stack_Trace_Element
|
||||
Error.get_stack_trace =
|
||||
prim_stack = this.primitive_get_stack_trace
|
||||
Returns the attached stack trace of the given throwable. Can be used to get
|
||||
an Enso friendly stack trace from native Java exceptions.
|
||||
|
||||
The ordering of the resulting vector is such that the top stack frame is the
|
||||
first element.
|
||||
Panic.get_attached_stack_trace : Caught_Panic | Throwable -> Vector.Vector Stack_Trace_Element
|
||||
Panic.get_attached_stack_trace error =
|
||||
throwable = case error of
|
||||
Caught_Panic _ internal_original_exception -> internal_original_exception
|
||||
throwable -> throwable
|
||||
prim_stack = Panic.primitive_get_attached_stack_trace throwable
|
||||
stack_with_prims = Vector.Vector prim_stack
|
||||
stack_with_prims.map Runtime_Extensions.wrap_primitive_stack_trace_element
|
||||
|
||||
## ADVANCED
|
||||
UNSTABLE
|
||||
|
||||
Returns the attached stack trace of the error.
|
||||
|
||||
The ordering of the resulting vector is such that the top stack frame is the
|
||||
first element.
|
||||
Error.stack_trace : Vector.Vector Stack_Trace_Element
|
||||
Error.stack_trace =
|
||||
Panic.get_attached_stack_trace this
|
||||
|
||||
## Checks if `this` is an error.
|
||||
|
||||
> Example
|
||||
@ -156,3 +174,95 @@ Error.is_error = True
|
||||
example_rethrow = Panic.rethrow Examples.throw_error
|
||||
Panic.rethrow : (Any ! Any) -> Any
|
||||
Panic.rethrow value = value.catch Panic.throw
|
||||
|
||||
## Returns the stack trace of the caught panic.
|
||||
Caught_Panic.stack_trace : Vector.Vector Stack_Trace_Element
|
||||
Caught_Panic.stack_trace =
|
||||
Panic.get_attached_stack_trace this
|
||||
|
||||
## Executes the provided action and if a panic matching the provided type was
|
||||
thrown, calls the provided callback.
|
||||
|
||||
If action executes successfully, the result of `Panic.catch` is the result of
|
||||
that action. Otherwise, if a matching panic is thrown from within the action,
|
||||
the result is obtained by calling the provided handler callback. Any
|
||||
non-matching panics are forwarded without changes.
|
||||
|
||||
Arguments:
|
||||
- panic_type: The expected panic type. It can either be an Enso type or a
|
||||
Java class. If the Java class is provided, `Polyglot_Error` containing a
|
||||
Java exception of this class will be matched.
|
||||
- action: The code to execute that potentially panics.
|
||||
- handler: The callback to handle the panics. The callback will be provided
|
||||
with a `Caught_Panic` instance encapsulating the `payload` of the caught
|
||||
panic and its stacktrace.
|
||||
|
||||
> Example
|
||||
Handling a specific type of panic.
|
||||
|
||||
Panic.catch Illegal_Argument_Error (Panic.throw (Illegal_Argument_Error "Oh no!" Nothing)) error->
|
||||
"Caught an `Illegal_Argument_Error`: "+error.payload.message
|
||||
|
||||
> Example
|
||||
Handling any panic.
|
||||
|
||||
Panic.catch Any (Panic.throw (Illegal_Argument_Error "Oh no!" Nothing)) error->
|
||||
"Caught some panic!"
|
||||
|
||||
> Example
|
||||
Convert a string to an integer, catching the Java `NumberFormatException`
|
||||
and converting it to a more Enso-friendly dataflow error.
|
||||
|
||||
polyglot java import java.lang.Long
|
||||
polyglot java import java.lang.NumberFormatException
|
||||
parse str =
|
||||
Panic.catch NumberFormatException (Long.parseLong str) caught_panic->
|
||||
Error.throw (Illegal_Argument_Error "The provided string is not a valid number: "+caught_panic.payload.cause.getMessage)
|
||||
Panic.catch : Any -> Any -> (Caught_Panic -> Any) -> Any
|
||||
Panic.catch panic_type ~action handler =
|
||||
Panic.catch_primitive action caught_panic->
|
||||
case Builtins.Meta.get_polyglot_language panic_type == "java" of
|
||||
False -> case caught_panic.payload.is_a panic_type of
|
||||
True -> handler caught_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
|
||||
True -> handler caught_panic
|
||||
False -> Panic.throw caught_panic
|
||||
_ -> Panic.throw caught_panic
|
||||
|
||||
## Executes the provided action and converts a possible panic matching any of
|
||||
the provided types into a dataflow Error.
|
||||
|
||||
If action executes successfully, the result of `Panic.recover` is the result
|
||||
of that action. Otherwise, if it panicked with a type matching one of the
|
||||
expected error types, that panic is returned as a dataflow error. Unexpected
|
||||
panics are passed through as-is. it is the panic that was thrown after
|
||||
conversion to a dataflow error.
|
||||
|
||||
Arguments:
|
||||
- expected_types: The types of expected panics which should be recovered.
|
||||
This can either be a Vector of types or a single type.
|
||||
- action: The code to execute that potentially panics.
|
||||
|
||||
> Example
|
||||
Converting an expected panic to a dataflow error.
|
||||
|
||||
Panic.recover Illegal_Argument_Error (Panic.throw (Illegal_Argument_Error "Oh!" Nothing))
|
||||
|
||||
> Example
|
||||
Converting one of many expected panic types to a dataflow error.
|
||||
|
||||
Panic.recover [Illegal_Argument_Error, Illegal_State_Error] (Panic.throw (Illegal_Argument_Error "Oh!" Nothing))
|
||||
Panic.recover : (Vector.Vector Any | Any) -> Any -> Any
|
||||
Panic.recover expected_types ~action =
|
||||
types_to_check = case expected_types of
|
||||
Vector.Vector _ -> expected_types
|
||||
_ -> [expected_types]
|
||||
Panic.catch Any action caught_panic->
|
||||
is_matched = types_to_check.exists typ->
|
||||
caught_panic.payload.is_a typ
|
||||
case is_matched of
|
||||
True -> caught_panic.convert_to_dataflow_error
|
||||
False -> Panic.throw caught_panic
|
||||
|
@ -554,7 +554,7 @@ type Http
|
||||
http.request req
|
||||
request : Request -> Response ! Request_Error
|
||||
request req =
|
||||
response = Panic.recover <|
|
||||
response = Panic.recover Any <|
|
||||
body_publishers = HttpRequest.BodyPublishers
|
||||
builder = HttpRequest.newBuilder
|
||||
# set uri
|
||||
|
@ -26,7 +26,7 @@ import Standard.Base.System.File
|
||||
example_new = Request.new Method.Post (Uri.parse "http://example.com")
|
||||
new : Method -> (Text | Uri) -> Vector.Vector -> Request_Body -> Request
|
||||
new method addr (headers = []) (body = Request_Body.Empty) =
|
||||
Panic.recover (Request method (Internal.panic_on_error (addr.to_uri)) headers body) . catch Internal.recover_panic
|
||||
Panic.recover Any (Request method (Internal.panic_on_error (addr.to_uri)) headers body) . catch Internal.recover_panic
|
||||
|
||||
## Create an Options request.
|
||||
|
||||
|
@ -22,7 +22,7 @@ polyglot java import java.util.Optional
|
||||
example_parse = Uri.parse "http://example.com"
|
||||
parse : Text -> Uri ! Syntax_Error
|
||||
parse text =
|
||||
Panic.recover (Uri (Java_URI.create text)) . catch e-> case e of
|
||||
Panic.recover Any (Uri (Java_URI.create text)) . catch e-> case e of
|
||||
Polyglot_Error ex -> Error.throw (Syntax_Error ex.getMessage)
|
||||
other -> Panic.throw other
|
||||
|
||||
|
@ -817,7 +817,7 @@ rethrow_java file exception =
|
||||
|
||||
Utility method for running an action with Java exceptions mapping.
|
||||
handle_java_exceptions file ~action =
|
||||
err = Panic.recover action
|
||||
err = Panic.recover Any action
|
||||
r = err.catch (here.rethrow_java file)
|
||||
r
|
||||
|
||||
|
@ -96,7 +96,7 @@ type Connection
|
||||
execute_update : Text | Sql.Statement -> Integer
|
||||
execute_update query = here.wrap_sql_errors <|
|
||||
Resource.bracket (this.prepare_statement query) .close stmt->
|
||||
result = Panic.recover stmt.executeLargeUpdate
|
||||
result = Panic.recover Any stmt.executeLargeUpdate
|
||||
result.catch err-> case err of
|
||||
Polyglot_Error exc ->
|
||||
case Java.is_instance exc UnsupportedOperationException of
|
||||
@ -115,7 +115,7 @@ type Connection
|
||||
prepare_statement query =
|
||||
go template holes=[] = Managed_Resource.with this.connection_resource java_connection->
|
||||
stmt = java_connection.prepareStatement template
|
||||
setup_error = Panic.recover <|
|
||||
setup_error = Panic.recover Any <|
|
||||
holes.map_with_index ix-> obj->
|
||||
position = ix + 1
|
||||
case obj.first of
|
||||
@ -345,7 +345,7 @@ type Sql_Timeout_Error
|
||||
- action: The computation to execute. This computation may throw SQL errors.
|
||||
wrap_sql_errors : Any -> Any ! Error
|
||||
wrap_sql_errors ~action =
|
||||
result = Panic.recover action
|
||||
result = Panic.recover Any action
|
||||
result.catch err-> case err of
|
||||
Polyglot_Error exc ->
|
||||
transformed = if Java.is_instance exc SQLTimeoutException then Sql_Timeout_Error exc else
|
||||
|
@ -409,7 +409,7 @@ type Table
|
||||
Arguments:
|
||||
- index: The column to use as the index of the table.
|
||||
set_index : Text | Column | Vector Text -> Table
|
||||
set_index index = Panic.recover <|
|
||||
set_index index = Panic.recover Any <|
|
||||
new_index = (Helpers.unify_vector_singleton index).map (this.at >> .as_internal)
|
||||
new_ctx = this.context.set_index new_index
|
||||
new_cols = this.internal_columns.filter col->
|
||||
@ -490,7 +490,7 @@ type Table
|
||||
quality_ratio = table.at 'Rating' / table.at 'Price'
|
||||
table.sort by=[quality_ratio, 'Rating']
|
||||
sort : Text | Column | Order_Rule | Vector.Vector (Text | Column | Order_Rule) -> Sort_Order -> Boolean -> Table
|
||||
sort by order=Sort_Order.Ascending missing_last=True = Panic.recover <|
|
||||
sort by order=Sort_Order.Ascending missing_last=True = Panic.recover Any <|
|
||||
order_to_ir = case _ of
|
||||
Sort_Order.Ascending -> IR.Ascending
|
||||
Sort_Order.Descending -> IR.Descending
|
||||
@ -547,7 +547,7 @@ type Table
|
||||
join : Table | Column -> Nothing | Text | Column | Vector (Text | Column) -> Boolean -> Text -> Text -> Table
|
||||
join other on=Nothing drop_unmatched=False left_suffix='_left' right_suffix='_right' = case other of
|
||||
Column _ _ _ _ _ -> this.join other.to_table on drop_unmatched left_suffix right_suffix
|
||||
Table _ _ _ _ -> Panic.recover <|
|
||||
Table _ _ _ _ -> Panic.recover Any <|
|
||||
Panic.rethrow (Helpers.ensure_name_is_sane left_suffix && Helpers.ensure_name_is_sane right_suffix)
|
||||
if left_suffix == right_suffix then
|
||||
Panic.throw <| Illegal_State_Error "left_suffix must be different from right_suffix"
|
||||
@ -627,7 +627,7 @@ type Table
|
||||
- by: The column names on which to group. If this is not set, the index
|
||||
will be used for grouping instead.
|
||||
group : Vector Text | Text | Nothing -> Aggregate_Table
|
||||
group by=Nothing = Panic.recover <|
|
||||
group by=Nothing = Panic.recover Any <|
|
||||
cols = case by of
|
||||
Nothing ->
|
||||
if this.context.meta_index.is_empty then Panic.throw <| Illegal_State_Error "Trying to group by an empty index." else
|
||||
|
@ -125,7 +125,7 @@ type No_Methods
|
||||
|
||||
## Returns a no_such_method_error as a value.
|
||||
no_such_method : No_Such_Method_Error
|
||||
no_such_method = Panic.recover No_Methods.frobnicate . catch
|
||||
no_such_method = Panic.recover Any No_Methods.frobnicate . catch
|
||||
|
||||
## A simple error type for example purposes.
|
||||
type My_Error message
|
||||
|
@ -38,7 +38,7 @@ read location flags=[] =
|
||||
if flags.is_empty then Java_Codecs.READ_FLAG_EMPTY else
|
||||
flags.map .to_integer . reduce (_.bit_or _)
|
||||
_ -> flags.to_integer
|
||||
Panic.recover (Image.Image (Java_Codecs.read path read_flags)) . catch e->
|
||||
Panic.recover Any (Image.Image (Java_Codecs.read path read_flags)) . catch e->
|
||||
case e of
|
||||
Polyglot_Error _ -> Error.throw (File.Io_Error ('Failed to read the file at ' + path + '.'))
|
||||
err -> Panic.throw err
|
||||
@ -71,7 +71,7 @@ Image.Image.write location flags=[] =
|
||||
Vector.Vector _ -> flags
|
||||
_ -> [flags]
|
||||
int_flags = Internal.mat_of_int (write_flags.flat_map x-> [x.to_integer, x.value])
|
||||
Panic.recover (Java_Codecs.write path this.opencv_mat int_flags) . catch e->
|
||||
Panic.recover Any (Java_Codecs.write path this.opencv_mat int_flags) . catch e->
|
||||
case e of
|
||||
Polyglot_Error _ -> Panic.throw (File.Io_Error ('Failed to write to the file at ' + path + '.'))
|
||||
err -> Panic.throw err
|
||||
|
@ -153,7 +153,7 @@ type Image
|
||||
image = Examples.image
|
||||
image + (Matrix.zeros rows=image.rows columns=image.columns channels=image.channels)
|
||||
+ : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal
|
||||
+ value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Image.add _ _ _)) . catch Internal.core_op_handler
|
||||
+ value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Image.add _ _ _)) . catch Internal.core_op_handler
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
@ -204,7 +204,7 @@ type Image
|
||||
image = Examples.image
|
||||
image - (Matrix.zeros rows=image.rows columns=image.columns channels=image.channels)
|
||||
- : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal
|
||||
- value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Image.subtract _ _ _)) . catch Internal.core_op_handler
|
||||
- value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Image.subtract _ _ _)) . catch Internal.core_op_handler
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
@ -255,7 +255,7 @@ type Image
|
||||
image = Examples.image
|
||||
image * (Matrix.ones rows=image.rows columns=image.columns channels=image.channels)
|
||||
* : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal
|
||||
* value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Image.multiply _ _ _)) . catch Internal.core_op_handler
|
||||
* value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Image.multiply _ _ _)) . catch Internal.core_op_handler
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
@ -306,7 +306,7 @@ type Image
|
||||
image = Examples.image
|
||||
image / (Matrix.ones rows=image.rows columns=image.columns channels=image.channels)
|
||||
/ : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal
|
||||
/ value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Image.divide _ _ _)) . catch Internal.core_op_handler
|
||||
/ value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Image.divide _ _ _)) . catch Internal.core_op_handler
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
|
@ -210,7 +210,7 @@ type Matrix
|
||||
|
||||
example_plus = Examples.matrix + Examples.matrix
|
||||
+ : (Number | Vector | Matrix) -> Matrix ! Dimensions_Not_Equal
|
||||
+ value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Matrix.add _ _ _)) . catch Internal.core_op_handler
|
||||
+ value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Matrix.add _ _ _)) . catch Internal.core_op_handler
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
@ -249,7 +249,7 @@ type Matrix
|
||||
|
||||
example_minus = Examples.matrix - Examples.matrix
|
||||
- : (Number | Vector | Matrix) -> Matrix ! Dimensions_Not_Equal
|
||||
- value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Matrix.subtract _ _ _)) . catch Internal.core_op_handler
|
||||
- value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Matrix.subtract _ _ _)) . catch Internal.core_op_handler
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
@ -293,7 +293,7 @@ type Matrix
|
||||
Multiply two matrices.
|
||||
m * m
|
||||
* : (Number | Vector | Matrix) -> Matrix ! Dimensions_Not_Equal
|
||||
* value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Matrix.multiply _ _ _)) . catch Internal.core_op_handler
|
||||
* value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Matrix.multiply _ _ _)) . catch Internal.core_op_handler
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
@ -331,7 +331,7 @@ type Matrix
|
||||
|
||||
example_div = Examples.matrix / Examples.matrix
|
||||
/ : (Number | Vector | Matrix) -> Matrix ! Dimensions_Not_Equal
|
||||
/ value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Matrix.divide _ _ _)) . catch Internal.core_op_handler
|
||||
/ value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Matrix.divide _ _ _)) . catch Internal.core_op_handler
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
|
@ -875,7 +875,7 @@ type Table
|
||||
price_rule = Table.Order_Rule "price" comparator=comparator
|
||||
table.sort by=price_rule
|
||||
sort : Text | Column.Column | Order_Rule | Vector.Vector (Text | Column.Column | Order_Rule) -> Sort_Order -> Boolean -> Table
|
||||
sort by order=Sort_Order.Ascending missing_last=True = Panic.recover <|
|
||||
sort by order=Sort_Order.Ascending missing_last=True = Panic.recover Any <|
|
||||
rules = this.build_java_order_rules by order missing_last
|
||||
fallback_cmp = here.comparator_to_java .compare_to
|
||||
mask = OrderBuilder.buildOrderMask rules.to_array fallback_cmp
|
||||
|
@ -71,9 +71,9 @@ from_csv csv has_header=True prefix='C' =
|
||||
case csv of
|
||||
Text ->
|
||||
input_stream = ByteArrayInputStream.new csv.utf_8.to_array
|
||||
Panic.recover Table.Table (parser_inst.parse input_stream) . catch handle_error
|
||||
Panic.recover Any Table.Table (parser_inst.parse input_stream) . catch handle_error
|
||||
File.File _ ->
|
||||
maybe_err = Panic.recover <| csv.with_input_stream [File.Option.Read] stream->
|
||||
maybe_err = Panic.recover Any <| csv.with_input_stream [File.Option.Read] stream->
|
||||
stream.with_java_stream java_stream->
|
||||
Table.Table (parser_inst.parse java_stream)
|
||||
maybe_err.catch handle_error
|
||||
|
@ -203,12 +203,14 @@ Error.should_fail_with matcher frames_to_skip=0 =
|
||||
Test.expect_panic_with Examples.throw_panic Examples.My_Error
|
||||
expect_panic_with : Any -> Any -> Assertion
|
||||
expect_panic_with ~action matcher =
|
||||
res = Panic.recover action
|
||||
res = Panic.recover Any action
|
||||
case res of
|
||||
_ -> here.fail ("Expected a " + matcher.to_text + " to be thrown, but the action succeeded.")
|
||||
_ ->
|
||||
loc = Meta.get_source_location 2
|
||||
here.fail ("Expected a " + matcher.to_text + " to be thrown, but the action succeeded (at "+loc+").")
|
||||
err = res.catch x->x
|
||||
if err.is_a matcher then Nothing else
|
||||
here.fail ("Unexpected error " + err.to_text + " thrown.")
|
||||
here.fail ("Expected a " + matcher.to_text + ", but " + err.to_text + " was thrown instead.")
|
||||
|
||||
## Asserts that `this` value is equal to the expected value.
|
||||
|
||||
@ -606,7 +608,7 @@ type Assertion
|
||||
- behavior: The behavior to execute.
|
||||
run_spec : Any -> Assertion
|
||||
run_spec ~behavior =
|
||||
recovery = Panic.recover <|
|
||||
recovery = Panic.recover Any <|
|
||||
result = behavior
|
||||
result.catch err-> Panic.throw (Finished_With_Error err result.get_stack_trace_text)
|
||||
Nothing
|
||||
|
@ -89,7 +89,7 @@ Error.catch_ ~val = this.catch (_-> val)
|
||||
## PRIVATE
|
||||
recover_errors : Any -> Any
|
||||
recover_errors ~body =
|
||||
result = Panic.recover body
|
||||
result = Panic.recover Any body
|
||||
result.catch err->
|
||||
Json.from_pairs [["error", err.to_display_text]] . to_text
|
||||
|
||||
|
@ -0,0 +1,83 @@
|
||||
package org.enso.interpreter.node.expression.builtin.error;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.exception.AbstractTruffleException;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.builtin.Builtins;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Panic",
|
||||
name = "catch_primitive",
|
||||
description = "Executes an action if a panic was thrown, calls the provided callback.")
|
||||
public abstract class CatchPanicNode extends Node {
|
||||
private @Child InvokeCallableNode invokeCallableNode;
|
||||
private @Child ThunkExecutorNode thunkExecutorNode = ThunkExecutorNode.build();
|
||||
|
||||
CatchPanicNode() {
|
||||
this.invokeCallableNode =
|
||||
InvokeCallableNode.build(
|
||||
new CallArgumentInfo[] {new CallArgumentInfo()},
|
||||
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
|
||||
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED);
|
||||
this.invokeCallableNode.setTailStatus(BaseNode.TailStatus.TAIL_DIRECT);
|
||||
}
|
||||
|
||||
static CatchPanicNode build() {
|
||||
return CatchPanicNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Stateful execute(
|
||||
VirtualFrame frame,
|
||||
@MonadicState Object state,
|
||||
Object _this,
|
||||
@Suspend Object action,
|
||||
Object handler);
|
||||
|
||||
@Specialization
|
||||
Stateful doExecute(
|
||||
VirtualFrame frame,
|
||||
@MonadicState Object state,
|
||||
Object _this,
|
||||
Object action,
|
||||
Object handler,
|
||||
@Cached BranchProfile panicBranchProfile,
|
||||
@Cached BranchProfile otherExceptionBranchProfile) {
|
||||
try {
|
||||
return thunkExecutorNode.executeThunk(action, state, BaseNode.TailStatus.TAIL_DIRECT);
|
||||
} catch (PanicException e) {
|
||||
panicBranchProfile.enter();
|
||||
Object payload = e.getPayload();
|
||||
return executeCallback(frame, state, handler, payload, e);
|
||||
} catch (AbstractTruffleException e) {
|
||||
otherExceptionBranchProfile.enter();
|
||||
Builtins builtins = Context.get(this).getBuiltins();
|
||||
Object payload = builtins.error().makePolyglotError(e);
|
||||
return executeCallback(frame, state, handler, payload, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Stateful executeCallback(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
Object handler,
|
||||
Object payload,
|
||||
AbstractTruffleException originalException) {
|
||||
Builtins builtins = Context.get(this).getBuiltins();
|
||||
Atom caughtPanic = builtins.caughtPanic().newInstance(payload, originalException);
|
||||
return invokeCallableNode.execute(handler, frame, state, new Object[] {caughtPanic});
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package org.enso.interpreter.node.expression.builtin.error;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.exception.AbstractTruffleException;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.builtin.Builtins;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.error.DataflowError;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Caught_Panic",
|
||||
name = "convert_to_dataflow_error",
|
||||
description = "Converts a Caught_Panic into a Dataflow Error")
|
||||
public abstract class CaughtPanicConvertToDataflowErrorNode extends Node {
|
||||
static CaughtPanicConvertToDataflowErrorNode build() {
|
||||
return CaughtPanicConvertToDataflowErrorNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Stateful execute(@MonadicState Object state, Atom _this);
|
||||
|
||||
@Specialization
|
||||
Stateful doExecute(
|
||||
@MonadicState Object state,
|
||||
Atom _this,
|
||||
@CachedLibrary(limit = "5") InteropLibrary interopLibrary) {
|
||||
Builtins builtins = Context.get(this).getBuiltins();
|
||||
Object payload = _this.getFields()[0];
|
||||
Object originalException = _this.getFields()[1];
|
||||
if (interopLibrary.isException(originalException)) {
|
||||
return new Stateful(
|
||||
state, DataflowError.withTrace(payload, (AbstractTruffleException) originalException));
|
||||
} else {
|
||||
throw new PanicException(
|
||||
builtins
|
||||
.error()
|
||||
.makeTypeError("Exception", originalException, "internal_original_exception"),
|
||||
this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package org.enso.interpreter.node.expression.builtin.error;
|
||||
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.AcceptsError;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.node.expression.builtin.runtime.GetStackTraceNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.builtin.Builtins;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Panic",
|
||||
name = "primitive_get_attached_stack_trace",
|
||||
description = "Gets the stack trace attached to the throwable.")
|
||||
public class GetAttachedStackTraceNode extends Node {
|
||||
Array execute(Object _this, @AcceptsError Object error) {
|
||||
if (error instanceof Throwable) {
|
||||
return GetStackTraceNode.stackTraceToArray((Throwable) error);
|
||||
} else {
|
||||
Builtins builtins = Context.get(this).getBuiltins();
|
||||
throw new PanicException(builtins.error().makeTypeError("Throwable", error, "throwable"), this);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package org.enso.interpreter.node.expression.builtin.error;
|
||||
|
||||
import com.oracle.truffle.api.TruffleStackTrace;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
import org.enso.interpreter.runtime.error.DataflowError;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Error",
|
||||
name = "primitive_get_stack_trace",
|
||||
description = "Gets the stack trace of the error's origin.")
|
||||
public class GetStackTraceNode extends Node {
|
||||
Array execute(DataflowError _this) {
|
||||
var elements = TruffleStackTrace.getStackTrace(_this);
|
||||
var ret = new Array(elements.size());
|
||||
for (int i = 0; i < elements.size(); i++) {
|
||||
var element = elements.get(i);
|
||||
ret.getItems()[i] = element.getGuestObject();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package org.enso.interpreter.node.expression.builtin.error;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.exception.AbstractTruffleException;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.error.DataflowError;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Panic",
|
||||
name = "recover",
|
||||
description = "Executes an action and converts any Panic thrown by it into an Error")
|
||||
public abstract class RecoverPanicNode extends Node {
|
||||
private @Child ThunkExecutorNode thunkExecutorNode = ThunkExecutorNode.build();
|
||||
private final BranchProfile unknownExceptionProfile = BranchProfile.create();
|
||||
|
||||
static RecoverPanicNode build() {
|
||||
return RecoverPanicNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Stateful execute(@MonadicState Object state, Object _this, @Suspend Object action);
|
||||
|
||||
@Specialization
|
||||
Stateful doExecute(
|
||||
@MonadicState Object state,
|
||||
Object _this,
|
||||
Object action,
|
||||
@CachedLibrary(limit = "5") InteropLibrary exceptions) {
|
||||
try {
|
||||
return thunkExecutorNode.executeThunk(action, state, BaseNode.TailStatus.NOT_TAIL);
|
||||
} catch (PanicException e) {
|
||||
return new Stateful(state, DataflowError.withTrace(e.getPayload(), e));
|
||||
} catch (Throwable e) {
|
||||
if (exceptions.isException(e)) {
|
||||
return new Stateful(
|
||||
state,
|
||||
DataflowError.withTrace(
|
||||
Context.get(this).getBuiltins().error().makePolyglotError(e), (AbstractTruffleException) e));
|
||||
}
|
||||
unknownExceptionProfile.enter();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,17 @@
|
||||
package org.enso.interpreter.node.expression.builtin.error;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Fallback;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.builtin.Builtins;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@ -9,8 +19,63 @@ import org.enso.interpreter.runtime.state.Stateful;
|
||||
type = "Panic",
|
||||
name = "throw",
|
||||
description = "Throws a new Panic with given payload.")
|
||||
public class ThrowPanicNode extends Node {
|
||||
Stateful execute(Object _this, Object payload) {
|
||||
public abstract class ThrowPanicNode extends Node {
|
||||
static ThrowPanicNode build() {
|
||||
return ThrowPanicNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Stateful execute(Object _this, Object payload);
|
||||
|
||||
Context getContext() {
|
||||
return Context.get(this);
|
||||
}
|
||||
|
||||
@Specialization(guards = {"payload.getConstructor() == getContext().getBuiltins().caughtPanic()"})
|
||||
Stateful doCaughtPanic(
|
||||
Object _this,
|
||||
Atom payload,
|
||||
@CachedLibrary(limit = "5") InteropLibrary interopLibrary,
|
||||
@Cached BranchProfile typeErrorProfile) {
|
||||
// Note [Original Exception Type]
|
||||
Object originalException = payload.getFields()[1];
|
||||
if (interopLibrary.isException(originalException)) {
|
||||
try {
|
||||
throw interopLibrary.throwException(originalException);
|
||||
} catch (UnsupportedMessageException e) {
|
||||
throw new IllegalStateException(
|
||||
"Impossible, `isException` returned true for `originalException`.");
|
||||
}
|
||||
} else {
|
||||
typeErrorProfile.enter();
|
||||
Builtins builtins = Context.get(this).getBuiltins();
|
||||
throw new PanicException(
|
||||
builtins
|
||||
.error()
|
||||
.makeTypeError("Exception", originalException, "internal_original_exception"),
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization(guards = "interopLibrary.isException(payload)")
|
||||
Stateful doOtherException(
|
||||
Object _this, Object payload, @CachedLibrary(limit = "5") InteropLibrary interopLibrary) {
|
||||
try {
|
||||
throw interopLibrary.throwException(payload);
|
||||
} catch (UnsupportedMessageException e) {
|
||||
throw new IllegalStateException("Impossible, `isException` returned true for `payload`.");
|
||||
}
|
||||
}
|
||||
|
||||
@Fallback
|
||||
Stateful doFallback(Object _this, Object payload) {
|
||||
throw new PanicException(payload, this);
|
||||
}
|
||||
}
|
||||
|
||||
/* Note [Original Exception Type]
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* We can assume that the exception stored in a `Caught_Panic` atom is a
|
||||
* subclass of a `AbstractTruffleException`, because the only place which
|
||||
* constructs `Caught_Panic` puts a `PanicException` there or an
|
||||
* `AbstractTruffleException` directly.
|
||||
*/
|
||||
|
@ -14,7 +14,12 @@ public class GetStackTraceNode extends Node {
|
||||
Array execute(Object _this) {
|
||||
var exception = new PanicException(null, this);
|
||||
TruffleStackTrace.fillIn(exception);
|
||||
return stackTraceToArray(exception);
|
||||
}
|
||||
|
||||
public static Array stackTraceToArray(Throwable exception) {
|
||||
var elements = TruffleStackTrace.getStackTrace(exception);
|
||||
if (elements == null) return new Array();
|
||||
var ret = new Array(elements.size());
|
||||
for (int i = 0; i < elements.size(); i++) {
|
||||
var element = elements.get(i);
|
||||
|
@ -12,7 +12,9 @@ import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.debug.DebugBreakpointMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.debug.DebugEvalMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.CatchAnyMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.RecoverPanicMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.CatchPanicMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.CaughtPanicConvertToDataflowErrorMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.GetAttachedStackTraceMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.ThrowPanicMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.function.ExplicitCallFunctionMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.interop.java.AddToClassPathMethodGen;
|
||||
@ -63,6 +65,7 @@ public class Builtins {
|
||||
private final AtomConstructor function;
|
||||
private final AtomConstructor nothing;
|
||||
private final AtomConstructor panic;
|
||||
private final AtomConstructor caughtPanic;
|
||||
|
||||
private final Bool bool;
|
||||
private final DataflowError dataflowError;
|
||||
@ -108,6 +111,12 @@ public class Builtins {
|
||||
number = new Number(language, scope);
|
||||
ordering = new Ordering(language, scope);
|
||||
panic = new AtomConstructor("Panic", scope).initializeFields();
|
||||
caughtPanic =
|
||||
new AtomConstructor("Caught_Panic", scope)
|
||||
.initializeFields(
|
||||
new ArgumentDefinition(0, "payload", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(
|
||||
1, "internal_original_exception", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
polyglot = new Polyglot(language, scope);
|
||||
resource = new Resource(language, scope);
|
||||
system = new System(language, scope);
|
||||
@ -138,6 +147,7 @@ public class Builtins {
|
||||
scope.registerConstructor(io);
|
||||
scope.registerConstructor(primIo);
|
||||
scope.registerConstructor(panic);
|
||||
scope.registerConstructor(caughtPanic);
|
||||
scope.registerConstructor(state);
|
||||
scope.registerConstructor(debug);
|
||||
scope.registerConstructor(projectDescription);
|
||||
@ -159,10 +169,16 @@ public class Builtins {
|
||||
scope.registerMethod(
|
||||
runtime, "no_inline_with_arg", NoInlineWithArgMethodGen.makeFunction(language));
|
||||
scope.registerMethod(runtime, "gc", GCMethodGen.makeFunction(language));
|
||||
scope.registerMethod(runtime, "primitive_get_stack_trace", GetStackTraceMethodGen.makeFunction(language));
|
||||
scope.registerMethod(
|
||||
runtime, "primitive_get_stack_trace", GetStackTraceMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerMethod(panic, "throw", ThrowPanicMethodGen.makeFunction(language));
|
||||
scope.registerMethod(panic, "recover", RecoverPanicMethodGen.makeFunction(language));
|
||||
scope.registerMethod(panic, "catch_primitive", CatchPanicMethodGen.makeFunction(language));
|
||||
scope.registerMethod(
|
||||
panic,
|
||||
"primitive_get_attached_stack_trace",
|
||||
GetAttachedStackTraceMethodGen.makeFunction(language));
|
||||
scope.registerMethod(caughtPanic, "convert_to_dataflow_error", CaughtPanicConvertToDataflowErrorMethodGen.makeFunction(language));
|
||||
scope.registerMethod(any, "catch_primitive", CatchAnyMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerMethod(state, "get", GetStateMethodGen.makeFunction(language));
|
||||
@ -308,6 +324,11 @@ public class Builtins {
|
||||
return polyglot;
|
||||
}
|
||||
|
||||
/** @return the {@code Caught_Panic} atom constructor */
|
||||
public AtomConstructor caughtPanic() {
|
||||
return caughtPanic;
|
||||
}
|
||||
|
||||
/** @return the container for ordering-related builtins */
|
||||
public Ordering ordering() {
|
||||
return ordering;
|
||||
|
@ -3,7 +3,6 @@ package org.enso.interpreter.runtime.builtin;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.error.CatchErrorMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.ErrorToTextMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.GetStackTraceMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.GetStackTraceTextMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.ThrowErrorMethodGen;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
@ -18,8 +17,8 @@ public class DataflowError {
|
||||
scope.registerConstructor(error);
|
||||
scope.registerMethod(error, "throw", ThrowErrorMethodGen.makeFunction(language));
|
||||
scope.registerMethod(error, "catch_primitive", CatchErrorMethodGen.makeFunction(language));
|
||||
scope.registerMethod(error, "get_stack_trace_text", GetStackTraceTextMethodGen.makeFunction(language));
|
||||
scope.registerMethod(error, "primitive_get_stack_trace", GetStackTraceMethodGen.makeFunction(language));
|
||||
scope.registerMethod(
|
||||
error, "get_stack_trace_text", GetStackTraceTextMethodGen.makeFunction(language));
|
||||
scope.registerMethod(error, "to_text", ErrorToTextMethodGen.makeFunction(language));
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.enso.interpreter.runtime.error;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.TruffleStackTrace;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.exception.AbstractTruffleException;
|
||||
@ -37,7 +38,9 @@ public class DataflowError extends AbstractTruffleException {
|
||||
* @return a new dataflow error
|
||||
*/
|
||||
public static DataflowError withoutTrace(Object payload, Node location) {
|
||||
return new DataflowError(payload, location);
|
||||
DataflowError result = new DataflowError(payload, location);
|
||||
TruffleStackTrace.fillIn(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -234,13 +234,6 @@ type Error
|
||||
get_stack_trace_text : Text
|
||||
get_stack_trace_text = @Builtin_Method "Error.get_stack_trace_text"
|
||||
|
||||
## PRIVATE
|
||||
|
||||
Returns a raw representation of the current execution stack trace.
|
||||
You probably want `Error.get_stack_trace` instead.
|
||||
primitive_get_stack_trace : Array
|
||||
primitive_get_stack_trace = @Builtin_Method "Error.primitive_get_stack_trace"
|
||||
|
||||
## Converts an error to a corresponding textual representation.
|
||||
|
||||
> Example
|
||||
@ -395,7 +388,7 @@ type Panic
|
||||
program control flow.
|
||||
|
||||
Panics "bubble up" through the program until they reach either an
|
||||
invocation of Panic.recover or the program's main method. An unhandled
|
||||
invocation of Panic.recover Any or the program's main method. An unhandled
|
||||
panic in main will terminate the program.
|
||||
|
||||
? Dataflow Errors or Panics
|
||||
@ -407,31 +400,64 @@ type Panic
|
||||
## Throws a new panic with the provided payload.
|
||||
|
||||
Arguments:
|
||||
- payload: The contents of the panic to be thrown.
|
||||
- payload: The contents of the panic to be thrown. If the payload is a
|
||||
`Caught_Panic` or a raw Java exception, instead of throwing a new panic
|
||||
with it as a payload, the original exception is rethrown, preserving
|
||||
its stacktrace.
|
||||
|
||||
> Example
|
||||
Throwing a panic containing the text "Oh no!".
|
||||
|
||||
Panic.throw "Oh no!"
|
||||
|
||||
> Example
|
||||
Use together with `Panic.catch` to catch only specific types of errors
|
||||
and rethrow any others, without affecting their stacktraces.
|
||||
|
||||
Panic.catch Any (Panic.throw "foo") caught_panic-> case caught_panic.payload of
|
||||
Illegal_Argument_Error message _ -> "Illegal arguments were provided: "+message
|
||||
other_panic -> Panic.throw other_panic
|
||||
throw : Any -> Panic
|
||||
throw payload = @Builtin_Method "Panic.throw"
|
||||
|
||||
## Executes the provided action and converts any panic thrown by it into
|
||||
an Error.
|
||||
## PRIVATE
|
||||
Executes the provided action and if any panic was thrown, calls the
|
||||
provided callback.
|
||||
|
||||
If action executes successfully, the result of Panic.recover is the
|
||||
result of that action. Otherwise, it is the panic that was thrown after
|
||||
conversion to a dataflow error.
|
||||
If action executes successfully, the result of `Panic.catch Any` is the
|
||||
result of that action. Otherwise, it is the result of the provided
|
||||
handler callback, executed with the caught panic as its first argument.
|
||||
|
||||
Arguments:
|
||||
- action: The code to execute that potentially panics.
|
||||
- handler: The callback to handle any panics.
|
||||
catch_primitive : Any -> (Caught_Panic -> Any) -> Any
|
||||
catch_primitive ~action handler = @Builtin_Method "Panic.catch_primitive"
|
||||
|
||||
> Example
|
||||
Handling a panic for a panicking action.
|
||||
## PRIVATE
|
||||
|
||||
Panic.recover (Panic.throw "Oh no!")
|
||||
recover : Any -> Any
|
||||
recover ~action = @Builtin_Method "Panic.recover"
|
||||
Returns a raw representation of the stack trace attached to the provided
|
||||
throwable. It can be a dataflow error, a panic or a native Java exception.
|
||||
You probably want `Panic.get_attached_stack_trace` instead.
|
||||
primitive_get_attached_stack_trace : Throwable -> Array
|
||||
primitive_get_attached_stack_trace throwable = @Builtin_Method "Panic.primitive_get_attached_stack_trace"
|
||||
|
||||
type Caught_Panic
|
||||
## A wrapper for a caught panic.
|
||||
|
||||
Arguments:
|
||||
- payload: the payload carried by the error.
|
||||
- internal_original_exception (private): the original Java exception that is
|
||||
the source of this panic. Only for internal use. To get the Java exception
|
||||
from polyglot exceptions, match the `payload` on `Polyglot_Error` and
|
||||
extract the Java object from there.
|
||||
@Builtin_Type
|
||||
type Caught_Panic payload internal_original_exception
|
||||
|
||||
## Converts this caught panic into a dataflow error containing the same
|
||||
payload and stack trace.
|
||||
convert_to_dataflow_error : Error
|
||||
convert_to_dataflow_error = @Builtin_Method "Caught_Panic.convert_to_dataflow_error"
|
||||
|
||||
# Function types.
|
||||
type Function
|
||||
|
@ -342,17 +342,17 @@ class RuntimeErrorsTest
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
xId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
yId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
mainResId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
@ -504,7 +504,7 @@ class RuntimeErrorsTest
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
xId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
TestMessages.update(contextId, yId, Constants.INTEGER),
|
||||
TestMessages.update(contextId, mainResId, Constants.NOTHING),
|
||||
@ -566,12 +566,12 @@ class RuntimeErrorsTest
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
xId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
yId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
TestMessages.update(contextId, mainResId, Constants.NOTHING),
|
||||
context.executionComplete(contextId)
|
||||
@ -617,12 +617,12 @@ class RuntimeErrorsTest
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
xId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
yId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
@ -706,12 +706,12 @@ class RuntimeErrorsTest
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
xId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
yId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
TestMessages.update(contextId, mainResId, Constants.NOTHING),
|
||||
context.executionComplete(contextId)
|
||||
@ -743,6 +743,7 @@ class RuntimeErrorsTest
|
||||
val requestId = UUID.randomUUID()
|
||||
val moduleName = "Enso_Test.Test.Main"
|
||||
val metadata = new Metadata
|
||||
val fooThrowId = metadata.addItem(74, 20)
|
||||
val xId = metadata.addItem(111, 8)
|
||||
val yId = metadata.addItem(128, 5)
|
||||
val mainResId = metadata.addItem(138, 12)
|
||||
@ -796,12 +797,12 @@ class RuntimeErrorsTest
|
||||
contextId,
|
||||
xId,
|
||||
Api.MethodPointer(moduleName, moduleName, "foo"),
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(fooThrowId, xId))
|
||||
),
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
yId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(fooThrowId, xId))
|
||||
),
|
||||
TestMessages.update(contextId, mainResId, Constants.NOTHING),
|
||||
context.executionComplete(contextId)
|
||||
@ -1328,12 +1329,12 @@ class RuntimeErrorsTest
|
||||
contextId,
|
||||
xId,
|
||||
Api.MethodPointer(moduleName, moduleName, "foo"),
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
yId,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
|
||||
),
|
||||
TestMessages.update(
|
||||
contextId,
|
||||
|
@ -2869,7 +2869,7 @@ class RuntimeServerTest
|
||||
"""from Standard.Builtins import all
|
||||
|
|
||||
|main =
|
||||
| x = Panic.recover @
|
||||
| x = Panic.catch_primitive @ .convert_to_dataflow_error
|
||||
| IO.println (x.catch .to_text)
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val contents = metadata.appendToCode(code)
|
||||
@ -2910,7 +2910,7 @@ class RuntimeServerTest
|
||||
Api.ExecutionResult.Diagnostic.error(
|
||||
"Unrecognized token.",
|
||||
Some(mainFile),
|
||||
Some(model.Range(model.Position(3, 22), model.Position(3, 23)))
|
||||
Some(model.Range(model.Position(3, 30), model.Position(3, 31)))
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -2932,7 +2932,7 @@ class RuntimeServerTest
|
||||
"""from Standard.Builtins import all
|
||||
|
|
||||
|main =
|
||||
| x = Panic.recover ()
|
||||
| x = Panic.catch_primitive () .convert_to_dataflow_error
|
||||
| IO.println (x.catch .to_text)
|
||||
|
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
@ -2974,7 +2974,7 @@ class RuntimeServerTest
|
||||
Api.ExecutionResult.Diagnostic.error(
|
||||
"Parentheses can't be empty.",
|
||||
Some(mainFile),
|
||||
Some(model.Range(model.Position(3, 22), model.Position(3, 24)))
|
||||
Some(model.Range(model.Position(3, 30), model.Position(3, 32)))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -1712,7 +1712,7 @@ class RuntimeVisualisationsTest
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
idMain,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(idMain))
|
||||
),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
@ -1813,7 +1813,7 @@ class RuntimeVisualisationsTest
|
||||
Api.VisualisationConfiguration(
|
||||
contextId,
|
||||
moduleName,
|
||||
"x -> Panic.recover x . catch_primitive _.to_text"
|
||||
"x -> Panic.catch_primitive x caught_panic-> caught_panic.payload.to_text"
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -1877,7 +1877,8 @@ class RuntimeVisualisationsTest
|
||||
|
||||
// NOTE: below values need to be kept in sync with what is used internally by Rust IDE code
|
||||
val visualisationModule = "Standard.Base.Main"
|
||||
val visualisationCode = Source.fromResource("error_preprocessor.enso").mkString
|
||||
val visualisationCode =
|
||||
Source.fromResource("error_preprocessor.enso").mkString
|
||||
|
||||
// create context
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
@ -1907,7 +1908,7 @@ class RuntimeVisualisationsTest
|
||||
TestMessages.error(
|
||||
contextId,
|
||||
idMain,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq())
|
||||
Api.ExpressionUpdate.Payload.DataflowError(Seq(idMain))
|
||||
),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
|
@ -13,7 +13,7 @@ class CompileDiagnosticsTest extends InterpreterTest {
|
||||
"""from Standard.Builtins import all
|
||||
|
|
||||
|main =
|
||||
| x = Panic.recover ()
|
||||
| x = Panic.catch_primitive () .convert_to_dataflow_error
|
||||
| x.catch_primitive err->
|
||||
| case err of
|
||||
| Syntax_Error msg -> "Oopsie, it's a syntax error: " + msg
|
||||
@ -28,8 +28,8 @@ class CompileDiagnosticsTest extends InterpreterTest {
|
||||
"""from Standard.Builtins import all
|
||||
|
|
||||
|main =
|
||||
| x = Panic.recover @
|
||||
| x.catch_primitive .to_text
|
||||
| x = Panic.catch_primitive @ caught_panic-> caught_panic.payload
|
||||
| x.to_text
|
||||
|""".stripMargin
|
||||
eval(code) shouldEqual "(Syntax_Error 'Unrecognized token.')"
|
||||
}
|
||||
@ -42,7 +42,7 @@ class CompileDiagnosticsTest extends InterpreterTest {
|
||||
| x = 1
|
||||
| x = 2
|
||||
|
|
||||
|main = Panic.recover here.foo . catch_primitive .to_text
|
||||
|main = Panic.catch_primitive here.foo caught_panic-> caught_panic.payload.to_text
|
||||
|""".stripMargin
|
||||
eval(code) shouldEqual "(Compile_Error 'Variable x is being redefined.')"
|
||||
}
|
||||
@ -55,9 +55,11 @@ class CompileDiagnosticsTest extends InterpreterTest {
|
||||
| my_var = 10
|
||||
| my_vra
|
||||
|
|
||||
|main = Panic.recover here.foo . catch_primitive .to_text
|
||||
|main = Panic.catch_primitive here.foo caught_panic-> caught_panic.payload.to_text
|
||||
|""".stripMargin
|
||||
eval(code) shouldEqual "(Compile_Error 'Variable `my_vra` is not defined.')"
|
||||
eval(
|
||||
code
|
||||
) shouldEqual "(Compile_Error 'Variable `my_vra` is not defined.')"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class PanicsTest extends InterpreterTest {
|
||||
|
|
||||
|main =
|
||||
| thrower = x -> Panic.throw x
|
||||
| caught = Panic.recover (thrower MyError)
|
||||
| caught = Panic.catch_primitive (thrower MyError) .convert_to_dataflow_error
|
||||
| IO.println caught
|
||||
|""".stripMargin
|
||||
|
||||
@ -55,7 +55,7 @@ class PanicsTest extends InterpreterTest {
|
||||
|polyglot java import java.lang.Long
|
||||
|
|
||||
|main =
|
||||
| caught = Panic.recover (Long.parseLong "oops")
|
||||
| caught = Panic.catch_primitive (Long.parseLong "oops") .convert_to_dataflow_error
|
||||
| IO.println caught
|
||||
| cause = caught.catch_primitive e-> case e of
|
||||
| Polyglot_Error err -> err
|
||||
|
@ -98,7 +98,7 @@ class StateTest extends InterpreterTest {
|
||||
|
|
||||
|stater =
|
||||
| State.put Number 5
|
||||
| Panic.recover here.panicker
|
||||
| Panic.catch_primitive here.panicker x->x
|
||||
| State.get Number
|
||||
|
|
||||
|main = State.run Number 0 here.stater
|
||||
|
@ -114,8 +114,8 @@ class TextTest extends InterpreterTest {
|
||||
| IO.println (Compile_Error "error :(").to_display_text
|
||||
| IO.println (Inexhaustive_Pattern_Match_Error 32).to_display_text
|
||||
| IO.println (Arithmetic_Error "cannot frobnicate quaternions").to_display_text
|
||||
| IO.println ((Panic.recover (1 + "foo")).catch_primitive .to_display_text)
|
||||
| IO.println ((Panic.recover (7 1)).catch_primitive .to_display_text)
|
||||
| IO.println ((Panic.catch_primitive (1 + "foo") .convert_to_dataflow_error).catch_primitive .to_display_text)
|
||||
| IO.println ((Panic.catch_primitive (7 1) .convert_to_dataflow_error).catch_primitive .to_display_text)
|
||||
| IO.println (Arity_Error 10 10 20).to_display_text
|
||||
|""".stripMargin
|
||||
eval(code)
|
||||
|
@ -3,7 +3,7 @@ from Standard.Database import all
|
||||
import Standard.Test
|
||||
|
||||
spec prefix connection pending=Nothing =
|
||||
make_table name column_names column_typenames = Panic.recover <|
|
||||
make_table name column_names column_typenames = Panic.recover Any <|
|
||||
quote x = '"' + x + '"'
|
||||
# TODO this is a hack with no sanitization, just for testing; it should be removed when proper create table is supported by the library
|
||||
cols = column_names.zip column_typenames name-> typ->
|
||||
@ -11,7 +11,7 @@ spec prefix connection pending=Nothing =
|
||||
sql = "CREATE TABLE " + quote name + " (" + (cols.join ", ") + ")"
|
||||
Panic.rethrow <| connection.execute_update sql
|
||||
Panic.rethrow <| connection.access_table name
|
||||
clean_table name = Panic.recover <|
|
||||
clean_table name = Panic.recover Any <|
|
||||
sql = 'DROP TABLE "' + name + '"'
|
||||
Panic.rethrow <| connection.execute_update sql
|
||||
t1 = make_table "T1" ["a", "b", "c"] ["INT", "INT", "INT"]
|
||||
|
@ -8,9 +8,9 @@ polyglot java import java.util.Locale as JavaLocale
|
||||
with_locale locale ~test =
|
||||
default_locale = JavaLocale.getDefault
|
||||
JavaLocale.setDefault locale.java_locale
|
||||
result = Panic.recover test . catch_primitive e->
|
||||
result = Panic.catch Any test caught_panic->
|
||||
JavaLocale.setDefault default_locale
|
||||
Panic.throw e
|
||||
Panic.throw caught_panic
|
||||
JavaLocale.setDefault default_locale
|
||||
result
|
||||
|
||||
|
@ -70,9 +70,9 @@ spec =
|
||||
Text.from Methods.get_bar . should_equal "'bar'"
|
||||
|
||||
Test.specify "should fail graciously when there is no conversion" <|
|
||||
Panic.recover (Foo.from (Quux 10)) . catch .to_display_text . should_equal "Could not find a conversion from `Quux` to `Foo`"
|
||||
Panic.recover Any (Foo.from (Quux 10)) . catch .to_display_text . should_equal "Could not find a conversion from `Quux` to `Foo`"
|
||||
Test.specify "should fail graciously when the conversion target is invalid" <|
|
||||
Panic.recover (123.from (Quux 10)) . catch .to_display_text . should_equal "123 is not a valid conversion target. Expected a type."
|
||||
Panic.recover Any (123.from (Quux 10)) . catch .to_display_text . should_equal "123 is not a valid conversion target. Expected a type."
|
||||
|
||||
Test.specify "should be callable with by-name arguments" <|
|
||||
.from that=4 this=Foo . should_equal (Foo [4, 0, 0, 0])
|
||||
|
@ -3,18 +3,23 @@ from Standard.Base import all
|
||||
import Standard.Test
|
||||
|
||||
polyglot java import java.util.Random
|
||||
polyglot java import java.lang.Long
|
||||
polyglot java import java.lang.NumberFormatException
|
||||
|
||||
type My_Type foo
|
||||
|
||||
throw_a_bar = Error.throw "bar"
|
||||
throw_a_bar_panicking = Panic.throw "bar"
|
||||
throw_a_foo_panicking = Panic.throw "foo"
|
||||
throw_raw_java msg = Panic.throw (NumberFormatException.new msg)
|
||||
do_a_parse str = Long.parseLong str
|
||||
|
||||
spec =
|
||||
Test.group "No Method Errors" <|
|
||||
Test.specify "should be recoverable" <|
|
||||
err_1 = Panic.recover (123 . foobar "baz") . catch e->e
|
||||
err_2 = Panic.recover ("foo" . baz 123) . catch e->e
|
||||
err_3 = Panic.recover (My_Type False . nope) . catch e->e
|
||||
err_1 = Panic.recover Any (123 . foobar "baz") . catch e->e
|
||||
err_2 = Panic.recover Any ("foo" . baz 123) . catch e->e
|
||||
err_3 = Panic.recover Any (My_Type False . nope) . catch e->e
|
||||
|
||||
err_1.target.should_equal 123
|
||||
err_1.method_name.should_equal "foobar"
|
||||
@ -67,9 +72,149 @@ spec =
|
||||
error = Error.throw 42
|
||||
Random.new error . should_fail_with Integer
|
||||
|
||||
Test.specify "should allow to inspect the stack trace of a recovered panic" <|
|
||||
error = Panic.recover <| here.throw_a_bar_panicking
|
||||
Test.specify "should allow to inspect their stacktrace" <|
|
||||
error = here.throw_a_bar
|
||||
error.catch . should_equal "bar"
|
||||
error.get_stack_trace.second.name . should_equal "Error_Spec.throw_a_bar_panicking"
|
||||
error.stack_trace.second.name . should_equal "Error_Spec.throw_a_bar"
|
||||
|
||||
Test.specify "should allow to inspect the stack trace of a recovered panic" <|
|
||||
error = Panic.recover Any <| here.throw_a_bar_panicking
|
||||
error.catch . should_equal "bar"
|
||||
error.stack_trace.second.name . should_equal "Error_Spec.throw_a_bar_panicking"
|
||||
|
||||
Test.group "Panics" <|
|
||||
Test.specify "should be able to be caught" <|
|
||||
result = Panic.catch Any (Panic.throw 42) caught_panic->
|
||||
caught_panic.payload+10
|
||||
result . should_equal 52
|
||||
|
||||
result_2 = Panic.catch Any (1 + 2) caught_panic->
|
||||
caught_panic.payload+10
|
||||
result_2 . should_equal 3
|
||||
|
||||
Test.specify "should not mix with dataflow errors" <|
|
||||
result = Panic.catch Any (Error.throw 42) caught_panic->
|
||||
caught_panic.payload+10
|
||||
result.catch . should_equal 42
|
||||
|
||||
Test.specify "should provide access to stack traces" <|
|
||||
stack = Panic.catch Any here.throw_a_bar_panicking caught_panic->
|
||||
caught_panic.stack_trace
|
||||
stack.second.name . should_equal "Error_Spec.throw_a_bar_panicking"
|
||||
|
||||
Test.specify "should provide access to Java stack traces" <|
|
||||
stack_1 = Panic.recover Any (here.do_a_parse "foo") . stack_trace
|
||||
stack_1.at 2 . name . should_equal "Error_Spec.do_a_parse"
|
||||
|
||||
stack_2 = Panic.catch Any (here.do_a_parse "foo") caught_panic->
|
||||
caught_panic.stack_trace
|
||||
stack_2.at 2 . 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 here.throw_a_bar_panicking x->x
|
||||
rethrow foo = Panic.throw foo
|
||||
rethrown_panic = Panic.catch Any (rethrow caught_panic) x->x
|
||||
(rethrown_panic.stack_trace.length > 0).should_be_true
|
||||
(rethrown_panic.stack_trace.map .name) . should_equal (caught_panic.stack_trace.map .name)
|
||||
|
||||
Test.specify "should allow the pattern for handling selected exceptions" <|
|
||||
perform_operation ~action =
|
||||
Panic.catch Any action caught_panic->
|
||||
if caught_panic.payload == "bar" then 42 else
|
||||
Panic.throw caught_panic
|
||||
Panic.recover Any (perform_operation here.throw_a_bar_panicking) . should_equal 42
|
||||
|
||||
error = Panic.recover Any (perform_operation here.throw_a_foo_panicking)
|
||||
error.catch . should_equal "foo"
|
||||
error.stack_trace.second.name . should_equal "Error_Spec.throw_a_foo_panicking"
|
||||
|
||||
Test.specify "should work as in the examples" <|
|
||||
fun ~act =
|
||||
Panic.catch Any act caught_panic-> case caught_panic.payload of
|
||||
Illegal_Argument_Error message _ -> "Illegal arguments were provided: "+message
|
||||
other_panic -> Panic.throw other_panic
|
||||
Panic.recover Any (fun "bar") . should_equal "bar"
|
||||
Panic.recover Any (fun (Panic.throw "foo")) . catch . should_equal "foo"
|
||||
Panic.recover Any (fun (Panic.throw (Illegal_Argument_Error "msg" Nothing))) . should_equal "Illegal arguments were provided: msg"
|
||||
|
||||
Test.specify "should allow catching Java exceptions easily" <|
|
||||
parse str =
|
||||
Panic.catch NumberFormatException (Long.parseLong str) caught_panic->
|
||||
Error.throw (Illegal_Argument_Error "The provided string is not a valid number: "+caught_panic.payload.cause.getMessage)
|
||||
|
||||
parse "42" . should_equal 42
|
||||
dataflow_error = parse "foo"
|
||||
dataflow_error.catch . should_equal (Illegal_Argument_Error 'The provided string is not a valid number: For input string: "foo"')
|
||||
Test.expect_panic_with (parse 0.0) Unsupported_Argument_Types
|
||||
|
||||
Test.specify "should allow to throw raw Java exceptions" <|
|
||||
exception = Panic.catch NumberFormatException (here.throw_raw_java "foo") (p -> p.payload.cause)
|
||||
exception.getMessage . should_equal "foo"
|
||||
Panic.get_attached_stack_trace exception . second . name . should_equal "Error_Spec.throw_raw_java"
|
||||
|
||||
caught_panic = Panic.catch Any (here.throw_raw_java "foo") x->x
|
||||
caught_panic.stack_trace.second.name . should_equal "Error_Spec.throw_raw_java"
|
||||
caught_panic.payload . should_be_a Polyglot_Error
|
||||
|
||||
Test.specify "should allow to re-throw raw Java exceptions" <|
|
||||
message_1 = Ref.new ""
|
||||
caught_1 = Panic.recover Any <|
|
||||
Panic.catch NumberFormatException (here.do_a_parse "foo") caught_panic->
|
||||
Ref.put message_1 caught_panic.payload.cause.getMessage
|
||||
Panic.throw caught_panic.payload.cause
|
||||
Ref.get message_1 . should_equal 'For input string: "foo"'
|
||||
caught_1.catch . should_be_a Polyglot_Error
|
||||
caught_1.stack_trace.at 2 . name . should_equal "Error_Spec.do_a_parse"
|
||||
|
||||
message_2 = Ref.new ""
|
||||
caught_2 = Panic.recover Any <|
|
||||
Panic.catch NumberFormatException (here.throw_raw_java "foo") caught_panic->
|
||||
Ref.put message_2 caught_panic.payload.cause.getMessage
|
||||
Panic.throw caught_panic.payload.cause
|
||||
Ref.get message_2 . should_equal "foo"
|
||||
caught_2.catch . should_be_a Polyglot_Error
|
||||
caught_2.stack_trace.second.name . should_equal "Error_Spec.throw_raw_java"
|
||||
|
||||
Test.specify "should allow to catch a specific panic type easily" <|
|
||||
message_1 = Panic.catch Illegal_Argument_Error (Panic.throw (Illegal_Argument_Error "msg" Nothing)) caught_panic->
|
||||
caught_panic.payload.message
|
||||
message_1 . should_equal "msg"
|
||||
|
||||
error = Panic.recover Any <| Panic.catch Illegal_Argument_Error (Panic.throw (Illegal_State_Error "foo" Nothing)) caught_panic->
|
||||
caught_panic.payload.message
|
||||
error.catch . should_be_an Illegal_State_Error
|
||||
|
||||
message_2 = Panic.catch Any (Panic.throw (Illegal_Argument_Error "msg" Nothing)) _->
|
||||
"caught"
|
||||
message_2 . should_equal "caught"
|
||||
|
||||
message_3 = Panic.catch Polyglot_Error (Long.parseLong "foo") _->
|
||||
"polyglot"
|
||||
message_3 . should_equal "polyglot"
|
||||
message_4 = Panic.catch Any (Long.parseLong "foo") _->
|
||||
"polyglot2"
|
||||
message_4 . should_equal "polyglot2"
|
||||
message_5 = Panic.catch Unsupported_Argument_Types (Long.parseLong 0) _->
|
||||
"uat"
|
||||
message_5 . should_equal "uat"
|
||||
|
||||
Test.expect_panic_with (Panic.catch Illegal_Argument_Error (Long.parseLong "foo") (_->"polyglot3")) Polyglot_Error
|
||||
Test.expect_panic_with (Panic.catch Nothing (Long.parseLong 0) (_->"polyglot4")) Unsupported_Argument_Types
|
||||
|
||||
Test.specify "should be able to be recovered selectively" <|
|
||||
Panic.recover Illegal_Argument_Error (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch . should_be_an Illegal_Argument_Error
|
||||
Panic.recover Any (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch . should_be_an Illegal_Argument_Error
|
||||
Panic.recover [Illegal_Argument_Error] (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch . should_be_an Illegal_Argument_Error
|
||||
Panic.recover [Illegal_State_Error, Illegal_Argument_Error] (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch . should_be_an Illegal_Argument_Error
|
||||
|
||||
Test.expect_panic_with <| Panic.recover Illegal_State_Error (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch
|
||||
Test.expect_panic_with <| Panic.recover [Illegal_State_Error, Polyglot_Error] (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch
|
||||
Test.expect_panic_with <| Panic.recover [] (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch
|
||||
|
||||
Panic.recover [Polyglot_Error] (here.do_a_parse "foo") . catch . should_be_a Polyglot_Error
|
||||
|
||||
Panic.recover Any here.throw_a_bar_panicking . catch . should_equal "bar"
|
||||
Panic.recover Text here.throw_a_bar_panicking . stack_trace . second . name . should_equal "Error_Spec.throw_a_bar_panicking"
|
||||
|
||||
|
||||
main = Test.Suite.run_main here.spec
|
||||
|
@ -142,7 +142,7 @@ spec = Test.group "Polyglot JS" <|
|
||||
|
||||
Test.specify "should allow Enso to catch JS exceptions" <|
|
||||
value = My_Type 1 2
|
||||
result = Panic.recover <| value.my_throw
|
||||
result = Panic.recover Any <| value.my_throw
|
||||
err = result.catch
|
||||
err.cause.message . should_equal "JS Exc"
|
||||
err.cause.name . should_equal "Error"
|
||||
@ -153,7 +153,7 @@ spec = Test.group "Polyglot JS" <|
|
||||
result . should_equal 7
|
||||
|
||||
Test.specify "should properly handle parse errors" <|
|
||||
err = Panic.recover here.does_not_parse . catch
|
||||
err = Panic.recover Any here.does_not_parse . catch
|
||||
err.cause.message.should .contain "Expected }"
|
||||
|
||||
Test.specify "allow access to properties of nested objects" <|
|
||||
|
@ -142,7 +142,7 @@ spec =
|
||||
|
||||
Test.specify "should allow Enso to catch Python exceptions" <|
|
||||
value = My_Type 1 2
|
||||
result = Panic.recover <| value.my_throw
|
||||
result = Panic.recover Any <| value.my_throw
|
||||
err = result.catch
|
||||
err.cause.args.at 0 . should_equal 'Error!'
|
||||
err.cause.to_text . should_equal "RuntimeError('Error!')"
|
||||
@ -153,7 +153,7 @@ spec =
|
||||
result . should_equal 7
|
||||
|
||||
Test.specify "should properly handle parse errors" <|
|
||||
err = Panic.recover here.does_not_parse . catch
|
||||
err = Panic.recover Any here.does_not_parse . catch
|
||||
err.cause.args.at 0 . should .contain 'invalid syntax'
|
||||
|
||||
Test.specify "should perform maths with mixed numbers" <|
|
||||
|
@ -125,7 +125,7 @@ spec =
|
||||
|
||||
Test.specify "should allow Enso to catch R exceptions" <|
|
||||
value = My_Type 1 2
|
||||
result = Panic.recover <| value.my_throw
|
||||
result = Panic.recover Any <| value.my_throw
|
||||
err = result.catch
|
||||
err.to_display_text.should_equal "Polyglot error: Error: error in R code!"
|
||||
|
||||
@ -136,7 +136,7 @@ spec =
|
||||
result . should_equal 7
|
||||
|
||||
Test.specify "should properly report parse errors" <|
|
||||
err = Panic.recover here.does_not_parse
|
||||
err = Panic.recover Any here.does_not_parse
|
||||
err.catch.to_display_text.should .contain 'parse exception'
|
||||
|
||||
Test.specify "should perform maths with mixed numbers" <|
|
||||
|
@ -141,10 +141,10 @@ spec = Test.group "Dataflow Warnings" <|
|
||||
mapped_2.catch . should_equal 17
|
||||
Warning.get_all mapped_2 . catch . should_equal 17
|
||||
|
||||
errored_3 = Panic.recover here.throw_a_bar
|
||||
errored_3 = Panic.recover Any here.throw_a_bar
|
||||
mapped_3 = here.map_odd_warnings_and_errors errored_3
|
||||
mapped_3.catch . should_equal "bar"
|
||||
mapped_3.get_stack_trace.second.name . should_equal "Warnings_Spec.throw_a_bar"
|
||||
mapped_3.stack_trace.second.name . should_equal "Warnings_Spec.throw_a_bar"
|
||||
Warning.get_all mapped_3 . catch . should_equal "bar"
|
||||
|
||||
main = Test.Suite.run_main here.spec
|
||||
|
@ -25,7 +25,7 @@ spec =
|
||||
|
||||
Test.specify "should handle exceptions when reading a non-existent file" <|
|
||||
file = File.new "does_not_exist.txt"
|
||||
successfully_failed = Panic.recover file.read . catch e-> case e of
|
||||
successfully_failed = Panic.recover Any file.read . catch e-> case e of
|
||||
File.No_Such_File_Error _ -> True
|
||||
_ -> False
|
||||
successfully_failed . should_be_true
|
||||
|
Loading…
Reference in New Issue
Block a user