wast2json
converts a .wast
file to a .json
file, and a collection of
associated .wat
file and .wasm
files.
# parse spec-test.wast, and write files to spec-test.json. Modules are written
# to spec-test.0.wasm, spec-test.1.wasm, etc.
$ bin/wast2json spec-test.wast -o spec-test.json
The wast format is described in the spec
interpreter.
It is an extension of the .wat
text format, with additional commands for
running scripts. The syntax is repeated here:
script: <cmd>*
cmd:
<module> ;; define, validate, and initialize module
( register <string> <name>? ) ;; register module for imports
module with given failure string
<action> ;; perform action and print results
<assertion> ;; assert result of an action
<meta> ;; meta command
module:
...
( module <name>? binary <string>* ) ;; module in binary format (may be malformed)
( module <name>? quote <string>* ) ;; module quoted in text (may be malformed)
action:
( invoke <name>? <string> <expr>* ) ;; invoke function export
( get <name>? <string> ) ;; get global export
assertion:
( assert_return <action> <result>* ) ;; assert action has expected results
( assert_trap <action> <failure> ) ;; assert action traps with given failure string
( assert_exhaustion <action> <failure> ) ;; assert action exhausts system resources
( assert_malformed <module> <failure> ) ;; assert module cannot be decoded with given failure string
( assert_invalid <module> <failure> ) ;; assert module is invalid with given failure string
( assert_unlinkable <module> <failure> ) ;; assert module fails to link
( assert_trap <module> <failure> ) ;; assert module traps on instantiation
result:
( <val_type>.const <numpat> )
numpat:
<value> ;; literal result
nan:canonical ;; NaN in canonical form
nan:arithmetic ;; NaN with 1 in MSB of payload
meta:
( script <name>? <script> ) ;; name a subscript
( input <name>? <string> ) ;; read script or module from file
( output <name>? <string>? ) ;; output module to stout or file
wast2json
supports all of this format except the meta
commands. The meta
commands are not used in any of the spec tests.
The JSON format has the following structure:
{"source_filename": <string>,
"commands": [ <commands>* ] }
The source_filename
is the name of the input .wast
file. Each command is a
JSON object. They all have the following structure:
{"type": <string>, "line": <number>, ...}
The line
property is the line in the .wast
file where this command is
defined. The type
property can be one of the following. Given the string, the
rest of the command object has the given structure:
type | extra properties |
---|---|
"module" | {..., "name": <string>, "filename": <string>} |
"action" | {..., "action": <action>} |
"assert_return" | {..., "action": <action>, "expected": <expected>} |
"assert_exhaustion" | {..., "action": <action>, "text": <string>} |
"assert_trap" | {..., "action": <action>, "text": <string>} |
"assert_invalid" | {..., "filename": <string>, "text": <string>, "module_type": <module_type>} |
"assert_malformed" | {..., "filename": <string>, "text": <string>, "module_type": <module_type>} |
"assert_uninstantiable" | {..., "filename": <string>, "text": <string>, "module_type": <module_type>} |
"assert_unlinkable" | {..., "filename": <string>, "text": <string>, "module_type": <module_type>} |
"register" | {..., "name": <string>, "as": <string>} |
An action represents the wast action
: either an "invoke" or a "get" command.
An action can be run with or without an associated assertion.
If this is an "invoke" action, the "field" property represents the name of the exported function to run. If this is a "get" action, the "field" property represents the name of the exported global to access.
An action has an optional module name. If the name is provided, this is the module to run the action on. If the name is not provided, the action is run on the most recently instantiated module.
The complete format for an "invoke" action command is:
{
"type": "invoke",
("module": <string>)?,
"field": <string>,
"args": <const_vector>
}
The "args" property represents the parameters to pass to the exported function.
The complete format for a "get" action command is:
{
"type": "get",
("module": <string>)?,
"field": <string>
}
A const vector is an array of consts.
[<const>]
A const is a constant value. It contains both a type and a value. The following types are supported, with the given JSON format:
type | JSON format |
---|---|
"i32" | {"type": "i32", "value": <string>} |
"i64" | {"type": "i64", "value": <string>} |
"f32" | {"type": "f32", "value": <string>} |
"f64" | {"type": "f64", "value": <string>} |
The reference-types
proposal adds three more valid types. In each case the
value can either be an integer or "null"
:
type | JSON format |
---|---|
"externref" | {"type": "externref", "value": <string>} |
"funcref" | {"type": "funcref", "value": <string>} |
"exnref" | {"type": "exnref", "value": <string>} |
The simd
proposal adds another type, with a slightly different syntax.
{
"type": "v128",
"lane_type": "i8" | "i16" | "i32" | "f32" | "f64",
"value": [ (<string>,)* ]
}
All numeric value are stored as strings, since JSON numbers are not guaranteed
to be precise enough to store all Wasm values. Values are always written as
decimal numbers. For example, the following const has the type "i32" and the
value 34
.
("type": "i32", "value": "34"}
For floats, the numbers are written as the decimal encoding of the binary
representation of the number. For example, the following const has the type
"f32" and the value 1.0
. The value is 1065353216
because that is equivalent
to hexadecimal 0x3f800000
, which is the binary representation of 1.0
as a
32-bit float.
("type": "f32", "value": "1065353216"}
A "v128" value stores each of its lanes as a separate string. The number of lanes depends on the "lane_type" property:
"lane_type" | #lanes |
---|---|
"i8" | 16 |
"i16" | 8 |
"i32" | 4 |
"i64" | 2 |
"f32" | 4 |
"f64" | 2 |
For example, a "v128" const with lane type "i32" and lanes 0
, 1
, 2
, 3
would be written:
{
"type": "v128",
"lane_type": "i32",
"value": ["0", "1", "2", "3"]
}
Expected values are similar to a const vector. They are always arrays, and they typically contain const values. For example, the following expected value is the same as a const vector, and has the type "i32" and the value "1":
[
{"type": "i32", "value": "1"}
]
However, an expected value can also contain a pattern. In particular, for "f32" and "f64" types (and simd lanes), the pattern can also be "nan:canonical" or "nan:arithmetic". For example, the following expected value has the type "f32" and must be a canonical NaN:
[
{"type": "f32", "value": "nan:canonical"}
]
The simd
proposal extends this pattern for multiple lanes, where each lane
can be a const or a pattern. For example, the following expected value has four
lanes, each of type "f32", two of which have const values (lanes 0 and 2), and
two of which are patterns (lanes 1 and 3):
[
{
"type": "v128",
"lane_type": "f32",
"value": [
"0",
"nan:arithmetic",
"0",
"nan:arithmetic",
]
}
]
The multi-value
proposal allows for multiple return values from a function,
which is why the expected values is always an array. For example, the following
example has two expected values:
[
{"type": "i32", "value": "1"},
{"type": "i64", "value": "2"}
]
The "module" JSON command represents the wast module
command. It compiles and
instantiates a new module, which then becomes the default target for future
assertions.
The complete format for the "module" command is:
{
"type": "module",
"line": <number>,
("name": <string>,)?
"filename": <string>
}
Optionally, the module can be given a name via the "name" property. If given, this name can be used in action commands to refer to this module instead of the most recently instantiated one.
The "filename" property specifies the path to the .wasm
file for this module.
The path is always relative to the JSON file.
{
"type": "action",
"line": <number>,
"action": <action>
}
The "action" JSON command represents a wast action (either invoke
or get
)
that does not have an associated assertion).
{
"type": "assert_return",
"line": <number>,
"action": <action>
"expected": <expected>
}
The "assert_return" JSON command represents the wast assert_return
command.
It runs an action and checks that the result is an expected value.
{
"type": "assert_exhaustion",
"line": <number>,
"action": <action>,
"text": <string>
}
The "assert_exhaustion" JSON command represents the wast assert_exhaustion
command. It runs an action, and checks whether this produces a stack overflow.
The "text" property specifies the text expected by the rerefence interpreter.
{
"type": "assert_trap",
"line": <number>,
"action": <action>,
"text": <string>
}
The "assert_trap" JSON command represents the action form of the wast
assert_trap
command. The wast format also has an assert_trap
that operates
on a module which is called "assert_uninstantiable" in the JSON format.
The "assert_trap" command runs an action with the expectation that it will trap.
The "text" property specifies the text expected by the rerefence interpreter.
{
"type": "assert_invalid",
"line": <number>,
"filename": <string>,
"text": <string>,
"module_type": "binary" | "text"
}
The "assert_invalid" JSON command represents the wast assert_invalid
command.
It reads a module with the expectation that it will not validate.
The "filename" property specifies the path to the .wasm
or .wat
file for
this module. The path is always relative to the JSON file.
The "text" property specifies the error text expected by the reference interpreter.
The "module_type" property specifies whether this module is in text or binary format.
{
"type": "assert_malformed",
"line": <number>,
"filename": <string>,
"text": <string>,
"module_type": "binary" | "text"
}
The "assert_malformed" JSON command represents the wast assert_malformed
command. It reads a module with the expectation that it is malformed. Note that
this is different than being invalid; a malformed module is one that doesn't
obey the syntax rules of the binary or text format.
The "filename" property specifies the path to the .wasm
or .wat
file for
this module. The path is always relative to the JSON file.
The "text" property specifies the error text expected by the reference interpreter.
The "module_type" property specifies whether this module is in text or binary format.
{
"type": "assert_uninstantiable",
"line": <number>,
"filename": <string>,
"text": <string>,
"module_type": "binary" | "text"
}
The "assert_uninstantiable" JSON command represents the module form of the wast
assert_trap
command. The wast format also has an assert_trap
command that
runs an action, which is called "assert_trap" in the JSON format.
An uninstantiable module is one where the linking step succeeds, but the start function traps.
The "filename" property specifies the path to the .wasm
or .wat
file for
this module. The path is always relative to the JSON file.
The "text" property specifies the error text expected by the reference interpreter.
The "module_type" property specifies whether this module is in text or binary format.
{
"type": "assert_unlinkable",
"line": <number>,
"filename": <string>,
"text": <string>,
"module_type": "binary" | "text"
}
The "assert_unlinkable" JSON command represents the wast assert_unlinkable
command. It reads a module with the expectation that it will not link with the
other modules provided; i.e., one of its imports is not provided or does not
match.
The "filename" property specifies the path to the .wasm
or .wat
file for
this module. The path is always relative to the JSON file.
The "text" property specifies the error text expected by the reference interpreter.
The "module_type" property specifies whether this module is in text or binary format.
{
"type": "register",
("name": <string>,)?
"as": <string>
}
The "register" JSON command represents the wast register
command. It
registers the exports of the given module as a given name.
The "name" property specifies the module to be registered. If it is empty, the most recently instantiated module is used.
The "as" property specifies the name to use in the registry. For example, if a module is registered as:
{
"type": "register",
"as": "my_module"
}
Then its exports can be imported as:
(import "my_module" "my_export" ...)
Assume we have the following .wast
file:
(module
(func (export "add") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add)
(func (export "trap")
unreachable)
)
(assert_return (invoke "add" (i32.const 11) (i32.const 22)) (i32.const 33))
(assert_trap (invoke "trap") "unreachable")
(assert_malformed
(module quote "(modulee)")
"syntax error"
)
The following JSON file will be generated (with added whitespace for clarity):
{
"source_filename": "example.wast",
"commands": [
{
"type": "module",
"line": 1,
"filename": "example.0.wasm"
},
{
"type": "assert_return",
"line": 11,
"action": {
"type": "invoke",
"field": "add",
"args": [
{"type": "i32", "value": "11"},
{"type": "i32", "value": "22"}
]
},
"expected": [
{"type": "i32", "value": "33"}
]
},
{
"type": "assert_trap",
"line": 13,
"action": {
"type": "invoke",
"field": "trap",
"args": []
},
"text": "unreachable",
},
{
"type": "assert_malformed",
"line": 16,
"filename": "example.1.wat",
"text": "syntax error",
"module_type": "text"
}
]
}