nadako / haxe-coroutines

41 stars 5 forks source link

Transformation: `try...catch` #7

Open RealyUniqueName opened 6 years ago

RealyUniqueName commented 6 years ago

Source

try {
    doSomething();
} catch(e:String) {
    catchString();
} catch(e:Int) {
    catchInt();
} catch(e:Dynamic) {
    catchDynamic();
}

Short legend for next snippet:

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();
nadako commented 6 years ago

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);
RealyUniqueName commented 6 years ago

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.