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.