diff options
Diffstat (limited to 'js/src/jit-test/tests/wasm/ref-types/ref-func.js')
-rw-r--r-- | js/src/jit-test/tests/wasm/ref-types/ref-func.js | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/ref-types/ref-func.js b/js/src/jit-test/tests/wasm/ref-types/ref-func.js new file mode 100644 index 0000000000..de874669b3 --- /dev/null +++ b/js/src/jit-test/tests/wasm/ref-types/ref-func.js @@ -0,0 +1,290 @@ +load(libdir + "wasm-binary.js"); + +const v2vSig = {args:[], ret:VoidCode}; +const v2vSigSection = sigSection([v2vSig]); + +// 'ref.func' parses, validates and returns a non-null value +wasmFullPass(` + (module + (elem declare $run) + (func $run (result i32) + ref.func $run + ref.is_null + ) + (export "run" (func $run)) + ) +`, 0); + +// function returning reference to itself +{ + let {f1} = wasmEvalText(` + (module + (elem declare $f1) + (func $f1 (result funcref) ref.func $f1) + (export "f1" (func $f1)) + ) + `).exports; + assertEq(f1(), f1); +} + +// function returning reference to a different function +{ + let {f1, f2} = wasmEvalText(` + (module + (elem declare $f1) + (func $f1) + (func $f2 (result funcref) ref.func $f1) + (export "f1" (func $f1)) + (export "f2" (func $f2)) + ) + `).exports; + assertEq(f2(), f1); +} + +// function returning reference to function in a different module +{ + let i1 = wasmEvalText(` + (module + (elem declare $f1) + (func $f1) + (export "f1" (func $f1)) + ) + `); + let i2 = wasmEvalText(` + (module + (import "" "f1" (func $f1)) + (elem declare $f1) + (func $f2 (result funcref) ref.func $f1) + (export "f1" (func $f1)) + (export "f2" (func $f2)) + ) + `, {"": i1.exports}); + + let f1 = i1.exports.f1; + let f2 = i2.exports.f2; + assertEq(f2(), f1); +} + +// function index must be valid +assertErrorMessage(() => { + wasmEvalText(` + (module + (func (result funcref) ref.func 10) + ) + `); +}, WebAssembly.CompileError, /(function index out of range)|(function index out of bounds)/); + +function validFuncRefText(forwardDeclare, tbl_type) { + return wasmEvalText(` + (module + (table 1 ${tbl_type}) + (func $test (result funcref) ref.func $referenced) + (func $referenced) + ${forwardDeclare} + ) + `); +} + +// referenced function must be forward declared somehow +assertErrorMessage(() => validFuncRefText('', 'funcref'), WebAssembly.CompileError, /(function index is not declared in a section before the code section)|(undeclared function reference)/); + +// referenced function can be forward declared via segments +assertEq(validFuncRefText('(elem 0 (i32.const 0) func $referenced)', 'funcref') instanceof WebAssembly.Instance, true); +assertEq(validFuncRefText('(elem func $referenced)', 'funcref') instanceof WebAssembly.Instance, true); +assertEq(validFuncRefText('(elem declare $referenced)', 'funcref') instanceof WebAssembly.Instance, true); + +// also when the segment is passive or active 'funcref' +assertEq(validFuncRefText('(elem 0 (i32.const 0) funcref (ref.func $referenced))', 'funcref') instanceof WebAssembly.Instance, true); +assertEq(validFuncRefText('(elem funcref (ref.func $referenced))', 'funcref') instanceof WebAssembly.Instance, true); + +// reference function can be forward declared via globals +assertEq(validFuncRefText('(global funcref (ref.func $referenced))', 'externref') instanceof WebAssembly.Instance, true); + +// reference function can be forward declared via export +assertEq(validFuncRefText('(export "referenced" (func $referenced))', 'externref') instanceof WebAssembly.Instance, true); + +// reference function cannot be forward declared via start +assertErrorMessage(() => validFuncRefText('(start $referenced)', 'externref'), WebAssembly.CompileError, /(function index is not declared in a section before the code section)|(undeclared function reference)/); + +// Tests not expressible in the text format. + +// element segment with elemexpr can carry non-reference type, but this must be +// rejected. + +assertErrorMessage(() => new WebAssembly.Module( + moduleWithSections([generalElemSection([{ flag: PassiveElemExpr, + typeCode: I32Code, + elems: [] }])])), + WebAssembly.CompileError, + /bad type/); + +// Test case for bug 1596026: when taking the ref.func of an imported function, +// the value obtained should not be the JS function. This would assert (even in +// a release build), so the test is merely that the code runs. + +var ins = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(` + (module + (import "m" "f" (func $f (param i32) (result i32))) + (elem declare $f) + (table 1 funcref) + (func (export "f") + (table.set 0 (i32.const 0) (ref.func $f))))`)), + {m:{f:(x) => 37+x}}); +ins.exports.f(); + +// Verification that we can handle encoding errors for passive element segments +// properly. + +function checkPassiveElemSegment(mangle, err) { + let bin = moduleWithSections( + [v2vSigSection, declSection([0]), // One function + tableSection(1), // One table + { name: elemId, // One passive segment + body: (function () { + let body = []; + body.push(1); // 1 element segment + body.push(0x1 | 0x4); // Flags: Passive and uses element expression + body.push(AnyFuncCode + (mangle == "type" ? 1 : 0)); // always anyfunc + body.push(1); // Element count + body.push(RefFuncCode + (mangle == "ref.func" ? 1 : 0)); // always ref.func + body.push(0); // func index + body.push(EndCode + (mangle == "end" ? 1 : 0)); + return body; + })() }, + bodySection( // Empty function + [funcBody( + {locals:[], + body:[]})]) + ]); + if (err) { + assertErrorMessage(() => new WebAssembly.Module(bin), + WebAssembly.CompileError, + err); + } else { + new WebAssembly.Module(bin); + } +} + +checkPassiveElemSegment(""); +checkPassiveElemSegment("type", /bad type/); +checkPassiveElemSegment("ref.func", /failed to read initializer operation/); +checkPassiveElemSegment("end", /failed to read end of initializer expression/); + +// Passive element segments can contain literal null values. + +{ + let txt = + `(module + (table (export "t") 10 funcref) + (elem (i32.const 1) $m) + (elem (i32.const 3) $m) + (elem (i32.const 6) $m) + (elem (i32.const 8) $m) + (elem funcref (ref.func $f) (ref.null func) (ref.func $g) (ref.null func) (ref.func $h)) + (func $m) + (func $f) + (func $g) + (func $h) + (func (export "doit") (param $idx i32) + (table.init 4 (local.get $idx) (i32.const 0) (i32.const 5))))`; + let ins = wasmEvalText(txt); + ins.exports.doit(0); + ins.exports.doit(5); + assertEq(typeof ins.exports.t.get(0), "function"); + assertEq(ins.exports.t.get(1), null); + assertEq(typeof ins.exports.t.get(2), "function"); + assertEq(ins.exports.t.get(0) == ins.exports.t.get(2), false); + assertEq(ins.exports.t.get(3), null); + assertEq(typeof ins.exports.t.get(4), "function"); + assertEq(typeof ins.exports.t.get(5), "function"); + assertEq(ins.exports.t.get(6), null); + assertEq(typeof ins.exports.t.get(7), "function"); + assertEq(ins.exports.t.get(8), null); + assertEq(typeof ins.exports.t.get(9), "function"); +} + +// Test ref.func in global initializer expressions + +for (let mutable of [true, false]) { + for (let imported of [true, false]) { + for (let exported of [true, false]) { + let globalType = mutable ? `(mut funcref)` : `funcref`; + + let imports = {}; + + if (imported) { + imports = wasmEvalText(` + (module + (global $g (export "g") ${globalType} (ref.func $f)) + (func $f (export "f") (result i32) i32.const 42) + ) + `).exports; + } + + let exports = wasmEvalText(` + (module + (global $g ${exported ? `(export "g")` : ``} ${imported ? `(import "" "g")` : ``} ${globalType} ${imported ? `` : `(ref.func $f)`}) + ${exported ? `` : `(func (export "get_g") (result funcref) global.get $g)`} + (func $f (export "f") (result i32) i32.const 42) + ) + `, { "": imports }).exports; + + let targetFunc = imported ? imports.f : exports.f; + let globalVal = exported ? exports.g.value : exports.get_g(); + assertEq(targetFunc(), 42); + assertEq(globalVal(), 42); + assertEq(targetFunc, globalVal); + if (imported && exported) { + assertEq(imports.g, exports.g); + } + } + } +} + +// Test invalid ref.func indices + +function testCodeRefFuncIndex(index) { + assertErrorMessage(() => { + new WebAssembly.Module(moduleWithSections( + [v2vSigSection, + declSection([0]), // One function + bodySection( + [funcBody( + {locals:[], + body:[ + RefFuncCode, + ...varU32(index), + DropCode + ]})]) + ])) + }, + WebAssembly.CompileError, + /(function index out of range)|(function index out of bounds)/); +} + +testCodeRefFuncIndex(1); +testCodeRefFuncIndex(2); +testCodeRefFuncIndex(10); +testCodeRefFuncIndex(1000); + +function testGlobalRefFuncIndex(index) { + assertErrorMessage(() => { + new WebAssembly.Module(moduleWithSections( + [v2vSigSection, + globalSection([ + { + valType: AnyFuncCode, + flags: 0, + initExpr: [RefFuncCode, ...varU32(index), EndCode], + } + ]) + ])) + }, + WebAssembly.CompileError, + /(function index out of range)|(function index out of bounds)/); +} + +testGlobalRefFuncIndex(1); +testGlobalRefFuncIndex(2); +testGlobalRefFuncIndex(10); +testGlobalRefFuncIndex(1000); |