Error handling

Even though we've tried to keep the language as user-friendly as possible by returning empty values rather than throwing exceptions where possible, there are still cases when exceptions need to be thrown.

Additionally, users may want to throw the errors themselves (for instance from common user-defined functions) and handle error results differently depending on context.

There are two types of exceptions:

  1. Java Exceptions - thrown from the java code running behind SIL™

  2. SIL™ objects thrown using throw (explained below)

Throwing SIL Objects

The general syntax for throwing an exception from SIL™ is:

1 throw <value>;

Any SIL™ value can be thrown as exception. Values can either be constants, variables or expressions.

1 2 3 4 5 throw errorCode; throw 2; throw array[0].fieldName; throw ("Fiend value invalid: " + structure.field); throw "Generic error!";


Try-catch block

Syntax

1 2 3 4 5 6 7 try { <instructions>  } catch <type> <varName> { // handle <varName> } catch { // handle other error }

The try-catch block contains the following components:

  • one try block containing instructions to execute. 

  • zero, one or more typed catch clauses.

  • zero or one catch all clause 

If any instruction in the try block throws an exception, execution of instructions within the try block is stopped. That is, any instructions within the try block after the one that throws the exception will not be executed. 

Next, SIL™ will iterate over the catch clauses in the order they were declared looking for a catch clause that matches the type of the error that was thrown:

  • Typed catch clauses will match if the <type> declared in the catch clause matches the type of the exception. That means that typed catch clauses can only catch SIL objects thrown using throw. The caught value is available inside the catch block using the defined <varname>

  • The catch all clause will catch any exception (SIL™ Objects or Java exceptions) regardless of type.

If a matching catch clause was found, the instructions within the catch block are executed and the program will continue. If no matching catch clause is found, the exception is thrown inside the outer code block. This makes it possible to nest try-catch blocks within one another and throw exceptions from the inner block to be caught by the topmost one.

The general catch has been supplemented with 2 new routines:

  • lastExceptionClass() - returning the Java class name of the exception

  • lastExceptionMessage() - returning the actual error message

These routines should only be used in this context, to get more info on the error itself and to better recover from it.

Examples

Type matching

1 2 3 4 5 6 7 8 9 try { number errorCode = 2; throw errorCode; } catch string s { // will not match } catch number err { // will match because a number was thrown //err = 2 }

Catch all

1 2 3 4 5 6 7 8 try { number errorCode = 2; throw errorCode; } catch string s { // will not match } catch { // will match, but the error code is not available in this context }

Nesting

1 2 3 4 5 6 7 8 9 10 try { try { throw "Error!"; } catch number errCode { // will not match because "Error!" is a string } } catch string err { // matches // err = "Error!" } 

Java Exceptions

1 2 3 4 5 6 7 8 9 try { runAs("user_that_does_not_exist"); } catch string err { // will not match } catch { // will match runnerLog("Class:" + lastExceptionClass()); runnerLog("Message:" + lastExceptionMessage()); }