Source code

Revision control

Copy as Markdown

Other Tools

///////////////////////////////////////////////////////////////////////////
//
// General table management in wasm
// Wasm: Create table-of-externref
new WebAssembly.Module(wasmTextToBinary(
`(module
(table 10 externref))`));
// Wasm: Import table-of-externref
// JS: create table-of-externref
new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
`(module
(table (import "m" "t") 10 externref))`)),
{m:{t: new WebAssembly.Table({element:"externref", initial:10})}});
new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
`(module
(import "m" "t" (table 10 externref)))`)),
{m:{t: new WebAssembly.Table({element:"externref", initial:10})}});
// Wasm: Export table-of-externref, initial values shall be null
{
let ins = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
`(module
(table (export "t") 10 externref))`)));
let t = ins.exports.t;
assertEq(t.length, 10);
for (let i=0; i < t.length; i++)
assertEq(t.get(0), null);
}
// JS: Exported table can be grown, and values are preserved
{
let ins = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
`(module
(table (export "t") 10 externref))`)));
let t = ins.exports.t;
let objs = [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}];
for (let i in objs)
t.set(i, objs[i]);
ins.exports.t.grow(10);
assertEq(ins.exports.t.length, 20);
for (let i in objs)
assertEq(t.get(i), objs[i]);
}
// Wasm: table.copy between tables of externref (currently source and destination
// are both table zero)
{
let ins = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
`(module
(table (export "t") 10 externref)
(func (export "f")
(table.copy (i32.const 5) (i32.const 0) (i32.const 3))))`)));
let t = ins.exports.t;
let objs = [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}];
for (let i in objs)
t.set(i, objs[i]);
ins.exports.f();
assertEq(t.get(0), objs[0]);
assertEq(t.get(1), objs[1]);
assertEq(t.get(2), objs[2]);
assertEq(t.get(3), objs[3]);
assertEq(t.get(4), objs[4]);
assertEq(t.get(5), objs[0]);
assertEq(t.get(6), objs[1]);
assertEq(t.get(7), objs[2]);
assertEq(t.get(8), objs[8]);
assertEq(t.get(9), objs[9]);
}
// Wasm: table.copy from table(funcref) to table(externref) should not work
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table (export "t") 10 externref)
(func $f1)
(func $f2)
(func $f3)
(func $f4)
(func $f5)
(table 5 funcref)
(elem (table 1) (i32.const 0) func $f1 $f2 $f3 $f4 $f5)
(func (export "f")
(table.copy 0 1 (i32.const 5) (i32.const 0) (i32.const 5))))`)),
WebAssembly.CompileError,
/(expression has type funcref but expected externref)|(type mismatch)/);
// Wasm: table.copy from table(externref) to table(funcref) should not work
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table 10 funcref)
(table 10 externref)
(func (export "f")
(table.copy 0 1 (i32.const 0) (i32.const 0) (i32.const 5))))`)),
WebAssembly.CompileError,
/(expression has type externref but expected funcref)|(type mismatch)/);
// Wasm: Element segments of funcref can't target tables of externref
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(func $f1 (result i32) (i32.const 0))
(table (export "t") 10 externref)
(elem 0 (i32.const 0) funcref (ref.func $f1)))`)),
WebAssembly.CompileError,
/type mismatch/);
// Wasm: Element segments of externref can't target tables of funcref
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table (export "t") 10 funcref)
(elem 0 (i32.const 0) externref (ref.null extern)))`)),
WebAssembly.CompileError,
/type mismatch/);
// Wasm: table.init on table-of-externref is not allowed when the segment has
// funcref.
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(func $f1 (result i32) (i32.const 0))
(table 10 externref)
(elem funcref (ref.func $f1))
(func
(table.init 0 (i32.const 0) (i32.const 0) (i32.const 0))))`)),
WebAssembly.CompileError,
/(expression has type funcref but expected externref)|(type mismatch)/);
// Wasm: table.init on table-of-funcref is not allowed when the segment has
// externref.
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table 10 funcref)
(elem externref (ref.null extern))
(func
(table.init 0 (i32.const 0) (i32.const 0) (i32.const 0))))`)),
WebAssembly.CompileError,
/(expression has type externref but expected funcref)|(type mismatch)/);
// Wasm: table types must match at link time
assertErrorMessage(
() => new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
`(module
(import "m" "t" (table 10 externref)))`)),
{m:{t: new WebAssembly.Table({element:"anyfunc", initial:10})}}),
WebAssembly.LinkError,
/imported table type mismatch/);
// call_indirect cannot reference table-of-externref
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table 10 externref)
(type $t (func (param i32) (result i32)))
(func (result i32)
(call_indirect (type $t) (i32.const 37))))`)),
WebAssembly.CompileError,
/(indirect calls must go through a table of 'funcref')|(indirect calls must go through a table of funcref)/);
///////////////////////////////////////////////////////////////////////////
//
// additional js api tests
{
let tbl = new WebAssembly.Table({element:"externref", initial:10});
// Initial value is undefined. This is different from when tables are
// created inside wasm.
assertEq(tbl.get(0), undefined);
// Identity preserving.
let x = {hi: 48};
tbl.set(0, x);
assertEq(tbl.get(0), x);
tbl.set(2, dummy);
assertEq(tbl.get(2), dummy);
tbl.set(2, null);
assertEq(tbl.get(2), null);
// Temporary semantics is to convert to object and leave as object; once we
// have a better wrapped externref this will change, we won't be able to
// observe the boxing.
tbl.set(1, 42);
let y = tbl.get(1);
assertEq(typeof y, "number");
assertEq(y, 42);
}
function dummy() { return 37 }
///////////////////////////////////////////////////////////////////////////
//
// table.get and table.set
const wasmFun = wasmEvalText(`(module (func (export "x")))`).exports.x;
// table.get in bounds - returns right value type & value
// table.get out of bounds - fails
function testTableGet(type, x) {
let ins = wasmEvalText(
`(module
(table (export "t") 10 ${type})
(func (export "f") (param i32) (result ${type})
(table.get (local.get 0))))`);
ins.exports.t.set(0, x);
assertEq(ins.exports.f(0), x);
assertEq(ins.exports.f(1), null);
assertErrorMessage(() => ins.exports.f(10), WebAssembly.RuntimeError, /index out of bounds/);
assertErrorMessage(() => ins.exports.f(-5), WebAssembly.RuntimeError, /index out of bounds/);
}
testTableGet('externref', {});
testTableGet('funcref', wasmFun);
// table.get with non-i32 index - fails validation
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table 10 externref)
(func (export "f") (param f64) (result externref)
(table.get (local.get 0))))`)),
WebAssembly.CompileError,
/type mismatch/);
// table.get when there are no tables - fails validation
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(func (export "f") (param i32)
(drop (table.get (local.get 0)))))`)),
WebAssembly.CompileError,
/(table index out of range for table.get)|(table index out of bounds)/);
// table.set in bounds with i32 x externref - works, no value generated
// table.set with null - works
// table.set out of bounds - fails
function testTableSet(lhs_type, rhs_type, rhs_reftype, x) {
let ins = wasmEvalText(
`(module
(table (export "t") 10 ${lhs_type})
(func (export "set_ref") (param i32) (param ${rhs_type})
(table.set (local.get 0) (local.get 1)))
(func (export "set_null") (param i32)
(table.set (local.get 0) (ref.null ${rhs_reftype}))))`);
ins.exports.set_ref(3, x);
assertEq(ins.exports.t.get(3), x);
ins.exports.set_null(3);
assertEq(ins.exports.t.get(3), null);
assertErrorMessage(() => ins.exports.set_ref(10, x), WebAssembly.RuntimeError, /index out of bounds/);
assertErrorMessage(() => ins.exports.set_ref(-1, x), WebAssembly.RuntimeError, /index out of bounds/);
}
testTableSet('externref', 'externref', 'extern', {});
testTableSet('funcref', 'funcref', 'func', wasmFun);
// Wasm: table.set on table(funcref) with externref value should fail
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table (export "t") 10 funcref)
(func (export "set_ref") (param i32) (param externref)
(table.set (local.get 0) (local.get 1))))`)),
WebAssembly.CompileError,
/(type mismatch: expression has type externref but expected funcref)|(type mismatch: expected funcref, found externref)/);
// table.set with non-i32 index - fails validation
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table 10 externref)
(func (export "f") (param f64)
(table.set (local.get 0) (ref.null extern))))`)),
WebAssembly.CompileError,
/type mismatch/);
// table.set with non-ref value - fails validation
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table 10 externref)
(func (export "f") (param f64)
(table.set (i32.const 0) (local.get 0))))`)),
WebAssembly.CompileError,
/type mismatch/);
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table 10 funcref)
(func (export "f") (param f64)
(table.set (i32.const 0) (local.get 0))))`)),
WebAssembly.CompileError,
/type mismatch/);
// table.set when there are no tables - fails validation
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(func (export "f") (param externref)
(table.set (i32.const 0) (local.get 0))))`)),
WebAssembly.CompileError,
/(table index out of range for table.set)|(table index out of bounds)/);
function testTableGrow(lhs_type, lhs_reftype, rhs_type, x) {
let ins = wasmEvalText(
`(module
(table (export "t") 10 20 ${lhs_type})
(func (export "grow") (param i32) (result i32)
(table.grow (ref.null ${lhs_reftype}) (local.get 0)))
(func (export "grow2") (param i32) (param ${rhs_type}) (result i32)
(table.grow (local.get 1) (local.get 0))))`);
// we can grow table of references
// table.grow with zero delta - always works even at maximum
// table.grow with delta - works and returns correct old value
// table.grow with delta at upper limit - fails
// table.grow with negative delta - fails
assertEq(ins.exports.grow(0), 10);
assertEq(ins.exports.t.length, 10);
assertEq(ins.exports.grow(1), 10);
assertEq(ins.exports.t.length, 11);
assertEq(ins.exports.t.get(10), null);
assertEq(ins.exports.grow2(9, x), 11);
assertEq(ins.exports.t.length, 20);
for (var i = 11; i < 20; i++)
assertEq(ins.exports.t.get(i), x);
assertEq(ins.exports.grow(0), 20);
// The JS API throws if it can't grow
assertErrorMessage(() => ins.exports.t.grow(1), RangeError, /failed to grow table/);
assertErrorMessage(() => ins.exports.t.grow(-1), TypeError, /bad [Tt]able grow delta/);
// The wasm API does not throw if it can't grow, but returns -1
assertEq(ins.exports.grow(1), -1);
assertEq(ins.exports.t.length, 20);
assertEq(ins.exports.grow(-1), -1);
assertEq(ins.exports.t.length, 20)
}
testTableGrow('externref', 'extern', 'externref', 42);
testTableGrow('funcref', 'func', 'funcref', wasmFun);
// Wasm: table.grow on table(funcref) with externref initializer should fail
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table (export "t") 10 20 funcref)
(func (export "grow2") (param i32) (param externref) (result i32)
(table.grow (local.get 1) (local.get 0))))`)),
WebAssembly.CompileError,
/(type mismatch: expression has type externref but expected funcref)|(type mismatch: expected funcref, found externref)/);
// Special case for private tables without a maximum
{
let ins = wasmEvalText(
`(module
(table 10 externref)
(func (export "grow") (param i32) (result i32)
(table.grow (ref.null extern) (local.get 0))))`);
assertEq(ins.exports.grow(0), 10);
assertEq(ins.exports.grow(1), 10);
assertEq(ins.exports.grow(9), 11);
assertEq(ins.exports.grow(0), 20);
}
// table.grow with non-i32 argument - fails validation
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(table 10 externref)
(func (export "f") (param f64)
(table.grow (ref.null extern) (local.get 0))))`)),
WebAssembly.CompileError,
/type mismatch/);
// table.grow when there are no tables - fails validation
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
`(module
(func (export "f") (param i32)
(table.grow (ref.null extern) (local.get 0))))`)),
WebAssembly.CompileError,
/(table index out of range for table.grow)|(table index out of bounds)/);
// table.size on table of externref
for (let visibility of ['', '(export "t")', '(import "m" "t")']) {
let exp = {m:{t: new WebAssembly.Table({element:"externref",
initial: 10,
maximum: 20})}};
let ins = wasmEvalText(
`(module
(table ${visibility} 10 20 externref)
(func (export "grow") (param i32) (result i32)
(table.grow (ref.null extern) (local.get 0)))
(func (export "size") (result i32)
(table.size)))`,
exp);
assertEq(ins.exports.grow(0), 10);
assertEq(ins.exports.size(), 10);
assertEq(ins.exports.grow(1), 10);
assertEq(ins.exports.size(), 11);
assertEq(ins.exports.grow(9), 11);
assertEq(ins.exports.size(), 20);
assertEq(ins.exports.grow(0), 20);
assertEq(ins.exports.size(), 20);
}
// table.size on table of funcref
{
let ins = wasmEvalText(
`(module
(table (export "t") 2 funcref)
(func (export "f") (result i32)
(table.size)))`);
assertEq(ins.exports.f(), 2);
ins.exports.t.grow(1);
assertEq(ins.exports.f(), 3);
}