haxe-coroutines
haxe-coroutines copied to clipboard
Transformation: `try...catch`
Source
try {
doSomething();
} catch(e:String) {
catchString();
} catch(e:Int) {
catchInt();
} catch(e:Dynamic) {
catchDynamic();
}
Short legend for next snippet:
-
__ctx__
is a context of state machine (holds current variable values and other data) -
${id}
is a unique id for current transformation
Preprocessed into:
//This line indicates that next state should be executed inside of a `try...catch` to handle exceptions;
//and `@state @catchBegin ${id}` is a state which should be executed on exception
__ctx__.enterTry(@state @catchBegin ${id});
//this line finishes current state and advances state machine to specified state
@goto @tryBegin ${id};
//this line indicates that a new state starts here. In the end it will be replaced with `case ${id}:`
@label @tryBegin ${id}
//actual code of `try` block
doSomething();
//this line indicates that exception handling should be turned off now.
__ctx__.leaveTry();
//if we reached this line, it means there were no exceptions, and we should skip `catch` and go to the line after last `catch` block.
@goto @tryCatchEnd ${id}
//this line indicates a start of a state with `catch` blocks
@label @catchBegin ${id}
//each catch is transformed to a type checking of caught exception
//transforming `if`s is a topic for another transformation.
if(Std.is(__ctx__.exception, String)) {
catchString();
} else if(Std.is(__ctx__.exception, Int)) {
catchInt();
} else {
catchDynamic();
//if no `catch(e:Dynamic)` is defined in user code, then this block is replaced with
//__ctx__.notCaught()
//which rethrows exception if current context is not awaited or propagates exception to a caller context otherwise
}
//indicates a start of next state
@label @tryCatchEnd ${id}
//some cleanup if needed
__ctx__.leaveTryCatch();
It would be nice to look at what this generates in the end. For example, I think current Kotlin implementation would generate something like this:
// state-machine context
var __state = 0;
var __exceptionState = 2;
var __exception = null;
do {
try {
switch (__state) {
case 0:
__exceptionState = 1;
doSomething();
return;
case 1:
__exceptionState = 2;
var e = __exception;
if ((e is String)) {
catchString();
} else if ((e is Int)) {
catchInt();
} else {
catchDynamic();
}
return;
case 2: // default one: rethrow
throw __exception;
}
} catch (e:Dynamic) {
if (__exceptionState == 2)
throw e; // rethrow
__state = __exceptionState;
__exception = e;
}
} while (true);
It's pretty much the same as your snippet, except try..catch
is not getenerated, but state machine is executed inside of a try...catch
instead when entering corresponding state.