Variable


You can define global or local variables in Wat. In a function, you can use local to declare local variables. Local variables are initialized to the appropriate zero value for their type (0 for integers, +0. for floating-point).

get_local reads the current value of a local variable and pushes to the stack. set_local pops and sets the value to a local variable. tee_local like set_local, but also pushes the value back to the stack.

For example, if you want to write Wat like the C code:

int a = 10;
int b = 20;
int c = a + b;

Using only get_local and set_local would be:

(module
    (import "env" "log" (func $log (param i32)))
    (func $main
        (local $a i32) (local $b i32) (local $c i32)                                
        i32.const 10
        set_local $a
        i32.const 20
        set_local $b
        get_local $a
        get_local $b
        i32.add
        set_local $c
        get_local $c
        call $log
    )
    (start $main)
)

Variable names are for readability. Local variables occupy a single index space local to the function. Indices start from 0. The above code can be shorten by tee_local.

(module
    (import "env" "log" (func $log (param i32)))
    (func $main
        (local $a i32) (local $b i32) (local $c i32)                                
        i32.const 10
        tee_local $a
        i32.const 20
        tee_local $b
        i32.add
        tee_local $c
        call $log
    )
    (start $main)
)

You can use global to declare global variables in the global section. Without mut, a global variable is default to immutable so you can initialize its value when declaring. get_local gets the current value of a global variable and pushes to the stack.

(module
    (import "env" "log" (func $log (param f32)))
    (global $PI f32 (f32.const 3.14159))
    (func $main
        get_global $PI
        call $log
    )
    (start $main)
)

You guessed it! The name of a global variable is for readability. Global variables actually are accessed via an integer index into the module-defined global index space.

You can combine global with mut to define mutable global variables. Then, use set_local to set the current value of a global variable.

(module
    (import "env" "log" (func $log (param f32)))
    (global $PI (mut f32) (f32.const 3.14159))
    (func $main
        get_global $PI
        call $log
        f32.const 3.14
        set_global $PI
        get_global $PI
        call $log
    )
    (start $main)
)

Imported variables from JavaScript are global variables.

(module
    (import "env" "PI" (global $PI f32))
    (import "env" "log" (func $log (param f32)))
    (func $main
        get_global $PI
        call $log
    )
    (start $main)
)

An imported variable is the property of the corresponding object.

const importObj = {
    env: {
        PI : 3.14159,
        log(n) {
            console.log(n);
        }
    }
};
WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj);

The value of the imported variable PI is a number. In this case, it must be an immutable global variable in WebAssembly. If you want to import a mutable variable:

(module
  (import "env" "PI" (global $PI (mut f32)))
    (import "env" "log" (func $log (param f32)))
    (func $main
        get_global $PI
        call $log
        f32.const 3.14
        set_global $PI
        get_global $PI
        call $log       
    )
    (start $main)
)

You should create an instance of WebAssembly.Global.

const importObj = {
    env: {
        PI : new WebAssembly.Global({value : 'f32', mutable : true}, 3.14159),
        log(n) {
            console.log(n);
        }
    }
};
WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj)
           .then(_ => {
               console.log(importObj.env.PI.value);
           });

In this case, if you modify the value of the global variable, the value of WebAssembly.Global would reflect the modification.

You can export a global variable.

(module
    (global $PI f32 (f32.const 3.14159))
    (export "PI" (global $PI))
    (func $nop)
)

(The (func $nop) here is necessary. It seems that you have to declare at least one function if a module want to export something. Without (func $nop) in the above example, an error will happen.)

The exported variable is a property of the WebAssembly.Instance object and it's an instance of WebAssembly.Global. You can access its value through the value property.

WebAssembly.instantiateStreaming(fetch('program.wasm'))
           .then(prog => console.log(prog.instance.exports.PI.value));

Only immutable variables can be exported. An error happens if not.