try-as
v1.0.1
Published
Exception handling for AssemblyScript
Maintainers
Readme
Installation
npm install try-asAdd the transform to your asc build and load it last.
asc assembly/index.ts --transform try-asOr in asconfig.json:
{
"options": {
"transform": ["try-as"]
}
}If you use multiple transforms, keep try-as last.
Usage
try-as rewrites try/catch/finally, throw, abort, unreachable, and selected stdlib throw paths so they can be handled through a consistent Exception object.
import { Exception } from "try-as";
try {
throw new Error("boom");
} catch (e) {
const err = e as Exception;
console.log(err.toString()); // Error: boom
} finally {
console.log("done");
}How It Works
try-as is a source-to-source transform. It analyzes the AssemblyScript call graph, rewrites exception-producing code paths into helper state updates, and lowers each try/catch/finally into explicit control-flow checks.
AssemblyScript source
-> source linker finds try blocks, throwing calls, methods, imports, and re-exports
-> exception-aware functions/methods are marked
-> throw/abort/unreachable are rewritten to helper state writes
-> try/catch/finally becomes explicit do/break + catch-state checks
-> catch receives a rebuilt Exception objectthrow / abort / unreachable
-> __ErrorState.error / __AbortState.abort / __UnreachableState.unreachable
-> __ExceptionState.Failures++
-> generated break/return exits the current rewritten scope
-> nearest transformed catch checks __ExceptionState.shouldCatch(mask)
-> new __Exception(__ExceptionState.Type) reconstructs the caught valueConceptually, code like this:
try {
mightFail();
} catch (e) {
trace((e as Exception).toString());
}is lowered into code shaped like this:
do {
__try_mightFail();
if (__ExceptionState.Failures > 0) break;
} while (false);
if (__ExceptionState.shouldCatch(/* throw|abort|unreachable */ <i32>14)) {
let e = new __Exception(__ExceptionState.Type);
__ExceptionState.Failures--;
trace((e as Exception).toString());
}The exact generated AST is more verbose, but that is the core model: transformed calls write shared exception state, generated control flow propagates it, and catch reconstructs a typed Exception.
Exception API
import { Exception, ExceptionType } from "try-as";Exception.type: ExceptionTypeException.toString(): stringException.is<T>(): boolException.as<T>(): TException.clone(): ExceptionException.rethrow(): never
Exception.as<T>() supports Error subclasses, other managed objects, and primitive payloads like i32, bool, and f64.
Exception.rethrow() uses the runtime method body.
If err is statically typed as Exception, throw err; is rewritten as err.rethrow();.
Other identifier throws still use the generated __try_rethrow() / rethrow() / raw throw fallback chain when available.
ExceptionType:
NoneAbortThrowUnreachable
Examples
Catch abort and throw
import { Exception, ExceptionType } from "try-as";
try {
abort("fatal");
} catch (e) {
const err = e as Exception;
if (err.type == ExceptionType.Abort) {
console.log(err.toString()); // abort: fatal
}
}Type-safe custom errors
import { Exception } from "try-as";
class MyError extends Error {
constructor(message: string) {
super(message);
}
}
try {
throw new MyError("typed");
} catch (e) {
const err = e as Exception;
if (err.is<MyError>()) {
const typed = err.as<MyError>();
console.log(typed.message);
}
}Throwing non-Error values
throw is not limited to Error.
import { Exception } from "try-as";
class PlainThing {
constructor(public label: string) {}
toString(): string {
return this.label;
}
}
try {
throw new PlainThing("plain");
} catch (e) {
const err = e as Exception;
if (err.is<PlainThing>()) {
const value = err.as<PlainThing>();
console.log(value.label); // plain
}
}Rethrow behavior
import { Exception } from "try-as";
try {
// risky code
} catch (e) {
const err = e as Exception;
if (!err.is<Error>()) {
throw err; // alias of err.rethrow() when `err` is typed as Exception
}
}Selective catch kinds
Use a // @try-as: ... comment immediately above a try to control which transformed exception kinds that catch should handle.
Accepted values are throw, abort, and unreachable, comma-separated in that exact format.
import { Exception } from "try-as";
try {
// @try-as: throw,abort
try {
abort("selected");
} catch (e) {
console.log((e as Exception).toString()); // abort: selected
}
} catch (_) {
// only runs if the inner catch does not select that exception kind
}Catching stdlib exceptions
Stdlib exceptions such as missing map keys, empty array pops, out-of-range string access, and malformed URI decode errors are catchable.
import { Exception } from "try-as";
try {
new Map<string, string>().get("missing");
} catch (e) {
const err = e as Exception;
console.log(err.toString()); // Error: Key does not exist
}Limitations
- The selective catch directive must be written exactly as
// @try-as: throw,abort,unreachablewith the chosen kinds, immediately above thetry. - Runtime/internal trap paths are intentionally not rewritten.
- Exceptions from these internals are not catchable by
try-as:~lib/rt~lib/shared~lib/wasi_~lib/performance
- This library handles transformed throw/abort flows, not low-level Wasm traps like out-of-bounds memory faults.
throw err;becomeserr.rethrow();whenerris statically typed asException.- Other identifier throws still use the generated
__try_rethrow()/rethrow()fallback path when available.
Debugging
DEBUG=1enables transform diagnostics.WRITE=pathA,pathBwrites transformed source snapshots as*.tmp.ts.
Example:
DEBUG=1 WRITE=./assembly/test.ts,~lib/map asc assembly/test.ts --transform try-asTransform Modes
TRY_AS_REWRITE_STDLIB=0disables stdlib throw rewriting.TRY_AS_IMPORT_SCOPE=userinjects helper imports only into user sources (allby default).TRY_AS_DIAGNOSTICS=1prints the active mode configuration at transform time.
Example:
TRY_AS_REWRITE_STDLIB=0 TRY_AS_IMPORT_SCOPE=user TRY_AS_DIAGNOSTICS=1 asc assembly/index.ts --transform try-asContributing
npm run build:transform
npm test
npm run formatLicense
This project is distributed under the MIT license.
Contact
- Issues: https://github.com/JairusSW/try-as/issues
- Repository: https://github.com/JairusSW/try-as
- Email: [email protected]
- Website: https://jairus.dev
