|
From: Kevin K. <kev...@gm...> - 2019-02-05 18:53:01
|
One more thing with returns. For [return -level N -code C $value] with N>1, there's some jiggery-pokery at the [return] site, to set iPtr->returnLevel to N, iPtr->returnCode to C, and then return TCL_RETURN. That means that there needs to be no check for the level at the call site until we've detected that we're in the TCL_RETURN path. For optimization, I'd like to keep track of which exception codes (ERROR, RETURN, BREAK, CONTINUE, other) that a proc might return, so as to be able to remove excess checks from after the invoke. I think that the base case before optimization will have to look something like: result ←invoke(callframe, command, arg1, ...) callframe ← extractCallframe(result) result ← retrieveResult(result) jumpMaybe exceptionPath, result jump normalPath normalPath: result ←extractMaybe(result) ... execution continues ... exceptionPath: # NOTE: This is where the hooks for escape to interpreted code, or for dynamic recompilation, would go. # QUESTION: Do we need to check iPtr->execEnvPtr->rewind? result ← extractFail(result) # If the innermost exception range is a loop (track this in translate.tcl): r1 ← resultIsBreak(result); # new test for TCL_BREAK jumpTrue breakPC, r1 jump e1 e1: r1 ← resultIsContinue(result); new test for TCL_CONTINUE jumpTrue continuePC, r1 jump e2 # end of code handling the loop exception range e2: # QUESTION: Code to append to the backtrace (analogous to TclLogCommandInfo) needed? # QUESTION: Do we need to check TclCanceled(interp)? TclLimitExceeded(iPtr->limit)? # If there's an enclosing catch, jump to it, and otherwise fall out to the error pathway. jump enclosingCatchOrError In addition, once we're on the error path of a proc, we need to check for abnormal returns. In bytecode, this goes through TEOEx_ByteCodeCallback. It checks for whether -level has reached 0 - if it hasn't, we'll be passing the exception through to the proc's caller. When -level has reached zero, if the return code is TCL_RETURN, it unboxes the options to replace with whatever they were hiding. Otherwise, if the result is not TCL_OK or TCL_ERROR, it formats 'invoked "break" outside a loop' or 'invoked "continue" outside a loop' or 'command returned bad code: C' and throws that as an error. I think we can detect this case statically because we have the exception range at hand when compiling for the invoke, so if there's neither a loop nor a catch exception range and a command might return BREAK, CONTINUE or [other], have the test for the result code jump to an 'initException / jump error' sequence. So we have: -- on return - we need to handle -level by updating iPtr->returnLevel and iPtr->returnCode. -- after an [invoke] etc. returns - we need to check for BREAK/CONTINUE (vs the innermost exception range, with different handling for loop and catch), have the special handling for BREAK/CONTINUE/[other] to turn into errors if nothing catches them, and have all the logic to unpack -level when the result code is TCL_RETURN. It would be desirable to have FAIL divide into multiple subtypes: ´could be an error' 'could be a break' 'could be a continue' 'could be a return' 'could be an unusual return code', so as to be able to optimize out the checks for the impossible cases. These subtypes wouldn't need any separate internal rep (and I could have the typename be FAIL for all of them) since they would all be 'integer Tcl result code, plus whatever baggage that carries in the interpreter internals' |