1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
Exceptions
==========
Exceptions in Basic Storm are thrown using the `throw` keyword. It accepts an arbitrary expression
that is evaluated to the exception object to be thrown. It typically looks as follows:
```bsstmt
throw RuntimeError("Something went wrong!");
```
As noted in the [storm reference](md:/Language_Reference/Storm/Exceptions), all exceptions must
inherit (either directly or indirectly) from the `core:Exception` class. As such, new exceptions can
be defined in Basic Storm, just like a regular class:
```bs
class MyException extends Exception {
private Str msg;
init(Str message) {
init { msg = message; }
// Save the stack trace of the current thread in the exception.
// This is optional (as it is an expensive operation), and will
// cause '.toS' to output the stack trace.
saveTrace();
}
protected void message(StrBuf to) : override {
to << "My error: " << msg;
}
}
```
As we can see, the implementation calls `saveTrace` to store a stack trace in the exception. This is
not done automatically, as collecting stack traces is a fairly expensive operation. If the stack
trace is not interesting, this step can be omitted.
Furthermore, to generate a descriptive message, the exception class overrides `message(StrBuf)`
rather than `toS(StrBuf)`. This is to make it possible to extract only the message (without the
stack trace) by calling `message()`. The `toS` implementation will include the stack trace if it is
present.
Exceptions are caught using a `try` block:
```bsstmt
try {
throw MyException("Test");
} catch (MyException e) {
print("My exception: ${e}");
} catch (Exception e) {
print("Generic exception: ${e}");
}
```
The code inside the `try` block is executed as normal. However, if an exception is thrown inside the
block, execution is redirected to one of the `catch` blocks. The system searches the call stack from
the `throw` block and upwards until a suitable `catch` block is found. If multiple `catch` blocks
are present, then they are searched from top to bottom. The example above would thus print `"My
exception: ..."` since the `MyException` handler is before the `Exception` handler. As illustrated
below, `catch` blocks closer to the throw site have priority:
```bs
void main() {
try {
a();
print("OK!");
} catch (MyException e) {
print("My exception: ${e}");
} catch (Exception e) {
print("Generic exception: ${e}");
}
}
void a() {
try {
b();
print("A OK!");
} catch (Exception e) {
print("Generic exception in a: ${e}");
}
}
void b() {
throw MyException("Test");
}
```
This example will print two lines. The first contains `Generic exception in a: ...` and the second
contains `OK!`. This means that the general `catch` block inside `a` takes precedence over the
specific one in `main` since it is closer to the site where the exception was thrown.
It is possible to omit the variable name (`e` in this case) if the code does not need to
inspect the caught exception.
It is of course possible to re-throw the caught exception in the handler by using the `throw`
keyword. Since all exceptions are classes, they are handled by reference, and there is therefore no
need for a special form of the `throw` keyword for this situation.
|