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:

throw RuntimeError("Something went wrong!");

As noted in the storm reference, 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:

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:

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:

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.