wasm_error (wasm v0.1.0)

View Source

Structured errors and traps.

Read this when you are deciding what to match on. Every failure you get back from the runtime is one of five classes, matching the distinctions the WebAssembly specification test suite draws:

  • malformed - the binary could not be decoded (assert_malformed)
  • invalid - it decoded but failed validation (assert_invalid)
  • link - instantiation failed (assert_unlinkable)
  • trap - execution trapped (assert_trap)
  • exhaustion - a resource limit was hit (assert_exhaustion) Match on class and kind; both are atoms and neither is derived from module data. msg carries the canonical specification message text, so the spec driver can compare it against the expected failure, and ctx carries enough to diagnose a module: which function, which byte offset, and the enclosing block path. Use format/1 for logs.

Inside the library the decoder and validator signal by throw/1 rather than threading {ok, _} | {error, _} through every combinator, which keeps the hot paths free of result-tuple allocation. capture/1 converts back to a value at the API boundary, so no throw ever reaches you.

Summary

Functions

Add fields to an error already caught, without losing what is there.

Run Fun, converting an internal throw into {error, Error}.

Throw: a limit stopped the guest, rather than the guest doing something wrong.

As exhaustion/1, carrying context for the report.

Render an error on one line, for your logs and test failures.

Throw: the module decodes but does not validate.

As invalid/2, carrying context for the report.

Whether this term is one of this module's errors.

Throw: instantiation cannot satisfy an import. Nothing has run yet.

As link_error/2, carrying context for the report.

Throw: the bytes are not a well-formed module.

As malformed/2, carrying context for the report.

Throw: the guest did something the specification says traps.

As trap/1, carrying context for the report.

Types

class()

-type class() :: malformed | invalid | link | trap | exhaustion.

error()

-type error() :: #{class := class(), kind := atom(), msg := binary(), ctx := map()}.

trap_reason()

-type trap_reason() ::
          unreachable | integer_divide_by_zero | integer_overflow | invalid_conversion_to_integer |
          out_of_bounds_memory_access | out_of_bounds_table_access | undefined_element |
          uninitialized_element | indirect_call_type_mismatch |
          {host_error, term()}.

Functions

add_context/2

-spec add_context(error(), map()) -> error().

Add fields to an error already caught, without losing what is there.

capture(Fun)

-spec capture(fun(() -> Result)) -> Result | {error, error()}.

Run Fun, converting an internal throw into {error, Error}.

It also converts unexpected Erlang exceptions into a structured internal error rather than letting them escape. A bug in this library still has to reach you as a value, because the whole point of the runtime is that hostile input cannot destabilise its caller.

capture(Fun, Ctx0)

-spec capture(fun(() -> Result), map()) -> Result | {error, error()}.

exhaustion(Kind)

-spec exhaustion(atom()) -> no_return().

Throw: a limit stopped the guest, rather than the guest doing something wrong.

Fuel, call depth, memory pages and host calls all arrive here. Distinct from a trap because the module is not at fault and the same call under a larger budget would succeed.

exhaustion(Kind, Ctx)

-spec exhaustion(atom(), map()) -> no_return().

As exhaustion/1, carrying context for the report.

format/1

-spec format(error()) -> iolist().

Render an error on one line, for your logs and test failures.

invalid(Kind, Msg)

-spec invalid(atom(), binary()) -> no_return().

Throw: the module decodes but does not validate.

invalid(Kind, Msg, Ctx)

-spec invalid(atom(), binary(), map()) -> no_return().

As invalid/2, carrying context for the report.

is_error/1

-spec is_error(term()) -> boolean().

Whether this term is one of this module's errors.

malformed(Kind, Msg)

-spec malformed(atom(), binary()) -> no_return().

Throw: the bytes are not a well-formed module.

Decoding only. A module that decodes but does not type check is invalid/2, and the distinction is what the specification suite asserts on: assert_malformed and assert_invalid are different directives.

malformed(Kind, Msg, Ctx)

-spec malformed(atom(), binary(), map()) -> no_return().

As malformed/2, carrying context for the report.

trap(Reason)

-spec trap(trap_reason()) -> no_return().

Throw: the guest did something the specification says traps.

A trap is defined behaviour, not a defect. Every one of them is reachable from valid WebAssembly and an embedder should expect them.

trap(Reason, Ctx)

-spec trap(trap_reason(), map()) -> no_return().

As trap/1, carrying context for the report.