blob: c71331790d70aab541045f996408c2bb5f7b7b18 [file] [log] [blame] [edit]
import * as assert from "../assert.js";
import { compile, instantiate } from "./wast-wrapper.js";
function module(bytes, valid = true) {
let buffer = new ArrayBuffer(bytes.length);
let view = new Uint8Array(buffer);
for (let i = 0; i < bytes.length; ++i) {
view[i] = bytes.charCodeAt(i);
}
return new WebAssembly.Module(buffer);
}
function testStructDeclaration() {
instantiate(`
(module
(type (struct))
)
`);
instantiate(`
(module
(type (struct (field i32)))
)
`);
instantiate(`
(module
(type (struct (field i32) (field i32)))
)
`);
instantiate(`
(module
(type (struct (field (mut i32))))
)
`);
instantiate(`
(module
(type $Point (struct (field (mut i32) (mut i32))))
(type $Line (struct (field (mut (ref $Point)) (mut (ref $Point)))))
)
`);
instantiate(`
(module
(type (struct (field i32)))
(func (result structref) (ref.null 0))
)
`);
/*
* too many fields
* (module
* (type (struct (field i32)))
* )
*/
assert.throws(
() =>
module(
"\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x07\x01\x5f\xff\xff\xff\x7f\x00"
),
WebAssembly.CompileError,
"WebAssembly.Module doesn't parse at byte 16: number of fields for struct type at position 0 is too big 268435455 maximum 10000 (evaluating 'new WebAssembly.Module(buffer)')"
);
/*
* invalid mutability
* (module
* (type (struct (field (mut i32))))
* )
*/
assert.throws(
() =>
module(
"\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x05\x01\x5f\x01\x7f\x02"
),
WebAssembly.CompileError,
"WebAssembly.Module doesn't parse at byte 15: invalid Field's mutability: 0x02 (evaluating 'new WebAssembly.Module(buffer)')"
);
/*
* struct payload size overflows unsigned type
* (module
* (type (struct (field (mut i64)) (field (mut i64)) ... (field (mut i64))))
* )
*/
assert.throws(
() =>
module(
"\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x07\x01\x5f\x80\x80\x2c\x7e\x01"
),
WebAssembly.CompileError,
"WebAssembly.Module doesn't parse at byte 15: number of fields for struct type at position 0 is too big 720896 maximum 10000 (evaluating 'new WebAssembly.Module(buffer)')"
);
// Invalid subtyping for structref.
assert.throws(
() =>
compile(`
(module
(type (array i32))
(func (result structref) (ref.null 0))
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null <array:0>) is not a (ref null struct), in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
);
// Invalid subtyping for structref.
assert.throws(
() =>
compile(`
(module
(type (struct))
(func (result structref) (ref.null 0))
(func (result (ref null 0)) (call 0))
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null struct) is not a (ref null <struct:0>), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);
}
function testStructJS() {
// Wasm-allocated structs can flow to JS and back.
{
let m = instantiate(`
(module
(type (struct))
(func (export "f") (result (ref null 0))
(ref.null 0))
)
`);
assert.eq(m.exports.f(), null);
}
{
let m = instantiate(`
(module
(type (struct))
(func (export "f") (param (ref null 0)))
)
`);
m.exports.f(null);
}
// Test cast failure
assert.throws(
() => {
let m = instantiate(`
(module
(type (struct))
(global (export "g") (mut (ref null 0)) (ref.null 0))
)
`);
m.exports.g.value = 42;
},
TypeError,
"Argument value did not match the reference type"
)
}
function testStructNew() {
{
/*
* (module
* (type $Empty (struct))
* (func (export "main")
* (drop
* (struct.new $Empty)
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x06\x02\x60\x00\x00\x5f\x00\x03\x02\x01\x00\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x08\x01\x06\x00\xfb\x00\x01\x1a\x0b"));
instance.exports.main();
}
instantiate(`
(module
(type $Empty (struct))
(func (export "main")
(drop
(struct.new $Empty)
)
)
)
`).exports.main();
instantiate(`
(module
;; Also test with subtype.
(type (sub (struct)))
(type $Empty (sub 0 (struct)))
(func (export "main")
(drop
(struct.new $Empty)
)
)
)
`).exports.main();
{
/*
* (module
* (type $Point (struct (field $x i32) (field $y i32)))
* (func (export "main")
* (drop
* (struct.new $Point (i32.const 19) (i32.const 37))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0a\x02\x60\x00\x00\x5f\x02\x7f\x00\x7f\x00\x03\x02\x01\x00\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x0c\x01\x0a\x00\x41\x13\x41\x25\xfb\x00\x01\x1a\x0b"));
instance.exports.main();
/*
* invalid type index for struct.new
* (module
* (type $Point (struct (field $x i32) (field $y i32)))
* (func (export "main")
* (drop
* (struct.new (i32.const 19) (i32.const 37))
* )
* )
* )
*/
assert.throws(
() =>
module(
"\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0a\x02\x60\x00\x00\x5f\x02\x7f\x00\x7f\x00\x03\x02\x01\x00\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x0c\x01\x0a\x00\x41\x13\x41\x25\xfb\x00\x03\x1a\x0b"
),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: struct.new index 3 is out of bound, in function at index 0 (evaluating 'new WebAssembly.Module(buffer)')"
);
}
{
/*
* (module
* (type $Point (struct (field $x i32) (field $y i32)))
* (func (export "main")
* unreachable
* struct.new $Point (i32.const 19) (i32.const 37)
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0a\x02\x60\x00\x00\x5f\x02\x7f\x00\x7f\x00\x03\x02\x01\x00\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x0c\x01\x0a\x00\x00\x41\x13\x41\x25\xfb\x07\x01\x0b"));
/*
* invalid type index for struct.new in unreachable context
* (module
* (type $Point (struct (field $x i32) (field $y i32)))
* (func (export "main")
* unreachable
* struct.new (i32.const 19) (i32.const 37)
* )
* )
*/
assert.throws(
() =>
module(
"\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0a\x02\x60\x00\x00\x5f\x02\x7f\x00\x7f\x00\x03\x02\x01\x00\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x0c\x01\x0a\x00\x00\x41\x13\x41\x25\xfb\x00\x02\x0b"
),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: struct.new index 2 is out of bound, in function at index 0 (evaluating 'new WebAssembly.Module(buffer)')"
);
}
instantiate(`
(module
(type $Point (struct (field $x i32) (field $y i32)))
(func (export "main")
unreachable
struct.new $Point (i32.const 19) (i32.const 37)
)
)
`);
}
function testStructNewDefault() {
instantiate(`
(module
(type $Empty (struct))
(func (export "main")
(drop
(struct.new_default $Empty)
)
)
)
`).exports.main();
instantiate(`
(module
;; Also test with subtype.
(type (sub (struct)))
(type $Empty (sub 0 (struct)))
(func (export "main")
(drop
(struct.new_default $Empty)
)
)
)
`).exports.main();
instantiate(`
(module
(type $Point (struct (field $x i32) (field $y i32)))
(func (export "main")
(drop
(struct.new_default $Point)
)
)
)
`).exports.main();
compile(`
(module
(rec (type (struct (field (ref null 0)))))
(func (param (ref null 0)))
(func
(struct.new_default 0)
(call 0))
)
`);
assert.throws(
() => compile(`
(module
(type $Point (struct (field $x (ref func))))
(func (export "main")
(drop
(struct.new_default $Point)
)
)
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't parse at byte 4: struct.new_default 0 requires all fields to be defaultable, but field 0 has type Ref, in function at index 0"
)
assert.throws(
() => compile(`
(module
(type $Point (struct (field $x i32) (field $y i32)))
(func (export "main")
(drop
(struct.new_default 3)
)
)
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: struct.new_default index 3 is out of bound, in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
)
assert.throws(
() => compile(`
(module
(type $Point (struct (field $x i32) (field $y i32)))
(func (export "main")
unreachable
struct.new_default 2
)
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: struct.new_default index 2 is out of bound, in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
)
}
function testStructGet() {
{
/*
* Point(i32)
*
* (module
* (type $Point (struct (field $x i32)))
* (func (export "main") (result i32)
* (struct.get $Point $x
* (struct.new $Point (i32.const 37))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x09\x02\x5f\x01\x7f\x00\x60\x00\x01\x7f\x03\x02\x01\x01\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x0d\x01\x0b\x00\x41\x25\xfb\x00\x00\xfb\x02\x00\x00\x0b"));
assert.eq(instance.exports.main(), 37);
}
{
let main = instantiate(`
(module
(type $Point (struct (field $x i32)))
(func (export "main") (result i32)
(struct.get $Point $x
(struct.new $Point (i32.const 37))
)
)
)
`).exports.main;
assert.eq(main(), 37);
}
{
let main = instantiate(`
(module
(type $Point (struct (field $x i32)))
(func (export "main") (result i32)
(struct.get $Point $x
(struct.new_default $Point)
)
)
)
`).exports.main;
assert.eq(main(), 0);
}
{
let main = instantiate(`
(module
;; Test subtype case as well.
(type (sub (struct (field i32))))
(type $Point (sub 0 (struct (field $x i32))))
(func (export "main") (result i32)
(struct.get $Point $x
(struct.new $Point (i32.const 37))
)
)
)
`).exports.main;
assert.eq(main(), 37);
}
{
/*
* Point(f32)
*
* (module
* (type $Point (struct (field $x f32)))
* (func (export "main") (result f32)
* (struct.get $Point $x
* (struct.new $Point (f32.const 37))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x09\x02\x5f\x01\x7d\x00\x60\x00\x01\x7d\x03\x02\x01\x01\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x10\x01\x0e\x00\x43\x00\x00\x14\x42\xfb\x00\x00\xfb\x02\x00\x00\x0b"));
assert.eq(instance.exports.main(), 37);
}
{
let main = instantiate(`
(module
(type $Point (struct (field $x f32)))
(func (export "main") (result f32)
(struct.get $Point $x
(struct.new_default $Point)
)
)
)
`).exports.main;
assert.eq(main(), 0);
}
{
/*
* Point(i64)
*
* (module
* (type $Point (struct (field $x i64)))
* (func (export "main") (result i32)
* (i64.eq
* (i64.const 37)
* (struct.get $Point $x
* (struct.new $Point (i64.const 37))
* )
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x09\x02\x5f\x01\x7e\x00\x60\x00\x01\x7f\x03\x02\x01\x01\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x10\x01\x0e\x00\x42\x25\x42\x25\xfb\x00\x00\xfb\x02\x00\x00\x51\x0b"));
assert.eq(instance.exports.main(), 1);
}
{
let main = instantiate(`
(module
(type $Point (struct (field $x i64)))
(func (export "main") (result i32)
(i64.eq
(i64.const 0)
(struct.get $Point $x
(struct.new_default $Point)
)
)
)
)
`).exports.main;
assert.eq(main(), 1);
}
{
/*
* Point(f64)
*
* (module
* (type $Point (struct (field $x f64)))
* (func (export "main") (result f64)
* (struct.get $Point $x
* (struct.new $Point (f64.const 37))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x09\x02\x5f\x01\x7c\x00\x60\x00\x01\x7c\x03\x02\x01\x01\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x14\x01\x12\x00\x44\x00\x00\x00\x00\x00\x80\x42\x40\xfb\x00\x00\xfb\x02\x00\x00\x0b"));
assert.eq(instance.exports.main(), 37);
}
{
let main = instantiate(`
(module
(type $Point (struct (field $x f64)))
(func (export "main") (result f64)
(struct.get $Point $x
(struct.new_default $Point)
)
)
)
`).exports.main;
assert.eq(main(), 0);
}
{
/*
* Point(externref)
*
* (module
* (type $Point (struct (field $x externref)))
* (func (export "main") (param $obj externref) (result externref)
* (struct.get $Point $x
* (struct.new $Point (local.get $obj))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0a\x02\x5f\x01\x6f\x00\x60\x01\x6f\x01\x6f\x03\x02\x01\x01\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x0d\x01\x0b\x00\x20\x00\xfb\x00\x00\xfb\x02\x00\x00\x0b"));
let obj = {};
assert.eq(instance.exports.main(obj), obj);
}
{
let main = instantiate(`
(module
(type $Point (struct (field $x externref)))
(func (export "main") (result externref)
(struct.get $Point $x
(struct.new_default $Point)
)
)
)
`).exports.main;
assert.eq(main(), null);
}
{
/*
* (module
* (func (export "f") (param f64) (result f64) (local.get 0))
* )
*/
let instance1 = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x06\x01\x60\x01\x7c\x01\x7c\x03\x02\x01\x00\x07\x05\x01\x01\x66\x00\x00\x0a\x06\x01\x04\x00\x20\x00\x0b"));
/*
* Point(funcref)
*
* (module
* (type $Point (struct (field $x funcref)))
* (func (export "main") (param $p funcref) (result funcref)
* (struct.get $Point $x
* (struct.new $Point (local.get $p))
* )
* )
* )
*/
let instance2 = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0a\x02\x5f\x01\x70\x00\x60\x01\x70\x01\x70\x03\x02\x01\x01\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x0d\x01\x0b\x00\x20\x00\xfb\x00\x00\xfb\x02\x00\x00\x0b"));
let foo = instance1.exports.f;
assert.eq(instance2.exports.main(foo), foo);
}
{
let main = instantiate(`
(module
(type $Point (struct (field $x funcref)))
(func (export "main") (result funcref)
(struct.get $Point $x
(struct.new_default $Point)
)
)
)
`).exports.main;
assert.eq(main(), null);
}
{
/*
* Point(i32, i32)
*
* (module
* (type $Point (struct (field $x i32) (field $y i32)))
* (func (export "main") (result i32)
* (struct.get $Point $x
* (struct.new $Point (i32.const 19) (i32.const 37))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0b\x02\x5f\x02\x7f\x00\x7f\x00\x60\x00\x01\x7f\x03\x02\x01\x01\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x0f\x01\x0d\x00\x41\x13\x41\x25\xfb\x00\x00\xfb\x02\x00\x00\x0b"));
assert.eq(instance.exports.main(), 19);
}
{
/*
* Point(f32, f32)
*
* (module
* (type $Point (struct (field $x f32) (field $y f32)))
* (func (export "main") (result f32)
* (struct.get $Point $x
* (struct.new $Point (f32.const 19) (f32.const 37))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0b\x02\x5f\x02\x7d\x00\x7d\x00\x60\x00\x01\x7d\x03\x02\x01\x01\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x15\x01\x13\x00\x43\x00\x00\x98\x41\x43\x00\x00\x14\x42\xfb\x00\x00\xfb\x02\x00\x00\x0b"));
assert.eq(instance.exports.main(), 19);
}
{
/*
* Point(i32, f64)
*
* (module
* (type $Point (struct (field $x i32) (field $y f64)))
* (func (export "get_x") (result i32)
* (struct.get $Point $x
* (struct.new $Point (i32.const 19) (f64.const 37))
* )
* )
* (func (export "get_y") (result f64)
* (struct.get $Point $y
* (struct.new $Point (i32.const 19) (f64.const 37))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0f\x03\x5f\x02\x7f\x00\x7c\x00\x60\x00\x01\x7f\x60\x00\x01\x7c\x03\x03\x02\x01\x02\x07\x11\x02\x05\x67\x65\x74\x5f\x78\x00\x00\x05\x67\x65\x74\x5f\x79\x00\x01\x0a\x2b\x02\x14\x00\x41\x13\x44\x00\x00\x00\x00\x00\x80\x42\x40\xfb\x00\x00\xfb\x02\x00\x00\x0b\x14\x00\x41\x13\x44\x00\x00\x00\x00\x00\x80\x42\x40\xfb\x00\x00\xfb\x02\x00\x01\x0b"));
assert.eq(instance.exports.get_x(), 19);
assert.eq(instance.exports.get_y(), 37);
}
{
// unreachable context
/*
* (module
* (type $Point (struct (field $x i32) (field $y i32)))
* (func (export "main") (result i32)
* unreachable
* (struct.get $Point $x
* (struct.new $Point (i32.const 19) (i32.const 37))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0b\x02\x5f\x02\x7f\x00\x7f\x00\x60\x00\x01\x7f\x03\x02\x01\x01\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x10\x01\x0e\x00\x00\x41\x13\x41\x25\xfb\x00\x00\xfb\x02\x00\x00\x0b"));
/*
* invalid type index for struct.get in unreachable context
*
* (module
* (type $Point (struct (field $x i32) (field $y i32)))
* (func (export "main") (result i32)
* unreachable
* (struct.get $Point $x
* (struct.new $Point (i32.const 19) (i32.const 37))
* )
* )
* )
*/
assert.throws(
() =>
module(
"\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0b\x02\x5f\x02\x7f\x00\x7f\x00\x60\x00\x01\x7f\x03\x02\x01\x01\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x00\x0a\x10\x01\x0e\x00\x00\x41\x13\x41\x25\xfb\x00\x00\xfb\x02\x03\x00\x0b"
),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: struct.get index 3 is out of bound, in function at index 0 (evaluating 'new WebAssembly.Module(buffer)')"
);
}
// Test error message for invalid struct.get index.
assert.throws(
() =>
compile(`
(module
(func (result i32) (struct.get 5 0 (ref.null 0)))
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: struct.get index 5 is out of bound, in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
);
// Test error message for invalid struct.get index.
assert.throws(
() =>
compile(`
(module
(type (func))
(func (result i32) (struct.get 0 0 (ref.null 0)))
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: struct.get: invalid type index 0, in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
);
// Cannot struct.get from a structref.
assert.throws(
() =>
compile(`
(module
(type $s (struct (field i32)))
(func (result structref) (ref.null 0))
(func (result i32) (struct.get 0 0 (call 0)))
)
`),
WebAssembly.CompileError,
"struct.get structref to type (ref null struct) expected (ref null <struct:0>), in function at index 1"
);
// Cannot struct.get from an anyref.
assert.throws(
() =>
compile(`
(module
(type $s (struct (field i32)))
(func (result anyref) (ref.null 0))
(func (result i32) (struct.get 0 0 (call 0)))
)
`),
WebAssembly.CompileError,
"struct.get structref to type (ref null any) expected (ref null <struct:0>), in function at index 1"
);
// Test null checks.
assert.throws(
() => instantiate(`
(module
(type (struct (field i32)))
(func (export "f") (result i32)
(struct.get 0 0 (ref.null 0)))
)
`).exports.f(),
WebAssembly.RuntimeError,
"access to a null reference"
);
// Bottom is type valid but throws due to null.
assert.throws(
() => instantiate(`
(module
(type (struct (field i32)))
(func (export "f") (result i32)
(struct.get 0 0 (ref.null none)))
)
`).exports.f(),
WebAssembly.RuntimeError,
"access to a null reference"
);
}
function testStructSet() {
{
/*
* Point(i32)
*
* (module
* (type $Point (struct (field $x (mut i32))))
* (func $doTest (param $p (ref $Point)) (result i32)
* (struct.set $Point $x
* (local.get $p)
* (i32.const 37)
* )
* (struct.get $Point $x
* (local.get $p)
* )
* )
*
* (func (export "main") (result i32)
* (call $doTest
* (struct.new $Point (i32.const 0))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0f\x03\x5f\x01\x7f\x01\x60\x01\x64\x00\x01\x7f\x60\x00\x01\x7f\x03\x03\x02\x01\x02\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x01\x0a\x1c\x02\x10\x00\x20\x00\x41\x25\xfb\x05\x00\x00\x20\x00\xfb\x02\x00\x00\x0b\x09\x00\x41\x00\xfb\x00\x00\x10\x00\x0b"));
assert.eq(instance.exports.main(), 37);
}
{
let main = instantiate(`
(module
(type $Point (struct (field $x (mut i32))))
(func $doTest (param $p (ref $Point)) (result i32)
(struct.set $Point $x
(local.get $p)
(i32.const 37)
)
(struct.get $Point $x
(local.get $p)
)
)
(func (export "main") (result i32)
(call $doTest
(struct.new $Point (i32.const 0))
)
)
)
`).exports.main;
assert.eq(main(), 37);
}
{
let main = instantiate(`
(module
;; Test subtype case as well.
(type (sub (struct (field (mut i32)))))
(type $Point (sub 0 (struct (field $x (mut i32)))))
(func $doTest (param $p (ref $Point)) (result i32)
(struct.set $Point $x
(local.get $p)
(i32.const 37)
)
(struct.get $Point $x
(local.get $p)
)
)
(func (export "main") (result i32)
(call $doTest
(struct.new $Point (i32.const 0))
)
)
)
`).exports.main;
assert.eq(main(), 37);
}
// Test actually passing a point that is a subtype to a $Point interface.
{
let main = instantiate(`
(module
(type $Point (sub (struct (field $x (mut i32)))))
(type $Sub (sub 0 (struct (field (mut i32) (mut i32)))))
(func $doTest (result i32) (local $p (ref null $Sub))
(local.set $p (struct.new $Sub (i32.const 0) (i32.const 1)))
(struct.set $Point $x
(local.get $p)
(i32.const 37)
)
(struct.get $Point $x
(local.get $p)
)
)
(func (export "main") (result i32)
(call $doTest)
)
)
`).exports.main;
assert.eq(main(), 37);
}
{
/*
* Point(f32)
*
* (module
* (type $Point (struct (field $x (mut f32))))
* (func $doTest (param $p (ref $Point)) (result f32)
* (struct.set $Point $x
* (local.get $p)
* (f32.const 37)
* )
* (struct.get $Point $x
* (local.get $p)
* )
* )
*
* (func (export "main") (result f32)
* (call $doTest
* (struct.new $Point (f32.const 0))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0f\x03\x5f\x01\x7d\x01\x60\x01\x64\x00\x01\x7d\x60\x00\x01\x7d\x03\x03\x02\x01\x02\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x01\x0a\x22\x02\x13\x00\x20\x00\x43\x00\x00\x14\x42\xfb\x05\x00\x00\x20\x00\xfb\x02\x00\x00\x0b\x0c\x00\x43\x00\x00\x00\x00\xfb\x00\x00\x10\x00\x0b"));
assert.eq(instance.exports.main(), 37);
}
{
/*
* Point(i64)
*
* (module
* (type $Point (struct (field $x (mut i64))))
* (func $doTest (param $p (ref $Point)) (result i64)
* (struct.set $Point $x
* (local.get $p)
* (i64.const 37)
* )
* (struct.get $Point $x
* (local.get $p)
* )
* )
*
* (func (export "main") (result i64)
* (call $doTest
* (struct.new $Point (i64.const 0))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0f\x03\x5f\x01\x7e\x01\x60\x01\x64\x00\x01\x7e\x60\x00\x01\x7e\x03\x03\x02\x01\x02\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x01\x0a\x1c\x02\x10\x00\x20\x00\x42\x25\xfb\x05\x00\x00\x20\x00\xfb\x02\x00\x00\x0b\x09\x00\x42\x00\xfb\x00\x00\x10\x00\x0b"));
assert.eq(instance.exports.main() == 37, true);
}
{
/*
* Point(f64)
*
* (module
* (type $Point (struct (field $x (mut f64))))
* (func $doTest (param $p (ref $Point)) (result f64)
* (struct.set $Point $x
* (local.get $p)
* (f64.const 37)
* )
* (struct.get $Point $x
* (local.get $p)
* )
* )
*
* (func (export "main") (result f64)
* (call $doTest
* (struct.new $Point (f64.const 0))
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0f\x03\x5f\x01\x7c\x01\x60\x01\x64\x00\x01\x7c\x60\x00\x01\x7c\x03\x03\x02\x01\x02\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x01\x0a\x2a\x02\x17\x00\x20\x00\x44\x00\x00\x00\x00\x00\x80\x42\x40\xfb\x05\x00\x00\x20\x00\xfb\x02\x00\x00\x0b\x10\x00\x44\x00\x00\x00\x00\x00\x00\x00\x00\xfb\x00\x00\x10\x00\x0b"));
assert.eq(instance.exports.main(), 37);
}
{
/*
* Point(externref)
*
* (module
* (type $Point (struct (field $x (mut externref))))
* (func $doSet (param $p (ref $Point)) (param $obj externref) (result externref)
* (struct.set $Point $x
* (local.get $p)
* (local.get $obj)
* )
* (struct.get $Point $x
* (local.get $p)
* )
* )
*
* (func (export "main") (param $obj externref) (result externref)
* (call $doSet
* (struct.new $Point (ref.null extern))
* (local.get $obj)
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x11\x03\x5f\x01\x6f\x01\x60\x02\x64\x00\x6f\x01\x6f\x60\x01\x6f\x01\x6f\x03\x03\x02\x01\x02\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x01\x0a\x1e\x02\x10\x00\x20\x00\x20\x01\xfb\x05\x00\x00\x20\x00\xfb\x02\x00\x00\x0b\x0b\x00\xd0\x6f\xfb\x00\x00\x20\x00\x10\x00\x0b"));
let obj = {};
assert.eq(instance.exports.main(obj), obj);
}
{
/*
* (module
* (func (export "f") (param f64) (result f64) (local.get 0))
* )
*/
let instance1 = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x06\x01\x60\x01\x7c\x01\x7c\x03\x02\x01\x00\x07\x05\x01\x01\x66\x00\x00\x0a\x06\x01\x04\x00\x20\x00\x0b"));
/*
* Point(funcref)
*
* (module
* (type $Point (struct (field $x (mut funcref))))
* (func $doSet (param $p (ref $Point)) (param $obj funcref) (result funcref)
* (struct.set $Point $x
* (local.get $p)
* (local.get $obj)
* )
* (struct.get $Point $x
* (local.get $p)
* )
* )
*
* (func (export "main") (param $obj funcref) (result funcref)
* (call $doSet
* (struct.new $Point (ref.null func))
* (local.get $obj)
* )
* )
* )
*/
let instance2 = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x11\x03\x5f\x01\x70\x01\x60\x02\x64\x00\x70\x01\x70\x60\x01\x70\x01\x70\x03\x03\x02\x01\x02\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x01\x0a\x1e\x02\x10\x00\x20\x00\x20\x01\xfb\x05\x00\x00\x20\x00\xfb\x02\x00\x00\x0b\x0b\x00\xd0\x70\xfb\x00\x00\x20\x00\x10\x00\x0b"));
let foo = instance1.exports.f;
assert.eq(instance2.exports.main(foo), foo);
}
{
/*
* Point(i32, i32)
*
* (module
* (type $Point (struct (field $x (mut i32)) (field $y (mut i32))))
* (func $doSet (param $p (ref $Point)) (param $val i32) (result i32)
* (struct.set $Point $y
* (local.get $p)
* (local.get $val)
* )
* (struct.get $Point $y
* (local.get $p)
* )
* )
*
* (func (export "main") (result i32)
* (call $doSet
* (struct.new $Point (i32.const 0) (i32.const 0))
* (i32.const 19)
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x12\x03\x5f\x02\x7f\x01\x7f\x01\x60\x02\x64\x00\x7f\x01\x7f\x60\x00\x01\x7f\x03\x03\x02\x01\x02\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x01\x0a\x20\x02\x10\x00\x20\x00\x20\x01\xfb\x05\x00\x01\x20\x00\xfb\x02\x00\x01\x0b\x0d\x00\x41\x00\x41\x00\xfb\x00\x00\x41\x13\x10\x00\x0b"));
assert.eq(instance.exports.main(), 19);
}
{
/*
* Point(i32, i64)
*
* (module
* (type $Point (struct (field $x (mut i32)) (field $y (mut i64))))
* (func $doSet (param $p (ref $Point)) (param $val i32) (result i32)
* (struct.set $Point $x
* (local.get $p)
* (local.get $val)
* )
* (struct.get $Point $x
* (local.get $p)
* )
* )
*
* (func (export "main") (result i32)
* (call $doSet
* (struct.new $Point (i32.const 0) (i64.const 0))
* (i32.const 19)
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x12\x03\x5f\x02\x7f\x01\x7e\x01\x60\x02\x64\x00\x7f\x01\x7f\x60\x00\x01\x7f\x03\x03\x02\x01\x02\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x01\x0a\x20\x02\x10\x00\x20\x00\x20\x01\xfb\x05\x00\x00\x20\x00\xfb\x02\x00\x00\x0b\x0d\x00\x41\x00\x42\x00\xfb\x00\x00\x41\x13\x10\x00\x0b"));
assert.eq(instance.exports.main(), 19);
}
{
/*
* trying to set a non-mutable field
*
* (module
* (type $Point (struct (field $x (i32))))
* (func $doTest (param $p (ref $Point)) (result i32)
* (struct.set $Point $x
* (local.get $p)
* (i32.const 37)
* )
* (struct.get $Point $x
* (local.get $p)
* )
* )
*
* (func (export "main") (result i32)
* (call $doTest
* (struct.new $Point (i32.const 0))
* )
* )
* )
*/
assert.throws(
() =>
module(
"\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0f\x03\x5f\x01\x7f\x00\x60\x01\x64\x00\x01\x7f\x60\x00\x01\x7f\x03\x03\x02\x01\x02\x07\x08\x01\x04\x6d\x61\x69\x6e\x00\x01\x0a\x1c\x02\x10\x00\x20\x00\x41\x25\xfb\x05\x00\x00\x20\x00\xfb\x02\x00\x00\x0b\x09\x00\x41\x00\xfb\x00\x00\x10\x00\x0b"
),
WebAssembly.CompileError,
"WebAssembly.Module doesn't parse at byte 9: the field 0 can't be set because it is immutable, in function at index 0 (evaluating 'new WebAssembly.Module(buffer)')"
);
}
{
// unreachable context
/*
*
* (module
* (type $Point (struct (field $x (mut i32))))
* (func $doTest (param $p (ref $Point)) (result i32)
* (unreachable)
* (struct.set $Point $x
* (local.get $p)
* (i32.const 37)
* )
* (struct.get $Point $x
* (local.get $p)
* )
* )
* )
*/
let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0b\x02\x5f\x01\x7f\x01\x60\x01\x64\x00\x01\x7f\x03\x02\x01\x01\x0a\x13\x01\x11\x00\x00\x20\x00\x41\x25\xfb\x05\x00\x00\x20\x00\xfb\x02\x00\x00\x0b"));
/*
* invalid type index for struct.set in unreachable context
*
* (module
* (type $Point (struct (field $x (mut i32))))
* (func $doTest (param $p (ref $Point)) (result i32)
* (unreachable)
* (struct.set $Point $x
* (local.get $p)
* (i32.const 37)
* )
* (struct.get $Point $x
* (local.get $p)
* )
* )
* )
*/
assert.throws(
() =>
module(
"\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x0b\x02\x5f\x01\x7f\x01\x60\x01\x64\x00\x01\x7f\x03\x02\x01\x01\x0a\x13\x01\x11\x00\x00\x20\x00\x41\x25\xfb\x05\x00\x00\x20\x00\xfb\x02\x05\x00\x0b"
),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: struct.get index 5 is out of bound, in function at index 0 (evaluating 'new WebAssembly.Module(buffer)')"
);
}
// Test error message for invalid struct.set index.
assert.throws(
() =>
compile(`
(module
(func (result i32) (struct.set 5 0 (ref.null 0) (i32.const 42)))
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: struct.set index 5 is out of bound, in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
);
// Test null checks.
assert.throws(
() => instantiate(`
(module
(type (struct (field (mut i32))))
(func (export "f") (result)
(struct.set 0 0 (ref.null 0) (i32.const 42)))
)
`).exports.f(),
WebAssembly.RuntimeError,
"access to a null reference"
);
// Bottom is type valid but throws on null.
assert.throws(
() => instantiate(`
(module
(type (struct (field (mut i32))))
(func (export "f") (result)
(struct.set 0 0 (ref.null none) (i32.const 42)))
)
`).exports.f(),
WebAssembly.RuntimeError,
"access to a null reference"
);
}
function testStructTable() {
{
let m = instantiate(`
(module
(type (struct (field i32)))
(table 10 (ref null 0))
(func (export "set") (param i32) (result)
(table.set (local.get 0) (struct.new 0 (i32.const 42))))
(func (export "get") (param i32) (result i32)
(struct.get 0 0 (table.get (local.get 0)))
)
)
`);
m.exports.set(2);
assert.eq(m.exports.get(2), 42);
assert.throws(() => m.exports.get(4), WebAssembly.RuntimeError, "access to a null");
}
// Invalid struct.get, needs a downcast.
assert.throws(
() => compile(`
(module
(type (struct (field i32)))
(table 10 (ref null struct))
(func (param i32) (result i32)
(struct.get 0 0 (table.get (local.get 0))))
)
`),
WebAssembly.CompileError,
"struct.get structref to type (ref null struct) expected (ref null <struct:0>), in function at index 0"
);
// Invalid non-defaultable table type.
assert.throws(
() => compile(`
(module
(type (struct))
(table 0 (ref 0)))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't parse at byte 26: Table's type must be defaultable (evaluating 'new WebAssembly.Module(binary)')"
)
// Test JS API.
{
const m = instantiate(`
(module
(type (struct))
(table (export "t") 10 (ref null 0))
(start 0)
(func
(table.fill (i32.const 0) (struct.new 0) (i32.const 10)))
(func (export "makeStruct") (result (ref 0)) (struct.new 0)))
`);
const m2 = instantiate(`
(module
(type (array i32))
(func (export "makeArray") (result (ref 0)) (array.new_default 0 (i32.const 5))))
`);
assert.eq(m.exports.t.get(0) !== null, true);
assert.throws(
() => m.exports.t.set(0, "foo"),
TypeError,
"Argument value did not match the reference type"
);
assert.throws(
() => m.exports.t.set(0, 3),
TypeError,
"Argument value did not match the reference type"
);
assert.throws(
() => m.exports.t.set(0, m2.exports.makeArray()),
TypeError,
"Argument value did not match the reference type"
);
const str = m.exports.makeStruct();
m.exports.t.set(0, str);
assert.eq(m.exports.t.get(0), str);
m.exports.t.set(0, null);
assert.eq(m.exports.t.get(0), null);
}
}
function testStructPacked() {
compile(`
(module (type (struct (field i8))))
`);
compile(`
(module (type (struct (field i16))))
`);
{
const m = instantiate(`
(module (type (struct (field i8)))
(global (ref 0) (struct.new 0 (i32.const 257)))
(func (export "f1") (result i32)
(struct.get_s 0 0 (global.get 0)))
(func (export "f2") (result i32)
(struct.get_u 0 0 (global.get 0))))
`);
assert.eq(m.exports.f1(), 1);
assert.eq(m.exports.f2(), 1);
}
{
const m = instantiate(`
(module (type (struct (field i8)))
(global (ref 0) (struct.new 0 (i32.const 255)))
(func (export "f1") (result i32)
(struct.get_s 0 0 (global.get 0)))
(func (export "f2") (result i32)
(struct.get_u 0 0 (global.get 0))))
`);
assert.eq(m.exports.f1(), -1);
assert.eq(m.exports.f2(), 255);
}
assert.throws(
() => instantiate(`
(module (type (struct (field i8)))
(global (ref 0) (struct.new 0 (i32.const 257)))
(func (export "f") (result i32)
(struct.get 0 0 (global.get 0))))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't parse at byte 7: struct.get applied to packed array of I8 -- use struct.get_s or struct.get_u, in function at index 0"
);
{
const m = instantiate(`
(module (type (struct (field i16)))
(global (ref 0) (struct.new 0 (i32.const 65537)))
(func (export "f1") (result i32)
(struct.get_s 0 0 (global.get 0)))
(func (export "f2") (result i32)
(struct.get_u 0 0 (global.get 0))))
`);
assert.eq(m.exports.f1(), 1);
assert.eq(m.exports.f2(), 1);
}
{
const m = instantiate(`
(module (type (struct (field i16)))
(global (ref 0) (struct.new 0 (i32.const 65535)))
(func (export "f1") (result i32)
(struct.get_s 0 0 (global.get 0)))
(func (export "f2") (result i32)
(struct.get_u 0 0 (global.get 0))))
`);
assert.eq(m.exports.f1(), -1);
assert.eq(m.exports.f2(), 65535);
}
{
const m = instantiate(`
(module (type (struct (field i8 i16 i8 i16)))
(global (ref 0) (struct.new 0 (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4)))
(func (export "f0") (result i32)
(struct.get_s 0 0 (global.get 0)))
(func (export "f1") (result i32)
(struct.get_s 0 1 (global.get 0)))
(func (export "f2") (result i32)
(struct.get_u 0 2 (global.get 0)))
(func (export "f3") (result i32)
(struct.get_u 0 3 (global.get 0))))
`);
assert.eq(m.exports.f0(), 1);
assert.eq(m.exports.f1(), 2);
assert.eq(m.exports.f2(), 3);
assert.eq(m.exports.f3(), 4);
}
{
const m = instantiate(`
(module (type (struct (field (mut i8))))
(global (ref 0) (struct.new 0 (i32.const 42)))
(func (export "f") (result i32)
(struct.set 0 0 (global.get 0) (i32.const 84))
(struct.get_u 0 0 (global.get 0))))
`);
assert.eq(m.exports.f(), 84);
}
{
const m = instantiate(`
(module (type (struct (field (mut i16))))
(global (ref 0) (struct.new 0 (i32.const 42)))
(func (export "f") (result i32)
(struct.set 0 0 (global.get 0) (i32.const 84))
(struct.get_u 0 0 (global.get 0))))
`);
assert.eq(m.exports.f(), 84);
}
{
const m = instantiate(`
(module (type (struct (field (mut i8) (mut i16) (mut i8) (mut i16))))
(global (ref 0) (struct.new_default 0))
(func (export "init")
(struct.set 0 0 (global.get 0) (i32.const 1))
(struct.set 0 1 (global.get 0) (i32.const 2))
(struct.set 0 2 (global.get 0) (i32.const 3))
(struct.set 0 3 (global.get 0) (i32.const 4)))
(func (export "f0") (result i32)
(struct.get_u 0 0 (global.get 0)))
(func (export "f1") (result i32)
(struct.get_u 0 1 (global.get 0)))
(func (export "f2") (result i32)
(struct.get_u 0 2 (global.get 0)))
(func (export "f3") (result i32)
(struct.get_u 0 3 (global.get 0))))
`);
assert.eq(m.exports.f0(), 0);
assert.eq(m.exports.f1(), 0);
assert.eq(m.exports.f2(), 0);
assert.eq(m.exports.f3(), 0);
m.exports.init();
assert.eq(m.exports.f0(), 1);
assert.eq(m.exports.f1(), 2);
assert.eq(m.exports.f2(), 3);
assert.eq(m.exports.f3(), 4);
}
}
testStructDeclaration();
testStructJS();
testStructNew();
testStructNewDefault();
testStructGet();
testStructSet();
testStructTable();
testStructPacked();