feat: Implement WebAssembly exception handling (try-catch-finally) #2
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes AssemblyScript#302.
Related: AssemblyScript#2956, AssemblyScript#484, AssemblyScript#447.
Changes proposed in this pull request:
⯈ Implemented
throwstatement - Compiles to WebAssembly's nativethrowinstruction using a global$errortag that carries an i32 pointer to Error objects⯈ Implemented
try-catchblocks - Full support for catching exceptions with proper catch variable binding, flow analysis, and nested try-catch structures⯈ Implemented
try-finallyandtry-catch-finally- Complete finally support including the complex case ofreturnstatements inside try/catch blocks, using a pending action pattern to ensure finally always runs before control flow exitsImplementation Details
Exception Tag:
$errorcarrying an i32 pointer to Error objectensureExceptionTag()when first neededThrow Statement (
compileThrowStatement):Feature.ExceptionHandlingis enabled, generatesmodule.throw("$error", [valueExpr])abort()when feature is disabled (preserves existing behavior)FlowFlags.Throws | FlowFlags.Terminateson the flowTry-Catch (
compileTryStatement):try/catchblocks via Binaryenmodule.pop()to retrieve the exception value_BinaryenLocalSetto avoid shadow stack interference with pop placementTry-Finally with Return Support:
Uses a "pending action" pattern to defer returns until after finally executes:
pendingActionLocal(i32): tracks pending action (0=none, 1=return)pendingValueLocal: stores the pending return valueReturn in finally block overrides any pending return from try/catch:
Structure generated:
Core changes in
src/compiler.ts:exceptionTagEnsuredfield andensureExceptionTag()method for lazy tag creationcompileThrowStatement()updated to usemodule.throw()when feature enabledcompileTryStatement()completely rewritten with full try-catch-finally supportcompileReturnStatement()updated to check for try-finally contextSupporting changes in
src/flow.ts:tryFinallyPendingActionLocal- local index for pending action trackingtryFinallyPendingValueLocal- local index for pending return valuetryFinallyDispatchLabel- label to branch to for finally dispatchtryFinallyReturnType- return type for the pending valueisInTryFinallygetter andgetTryFinallyContext()methodTest Coverage
Basic Tests:
testThrow()- Basic throw statementtestTryCatch()- Basic try-catchtestCatchVar()- Accessing caught exception variable (e.message)testNoThrow()- Try-catch when no exception is throwntestFinally()- Basic finally blocktestNested()- Nested try-catch blocksFinally with Return Tests:
testReturnInCatchFinally()- Return in catch with finally (finally must run first)testTryCatchFinally()- Try-catch-finally without return in catchtestFinallyWithException()- Finally runs even when exception propagatestestFinallyNormalCompletion()- Finally with no exceptiontestReturnFromTry()- Return from try block with finallytestMultipleReturnsWithFinally()- Multiple return points with finallyClass-Based Tests:
CustomError- Custom error class extending ErrorResource- Resource management class with dispose patternCalculator- Class with try-catch in methods (divide,safeDivide)Outer/Inner- Nested class exception handlingStateMachine- State machine with exception-based error handlingCounter- Counter class with exception limitComplex Tests:
testArrayWithExceptions()- Array operations with exceptionstestRethrowWithFinally()- Rethrow with finally (verifies finally runs)testDeepNesting()- Deeply nested try-catch-finally tracking execution orderReturn in Finally Tests:
testReturnInFinally()- Return in finally overrides return in trytestReturnInFinallyOverridesCatch()- Return in finally overrides return in catchtestReturnInFinallySuppressesException()- Return in finally suppresses thrown exceptionLimitations
This implementation has one known limitation:
Usage
# Enable exception handling feature asc myfile.ts --enable exception-handling