File: 10-Exceptions.md

package info (click to toggle)
storm-lang 0.7.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,028 kB
  • sloc: ansic: 261,471; cpp: 140,432; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (98 lines) | stat: -rw-r--r-- 3,362 bytes parent folder | download | duplicates (2)
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.