tag: Wasm definition

The tag definition declares an exception type that can be thrown in the module.

Try it

(module
  ;; Declare an exception tag $my_error with two i32 parameters
  (tag $my_error (param i32) (param i32))

  ;; Import console.log
  (import "env" "log" (func $log (param i32)))

  ;; Define $try_and_catch function that tries running the $might_throw function
  ;; and catches the $my_error exception if thrown, returning the exception's
  ;; arguments from the block
  (func $try_and_catch (param $value i32)
    (block $handler (result i32) (result i32)
      (try_table (catch $my_error $handler)
        (call $might_throw (local.get $value))
      )
      (return)
    )

    ;; Log the exception's arguments
    call $log
    call $log
  )

  (func $might_throw (param $value i32)
    ;; If value is negative, throw an exception
    (local.get $value)
    (i32.const 0)
    (i32.lt_s)
    (if
      (then
        ;; Push the error code onto the stack, then throw
        (i32.const 0)       ;; error code
        (i32.const 42)      ;; error payload
        (throw $my_error)   ;; throw $my_error exception
      )
    )
  )

  ;; Export $try_and_catch function
  (export "try_and_catch" (func $try_and_catch))
)
// Import object containing console.log
const env = {
  log: console.log,
};

WebAssembly.instantiateStreaming(fetch("{%wasm-url%}"), { env }).then(
  (result) => {
    // Negative value causes function to throw
    result.instance.exports.try_and_catch(-1);
  },
);

Syntax

tag identifier parameters
tag

The tag definition type. Must always be included first.

identifier Optional

An identifying name for the tag. This must begin with a $ symbol, for example $my_error.

parameters

One or more values specifying the exception's parameters and their types. Each one consists of:

Description

The WebAssembly tag definition allows exception types to be defined for the module. Each one consists of an optional identifying name preceded by a $ symbol, followed by one or more parameter definitions. For example:

wat
(tag $my_error (param i32))

Later in the module, you can reference the exception type by its identifying name, $my_error in this case.

Note: If an identifier is not specified, the tag can be identified by its tag index number — 0 for the first specified tag, 1 for the second, etc.

For example, the following function takes an i32 parameter and evaluates whether it is less than 0 using the lt_s instruction. If this is the case, we throw an exception of type $my_error, passing it a value of 42, which could represent an error code or payload.

wat
(func $might_throw (param $value i32)
  (local.get $value)
  (i32.const 0)
  (i32.lt_s)
  (if
    (then
      (i32.const 42)
      (throw $my_error)
    )
  )
)

The thrown exception could then be handled and the payload accessed using a Wasm try/catch block. You can see an example in the Try it section at the top of the page; also see the following pages for more examples:

Handling Wasm exceptions in JavaScript

Alternatively, if the function that throws the exception is exported, the exception can be handled via a regular JavaScript try...catch statement.

For example, we could export the $might_throw function seen earlier like this:

wat
(export "might_throw" (func $might_throw))

To access the exception's payload, you have to export the tag as well:

wat
(export "my_error" (tag $my_error))

Back over in the JavaScript, we can instantiate the module and then call the exported function (via the exports object) with a number argument that is less than 0 to cause it to throw the $my_error exception. We can also access the exported tag via the exports object.

We can then access the exception payload inside the catch block via the Exception.getArg() method.

js
let myErrorTag;

WebAssembly.instantiateStreaming(fetch("module.wasm")).then((result) => {
  myErrorTag = result.instance.exports.my_error;
  try {
    result.instance.exports.might_throw(-1); // negative value causes function to throw
  } catch (e) {
    if (e instanceof WebAssembly.Exception && e.is(myErrorTag)) {
      console.log("Error code:", e.getArg(myErrorTag, 0));
    } else {
      throw e; // throw other errors
    }
  }
});

You can see this in action along with a complete explanation in our Full JavaScript exception handling example example later on.

Creating tags in JavaScript

It is also possible to create a Wasm tag from within the JavaScript host using the WebAssembly.Tag() constructor:

For example:

js
const myErrorTag = new WebAssembly.Tag({ parameters: ["i32"] });

You can import it into the module:

js
const env = {
  my_error: myErrorTag,
};

WebAssembly.instantiateStreaming(fetch("module.wasm"), { env });

Then, reference it inside the Wasm module like so:

wat
(module
  (tag $my_error (import "env" "my_error") (param i32))

  ...
)

Examples

Full JavaScript exception handling example

In this example, we demonstrate how to handle a Wasm exception defined and thrown inside a Wasm module from the associated JavaScript host.

Wasm

In our Wasm module, we start by defining an exception tag called $my_error with a single i32 parameter, then export it. We then define a function called $might_throw that takes a single i32 parameter, checks whether it is less than 0, and then throws the $my_error exception if so, with a payload of 42. Finally, we export the $might_throw function.

wat
(module
  (tag $my_error (param i32))
  (export "my_error" (tag $my_error))

  (func $might_throw (param $value i32)
    (local.get $value)
    (i32.const 0)
    (i32.lt_s)
    (if
      (then
        (i32.const 42)
        (throw $my_error)
      )
    )
  )

  (export "might_throw" (func $might_throw))
)

JavaScript

We begin our script by defining a variable called myErrorTag, grabbing a reference to an output <p> to output results into, and instantiating our Wasm module using WebAssembly.instantiateStreaming().

js
let myErrorTag;
const output = document.querySelector("p");
const wasm = WebAssembly.instantiateStreaming(fetch("{%wasm-url%}"));

When the instantiateStreaming() promise resolves, we set the myErrorTag variable equal to the exported my_error tag, then try running the exported might_throw() function inside a try block with a negative number argument to make it throw.

Inside the respective catch block, the thrown Wasm exception will be available in the error object will be a WebAssembly.Exception instance. We check that this is true using error instanceof WebAssembly.Exception, and whether the error object represents an exception of the exported myErrorTag type (using the is() method).

If both of these are true, we access the Wasm exception payload using the getArg() method and write it to the output <p>. If not, we write the error object to the output <p>.

js
wasm.then((result) => {
  myErrorTag = result.instance.exports.my_error;
  try {
    result.instance.exports.might_throw(-1);
  } catch (error) {
    if (error instanceof WebAssembly.Exception && error.is(myErrorTag)) {
      output.textContent = `Error code: ${error.getArg(myErrorTag, 0)}`;
    } else {
      output.textContent = `Error: ${error}`; // report other errors
    }
  }
});

Result

Specifications

This feature does not appear to be defined in any specification.

Browser compatibility

See also