From 1fa0c8d00471f8d6fe77fc0be349f3a69055954a Mon Sep 17 00:00:00 2001 From: Ilya Rezvov Date: Tue, 3 Oct 2023 10:53:40 -0600 Subject: [PATCH 1/2] Port V8 tests for more coverage --- test/js-api/functions/assertions.js | 13 ++ test/js-api/functions/constructor.any.js | 148 ++++++++++++++++++ test/js-api/functions/module.any.js | 189 +++++++++++++++++++++++ 3 files changed, 350 insertions(+) create mode 100644 test/js-api/functions/assertions.js create mode 100644 test/js-api/functions/constructor.any.js create mode 100644 test/js-api/functions/module.any.js diff --git a/test/js-api/functions/assertions.js b/test/js-api/functions/assertions.js new file mode 100644 index 00000000..ba145b45 --- /dev/null +++ b/test/js-api/functions/assertions.js @@ -0,0 +1,13 @@ +function assert_Function(func) { + assert_equals(Object.getPrototypeOf(func), WebAssembly.Function.prototype, + "prototype"); + assert_true(Object.isExtensible(func), "extensible"); + assert_true(fun instanceof WebAssembly.Function); + assert_true(fun instanceof Function); + assert_true(fun instanceof Object); + assert_equals(fun.__proto__, WebAssembly.Function.prototype); + assert_equals(fun.__proto__.__proto__, Function.prototype); + assert_equals(fun.__proto__.__proto__.__proto__, Object.prototype); + assert_equals(fun.constructor, WebAssembly.Function); + assert_equals(typeof fun, 'function'); +} diff --git a/test/js-api/functions/constructor.any.js b/test/js-api/functions/constructor.any.js new file mode 100644 index 00000000..56d23f88 --- /dev/null +++ b/test/js-api/functions/constructor.any.js @@ -0,0 +1,148 @@ + +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/function/assertions.js + +test(() => { + let desc = Object.getOwnPropertyDescriptor(WebAssembly, 'Function'); + assert_equals(typeof desc.value, 'function'); + assert_true(desc.writable); + assert_true(desc.enumerable); + assert_true(desc.configurable); +}, "constructor"); + +test(() => { + assert_function_name(WebAssembly.Function, "Function", "WebAssembly.Function"); +}, "name"); + +test(() => { + assert_function_length(WebAssembly.Function, 2, "WebAssembly.Function"); +}, "length"); + +test(() => { + const longArr = new Array(1000 + 1); + const invalidArguments = [ + undefined, + null, + false, + true, + "", + "test", + Symbol(), + 1, + NaN, + {}, + {parameters:[]}, + {parameters:['foo'], results:[]}, + {parameters:[], results:['foo']}, + {parameters:longArr, results:[]}, + {parameters:[], results:longArr}, + ]; + for (const invalidArgument of invalidArguments) { + assert_throws_js(TypeError, + () => new WebAssembly.WebAssembly(invalidArgument, () => 0), + `new WebAssembly(${format_value(invalidArgument)}, () => 0)`); + } + const validDescriptor = {parameters:[], results:[]}; + // invalid function argument + assert_throws_js(TypeError, + () => new WebAssembly.WebAssembly(validDescriptor), + `new WebAssembly(${format_value(validDescriptor)})`); + assert_throws_js(TypeError, + () => new WebAssembly.WebAssembly(validDescriptor, {}), + `new WebAssembly(${format_value(validDescriptor)}, {})`); +}, "Invalid descriptor argument"); + +test(() => { + const builder = new WasmModuleBuilder(); + + builder + .addFunction("func1", kSig_v_i) + .addBody([]) + .exportFunc(); + builder + .addFunction("func2", kSig_v_v) + .addBody([]) + .exportFunc(); + + const instance = builder.instantiate({}); + + assert_throws_js(TypeError, + () => new WebAssembly.WebAssembly( + {parameters: [], results: []}, + instance.exports.func1 + ), + `new WebAssembly({parameters: [], results: []},instance.exports.func1)`); + assert_Function(new WebAssembly.WebAssembly( + {parameters: [], results: []}, + instance.exports.func2 + )); +}, "Re-wrap Wasm Exported function"); + +test(() => { + const func = new WebAssembly.Function({parameters: [], results: []}, _ => 0); + assert_throws_js(TypeError, + () => new WebAssembly.WebAssembly( + {parameters: ['i32'], results: []}, + func + ), + `new WebAssembly({parameters: ['i32'], results: []},func)`); + assert_Function(new WebAssembly.Function({parameters: [], results: []}, func)); +}, "Re-wrap Wasm function with other signature"); + +test(() => { + let log = []; // Populated with a log of accesses. + let two = { toString: () => "2" }; // Just a fancy "2". + let logger = new Proxy({ length: two, "0": "i32", "1": "f32"}, { + get: function(obj, prop) { log.push(prop); return Reflect.get(obj, prop); }, + set: function(obj, prop, val) { assertUnreachable(); } + }); + let fun = new WebAssembly.Function({parameters:logger, results:[]}, _ => 0); + assert_array_equals(["i32", "f32"], WebAssembly.Function.type(fun).parameters); + assert_array_equals(["length", "0", "1"], log); +}, "Access signature"); + +test(() => { + let throw1 = { get length() { throw new Error("cannot see length"); }}; + let throw2 = { length: { toString: _ => { throw new Error("no length") } } }; + let throw3 = { length: "not a length value, this also throws" }; + assert_throws_js(Error, + () => new WebAssembly.Function({parameters:throw1, results:[]}), + "new WebAssembly.Function({parameters:throw1, results:[]})"); + assert_throws_js(Error, + () => new WebAssembly.Function({parameters:throw2, results:[]}), + "new WebAssembly.Function({parameters:throw2, results:[]})"); + assert_throws_js(TypeError, + () => new WebAssembly.Function({parameters:throw3, results:[]}), + "new WebAssembly.Function({parameters:throw3, results:[]})"); + assert_throws_js(Error, + () => new WebAssembly.Function({parameters:[], results:throw1}), + "new WebAssembly.Function({parameters:[], results:throw1})"); + assert_throws_js(Error, + () => new WebAssembly.Function({parameters:[], results:throw2}), + "new WebAssembly.Function({parameters:[], results:throw2})"); + assert_throws_js(TypeError, + () => new WebAssembly.Function({parameters:[], results:throw3}), + "new WebAssembly.Function({parameters:[], results:throw3})"); +}, "Throwing signature access"); + +test(() => { + const fn = new WebAssembly.Function({parameters:[], results:[]}, _ => 0) + assert_Function(fn); + assert_equals(fn(), 0); +}, "Success construction"); + +test(() => { + const testcases = [ + {parameters:[], results:[]}, + {parameters:["i32"], results:[]}, + {parameters:["i64"], results:["i32"]}, + {parameters:["f64", "f64", "i32"], results:[]}, + {parameters:["f32"], results:["f32"]}, + ]; + testcases.forEach(function(expected) { + let fun = new WebAssembly.Function(expected, _ => 0); + let type = WebAssembly.Function.type(fun); + assert_equals(expected, type) + }); +}, "Function type"); diff --git a/test/js-api/functions/module.any.js b/test/js-api/functions/module.any.js new file mode 100644 index 00000000..f9a781db --- /dev/null +++ b/test/js-api/functions/module.any.js @@ -0,0 +1,189 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/function/assertions.js + +test(() => { + const builder = new WasmModuleBuilder(); + + builder + .addFunction("fun", kSig_v_v) + .addBody([]) + .exportFunc(); + + const instance = builder.instantiate({}); + const fun = instance.exports.fun; + + assert_Function(fun); + assert_equals(fun(), undefined); +}, "Call exported function"); + +test(() => { + let testcases = [ + [kSig_v_v, {parameters:[], results:[]}], + [kSig_v_i, {parameters:["i32"], results:[]}], + [kSig_i_l, {parameters:["i64"], results:["i32"]}], + [kSig_v_ddi, {parameters:["f64", "f64", "i32"], results:[]}], + [kSig_f_f, {parameters:["f32"], results:["f32"]}], + ]; + testcases.forEach(function([sig, expected]) { + let builder = new WasmModuleBuilder(); + builder.addFunction("fun", sig).addBody([kExprUnreachable]).exportFunc(); + let instance = builder.instantiate({}); + assert_Function(instance.exports.fun); + let type = WebAssembly.Function.type(instance.exports.fun); + assert_equals(expected, type); + let module = new WebAssembly.Module(builder.toBuffer()); + let exports = WebAssembly.Module.exports(module); + assert_equals("fun", exports[0].name); + assert_true("type" in exports[0]); + assert_equals(expected, exports[0].type); + }); +}, "Exported function type"); + +test(() => { + let testcases = [ + [kSig_v_v, {parameters:[], results:[]}], + [kSig_v_i, {parameters:["i32"], results:[]}], + [kSig_i_l, {parameters:["i64"], results:["i32"]}], + [kSig_v_ddi, {parameters:["f64", "f64", "i32"], results:[]}], + [kSig_f_f, {parameters:["f32"], results:["f32"]}], + ]; + testcases.forEach(function([sig, expected]) { + let builder = new WasmModuleBuilder(); + builder.addImport("m", "fun", sig); + let module = new WebAssembly.Module(builder.toBuffer()); + let imports = WebAssembly.Module.imports(module); + assert_equals("fun", imports[0].name); + assert_equals("m", imports[0].module); + assert_true("type" in imports[0]); + assert_equals(expected, imports[0].type); + }); +}, "Import function type"); + +test(() => { + let obj1 = { valueOf: _ => 123.45 }; + let obj2 = { toString: _ => "456" }; + let gcer = { valueOf: _ => gc() }; + let testcases = [ + { params: { sig: ["i32"], + val: [23.5], + exp: [23], }, + result: { sig: ["i32"], + val: 42.7, + exp: 42, }, + }, + { params: { sig: ["i32", "f32", "f64"], + val: [obj1, obj2, "789"], + exp: [123, 456, 789], }, + result: { sig: [], + val: undefined, + exp: undefined, }, + }, + { params: { sig: ["i32", "f32", "f64"], + val: [gcer, {}, "xyz"], + exp: [0, NaN, NaN], }, + result: { sig: ["f64"], + val: gcer, + exp: NaN, }, + }, + ]; + testcases.forEach(function({params, result}) { + let p = params.sig; let r = result.sig; var params_after; + function testFun() { params_after = arguments; return result.val; } + let fun = new WebAssembly.Function({parameters:p, results:r}, testFun); + let result_after = fun.apply(undefined, params.val); + assert_array_equals(params.exp, params_after); + assert_equals(result.exp, result_after); + }); +}, "Function constructed coercions"); + +test(() => { + let builder = new WasmModuleBuilder(); + let fun = new WebAssembly.Function({parameters:[], results:["i64"]}, _ => 0n); + let table = new WebAssembly.Table({element: "anyfunc", initial: 2}); + let table_index = builder.addImportedTable("m", "table", 2); + let sig_index = builder.addType(kSig_l_v); + table.set(0, fun); + builder.addFunction('main', kSig_v_i) + .addBody([ + kExprLocalGet, 0, + kExprCallIndirect, sig_index, table_index, + kExprDrop + ]) + .exportFunc(); + let instance = builder.instantiate({ m: { table: table }}); + assert_equals(instance.exports.main(0), 0); + assert_throws_js(RangeError, () => instance.exports.main(1), "instance.exports.main(1)"); + table.set(1, fun); + assert_equals(instance.exports.main(1), 0); +}, "Function Table set"); + +test(() => { + let builder = new WasmModuleBuilder(); + let fun = new WebAssembly.Function({parameters:[], results:["i32"]}, _ => 7); + let fun_index = builder.addImport("m", "fun", kSig_i_v) + builder.addFunction('main', kSig_i_v) + .addBody([ + kExprCallFunction, fun_index + ]) + .exportFunc(); + let instance = builder.instantiate({ m: { fun: fun }}); + assert_equals(7, instance.exports.main()); +}, "Function Import matching a signature"); + +test(() => { + let builder = new WasmModuleBuilder(); + let fun1 = new WebAssembly.Function({parameters:[], results:[]}, _ => 7); + let fun2 = new WebAssembly.Function({parameters:["i32"], results:[]}, _ => 8); + let fun3 = new WebAssembly.Function({parameters:[], results:["f32"]}, _ => 9); + let fun_index = builder.addImport("m", "fun", kSig_i_v) + builder.addFunction('main', kSig_i_v) + .addBody([ + kExprCallFunction, fun_index + ]) + .exportFunc(); + assert_throws_js(WebAssembly.LinkError, + () => builder.instantiate({ m: { fun: fun1 }}), + "builder.instantiate({ m: { fun: fun1 }})"); + assert_throws_js(WebAssembly.LinkError, + () => builder.instantiate({ m: { fun: fun2 }}), + "builder.instantiate({ m: { fun: fun2 }})"); + assert_throws_js(WebAssembly.LinkError, + () => builder.instantiate({ m: { fun: fun3 }}), + "builder.instantiate({ m: { fun: fun3 }})"); +}, "Function Import mismatching a signature"); + +test(() => { + let builder = new WasmModuleBuilder(); + let fun = new WebAssembly.Function({parameters:[], results:["i32"]}, _ => 7); + let fun_index = builder.addImport("m", "fun", kSig_i_v) + builder.addExport("fun1", fun_index); + builder.addExport("fun2", fun_index); + let instance = builder.instantiate({ m: { fun: fun }}); + assert_equals(instance.exports.fun1, instance.exports.fun2); + assert_equals(fun, instance.exports.fun1); +}, "Function Import module import re-export"); + +test(() => { + let imp = new WebAssembly.Function( + {parameters:["i32", "i32", "i32"], results:["i32"]}, + function(a, b, c) { if (c) return a; return b; }); + + let builder = new WasmModuleBuilder(); + let sig_index = builder.addType(kSig_i_iii); + let fun_index = builder.addImport("m", "imp", kSig_i_iii) + builder.addTable(kWasmFuncRef, 1, 1); + let table_index = 0; + let segment = builder.addActiveElementSegment( + table_index, wasmI32Const(0), [[kExprRefFunc, 0]], kWasmFuncRef); + + let main = builder.addFunction("rc", kSig_i_i) + .addBody([...wasmI32Const(-2), kExprI32Const, 3, kExprLocalGet, 0, + kExprI32Const, 0, kExprCallIndirect, sig_index, table_index]) + .exportFunc(); + + let instance = builder.instantiate({ m: { imp: imp }}); + + assert_equals(instance.exports.rc(1), -2); + assert_equals(instance.exports.rc(0), 3); +}, "Call_indirect js function"); \ No newline at end of file From f0c70d9a0cb2469cea4f208756cdaa0b56b0e3b2 Mon Sep 17 00:00:00 2001 From: Ilya Rezvov Date: Wed, 4 Oct 2023 09:25:37 -0600 Subject: [PATCH 2/2] Rename v8 tests to be tentative --- .../functions/{constructor.any.js => constructor.tentative.js} | 0 test/js-api/functions/{module.any.js => module.tentative.js} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename test/js-api/functions/{constructor.any.js => constructor.tentative.js} (100%) rename test/js-api/functions/{module.any.js => module.tentative.js} (100%) diff --git a/test/js-api/functions/constructor.any.js b/test/js-api/functions/constructor.tentative.js similarity index 100% rename from test/js-api/functions/constructor.any.js rename to test/js-api/functions/constructor.tentative.js diff --git a/test/js-api/functions/module.any.js b/test/js-api/functions/module.tentative.js similarity index 100% rename from test/js-api/functions/module.any.js rename to test/js-api/functions/module.tentative.js