| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- let verbose = false;
- let doInvalid = true;
- const buf = readbuffer("binaries/api.wasm");
- const imports = {
- test: {
- fn() {return 1;},
- fn2() {return 2.14;},
- memory: new WebAssembly.Memory({initial: 1, maximum: 1}),
- g1: 45,
- g2: -8,
- },
- table: {"": new WebAssembly.Table({element: "anyfunc", initial: 30, maximum: 100})}
- };
- function overrideImports(overrides) {
- return {
- test: Object.assign({}, imports.test, overrides),
- table: overrides.table ? {"":overrides.table} : imports.table
- };
- }
- function objectToString(o, maxDepth = 5, depth = 0) {
- if (depth >= maxDepth) {
- return o.toString();
- }
- if (Array.isArray(o)) {
- return `[${o.map(e => objectToString(e, maxDepth, depth + 1)).join(", ")}]`;
- } else if (typeof o === "object") {
- return `{${Object.keys(o).map(k => `"${k}":"${objectToString(o[k], maxDepth, depth + 1)}"`).join(", ")}}`;
- }
- return o.toString();
- }
- function test(module, {exports} = {}) {
- if (module) {
- console.log("Testing module");
- console.log("exports");
- console.log(objectToString(WebAssembly.Module.exports(module)));
- console.log("imports");
- console.log(objectToString(WebAssembly.Module.imports(module)));
- }
- if (exports) {
- console.log("Testing instance");
- console.log(`f1: ${exports.f1()}`);
- console.log(`fn: ${exports.fn()}`);
- console.log(`fn2: ${exports.fn2()}`);
- console.log(`g1: ${exports.g1}`);
- console.log(`g2: ${exports.g2}`);
- console.log(`g3: ${exports.g3}`);
- }
- }
- async function testInvalidCases(tests) {
- if (!doInvalid) return;
- let i = 0;
- for (const testCase of tests) {
- if (verbose) {
- console.log(testCase);
- }
- try {
- await testCase();
- console.log(`Test ${i++} failed. Should have thrown error`);
- } catch (e) {
- console.log(`Test ${i++} passed. Expected Error: ${e}`);
- }
- }
- }
- function createView(bytes) {
- const buffer = new ArrayBuffer(bytes.length);
- const view = new Uint8Array(buffer);
- for (let i = 0; i < bytes.length; ++i) {
- view[i] = bytes.charCodeAt(i);
- }
- return view;
- }
- const invalidBuffers = [
- ,
- "",
- "123",
- 4568,
- {},
- {length: 15},
- function() {},
- new ArrayBuffer(),
- createView("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80"),
- new Proxy(buf, {get(target, name) {return target[name];}}),
- ];
- const invalidImports = [
- ,
- "",
- 123,
- function() {},
- {wrongNamespace: imports.test, table: imports.table},
- ];
- const invalidModules = [
- null,
- "123",
- {},
- new Proxy({}, {})
- ];
- async function testValidate() {
- console.log("\nWebAssembly.validate tests");
- await testInvalidCases([
- // Invalid buffer source
- ...(invalidBuffers.map(b => () => {
- if (!WebAssembly.validate(b)) {
- throw new Error("Buffer source is the right type, but doesn't validate");
- }
- })),
- ]);
- // Make sure empty buffer doesn't validate and doesn't throw
- const emptyBuffer = new ArrayBuffer();
- console.log(`WebAssembly.validate(new ArrayBuffer()) = ${WebAssembly.validate(emptyBuffer)}`);
- console.log(`WebAssembly.validate(new Uint8Array(emptyBuffer)) = ${WebAssembly.validate(new Uint8Array(emptyBuffer))}`);
- console.log(`api.wasm valid: ${WebAssembly.validate(buf)}`);
- }
- async function testCompile() {
- console.log("\nWebAssembly.compile tests");
- await testInvalidCases([
- ...(invalidBuffers.map(b => () => WebAssembly.compile(b))),
- ]);
- const module = await WebAssembly.compile(buf);
- test(module);
- }
- async function testInstantiate(baseModule) {
- console.log("\nWebAssembly.instantiate tests");
- await testInvalidCases([
- // Invalid buffer source
- ...(invalidBuffers.map(b => () => WebAssembly.instantiate(b))),
- // Invalid Imports
- ...(invalidImports.map(i => () => WebAssembly.instantiate(buf, i))),
- ...(invalidImports.map(i => () => WebAssembly.instantiate(baseModule, i))),
- ]);
- const {module, instance} = await WebAssembly.instantiate(buf, imports);
- test(module, instance);
- const instance2 = await WebAssembly.instantiate(baseModule, imports);
- test(baseModule, instance2);
- }
- async function testModuleConstructor() {
- console.log("\nnew WebAssembly.Module tests");
- await testInvalidCases([
- () => WebAssembly.Module(),
- () => WebAssembly.Module(buf),
- ...(invalidBuffers.map(b => () => new WebAssembly.Module(b))),
- ]);
- const module = new WebAssembly.Module(buf);
- test(module);
- }
- async function testModuleApi() {
- console.log("\nWebAssembly.Module api tests");
- console.log("\nWebAssembly.Module.exports invalid tests");
- await testInvalidCases([
- ...(invalidModules.map(b => () => WebAssembly.Module.exports(b))),
- ]);
- console.log("\nWebAssembly.Module.imports invalid tests");
- await testInvalidCases([
- ...(invalidModules.map(b => () => WebAssembly.Module.imports(b))),
- ]);
- }
- async function testModuleCustomSection(baseModule) {
- console.log("\nWebAssembly.Module.customSections tests");
- await testInvalidCases([
- ...(invalidModules.map(b => () => WebAssembly.Module.customSections(b))),
- () => WebAssembly.Module.customSections(baseModule, {toString() {throw new Error("Doesn't support toString");}}),
- () => WebAssembly.Module.customSections(baseModule, Symbol()),
- ]);
- const baseSections = WebAssembly.Module.customSections(baseModule, "");
- if (!Array.isArray(baseSections) || baseSections.length !== 0) {
- console.log("Invalid result for WebAssembly.Module.customSections");
- console.log(baseSections);
- }
- let passed = 0, failed = 0;
- function compare(module, customSectionName, expected) {
- const sections = WebAssembly.Module.customSections(module, customSectionName);
- if (sections.length !== expected.length) {
- console.log(`Invalid length. Got ${sections.length}, expected ${expected.length}. ${(new Error()).stack}`);
- ++failed;
- return;
- }
- let asExpected = true;
- for (let iSection = 0; iSection < sections.length; ++iSection) {
- const resBuffer = sections[iSection];
- const expectedStr = expected[iSection];
- const view = new Uint8Array(resBuffer);
- let resStr = "";
- for (let i = 0; i < view.length; ++i) {
- resStr += String.fromCharCode(view[i]);
- }
- if (resStr !== expectedStr) {
- asExpected = false;
- console.log(`Invalid buffer result. Got ${resStr}, expected ${expectedStr}. ${(new Error()).stack}`);
- }
- }
- passed += asExpected;
- failed += !asExpected;
- }
- // See wasts/custom_section.wast for reference
- const module1 = new WebAssembly.Module(createView("\x00\x61\x73\x6d\x01\x00\x00\x00\x00\x24\x10\x61\x20\x63\x75\x73\x74\x6f\x6d\x20\x73\x65\x63\x74\x69\x6f\x6e\x74\x68\x69\x73\x20\x69\x73\x20\x74\x68\x65\x20\x70\x61\x79\x6c\x6f\x61\x64\x00\x20\x10\x61\x20\x63\x75\x73\x74\x6f\x6d\x20\x73\x65\x63\x74\x69\x6f\x6e\x74\x68\x69\x73\x20\x69\x73\x20\x70\x61\x79\x6c\x6f\x61\x64\x00\x11\x10\x61\x20\x63\x75\x73\x74\x6f\x6d\x20\x73\x65\x63\x74\x69\x6f\x6e\x00\x10\x00\x74\x68\x69\x73\x20\x69\x73\x20\x70\x61\x79\x6c\x6f\x61\x64\x00\x01\x00\x00\x24\x10\x00\x00\x63\x75\x73\x74\x6f\x6d\x20\x73\x65\x63\x74\x69\x6f\x00\x74\x68\x69\x73\x20\x69\x73\x20\x74\x68\x65\x20\x70\x61\x79\x6c\x6f\x61\x64"));
- compare(module1, "a custom section", [
- "this is the payload",
- "this is payload",
- "",
- ]);
- compare(module1, "", [
- "this is payload",
- "",
- ]);
- compare(module1, "\00\00custom sectio\00", [
- "this is the payload",
- ]);
- const module2 = new WebAssembly.Module(createView("\x00\x61\x73\x6d\x01\x00\x00\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x01\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x02\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x03\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x04\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x05\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x06\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x07\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x09\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x0a\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x0b\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64"));
- compare(module2, "custom", new Proxy({length: 22}, {
- get: (target, name) => name in target ? target[name] : "payload"
- }));
- const module3 = new WebAssembly.Module(createView("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x07\x01\x60\x02\x7f\x7f\x01\x7f\x00\x1a\x06\x63\x75\x73\x74\x6f\x6d\x74\x68\x69\x73\x20\x69\x73\x20\x74\x68\x65\x20\x70\x61\x79\x6c\x6f\x61\x64\x03\x02\x01\x00\x07\x0a\x01\x06\x61\x64\x64\x54\x77\x6f\x00\x00\x0a\x09\x01\x07\x00\x20\x00\x20\x01\x6a\x0b\x00\x1b\x07\x63\x75\x73\x74\x6f\x6d\x32\x74\x68\x69\x73\x20\x69\x73\x20\x74\x68\x65\x20\x70\x61\x79\x6c\x6f\x61\x64"));
- compare(module3, "custom", ["this is the payload"]);
- compare(module3, "custom2", ["this is the payload"]);
- console.log(`${passed}/${passed + failed} tests passed`);
- }
- async function testInstanceConstructor(module) {
- console.log("\nnew WebAssembly.Instance tests");
- const instance = new WebAssembly.Instance(module, imports);
- await testInvalidCases([
- () => WebAssembly.Instance(),
- () => WebAssembly.Instance(module, imports),
- ...(invalidImports.map(i => () => new WebAssembly.Instance(module, i))),
- () => new WebAssembly.Instance(module, overrideImports({fn: 4})),
- () => new WebAssembly.Instance(module, overrideImports({fn2: instance.exports.fn /*wrong signature*/})),
- () => new WebAssembly.Instance(module, overrideImports({memory: 123})),
- () => new WebAssembly.Instance(module, overrideImports({memory: ""})),
- () => new WebAssembly.Instance(module, overrideImports({memory: {}})),
- () => new WebAssembly.Instance(module, overrideImports({table: 123})),
- () => new WebAssembly.Instance(module, overrideImports({table: "135"})),
- () => new WebAssembly.Instance(module, overrideImports({table: {}})),
- ]);
- test(null, instance);
- }
- async function testMemoryApi(baseModule) {
- console.log("\nWebAssembly.Memory tests");
- await testInvalidCases([
- () => WebAssembly.Memory({initial: 1, maximum: 2}),
- () => new WebAssembly.Memory(),
- () => new WebAssembly.Memory(0),
- () => new WebAssembly.Memory(""),
- () => Reflect.apply(WebAssembly.Memory.prototype.buffer, null, []),
- () => Reflect.apply(WebAssembly.Memory.prototype.buffer, {}, []),
- () => Reflect.apply(WebAssembly.Memory.prototype.buffer, baseModule, []),
- () => Reflect.apply(WebAssembly.Memory.prototype.grow, null, []),
- () => Reflect.apply(WebAssembly.Memory.prototype.grow, {}, []),
- () => Reflect.apply(WebAssembly.Memory.prototype.grow, baseModule, []),
- // todo:: test invalid memory imports, need to find the spec
- ]);
- const toHex = v => typeof v === "number" ? `0x${v.toString(16)}` : "undefined";
- const memory = new WebAssembly.Memory({initial: 1, maximum: 2});
- const {exports: {load}} = new WebAssembly.Instance(baseModule, overrideImports({memory}));
- const v1 = load(1);
- const v2 = load(5);
- const v3 = load(9) & 0xFFFF;
- console.log(`${toHex(v1)} == "0123"`);
- console.log(`${toHex(v2)} == "4567"`);
- console.log(`${toHex(v3)} == "89"`);
- memory.buffer = null; // make sure we can't modify the buffer
- let view = new Int32Array(memory.buffer);
- view[0] = 45;
- console.log(`heap32[0] = ${load(0)}`);
- function testOutOfBounds(index) {
- try {
- load(index);
- console.log("Should have trap with out of bounds error");
- } catch (e) {
- if (e instanceof WebAssembly.RuntimeError) {
- console.log(`Correctly trap on heap access at ${index}`);
- } else {
- console.log(`Unexpected Error: ${e}`);
- }
- }
- }
- const pageSize = 64 * 1024;
- console.log(`memory.buffer.byteLength = ${memory.buffer.byteLength} == ${pageSize}`);
- {
- view[pageSize / 4 - 1] = 0x12345678;
- console.log(`view32[${pageSize / 4 - 1}] = ${toHex(view[pageSize / 4 - 1])}`);
- console.log(`view32[${pageSize / 4}] = ${toHex(view[pageSize / 4])}`);
- console.log(`heap[${pageSize - 4}] = ${toHex(load(pageSize - 4))}`);
- testOutOfBounds(pageSize - 3);
- testOutOfBounds(pageSize - 2);
- testOutOfBounds(pageSize - 1);
- testOutOfBounds(pageSize);
- }
- console.log("grow by 1 page");
- memory.grow(1);
- console.log(`memory.buffer.byteLength = ${memory.buffer.byteLength} == 2 * ${pageSize}`);
- view = new Int32Array(memory.buffer); // buffer has been detached, reset the view
- {
- view[2 * pageSize / 4 - 1] = 0x87654321;
- console.log(`view32[${2 * pageSize / 4 - 1}] = ${toHex(view[2 * pageSize / 4 - 1])}`);
- console.log(`view32[${2 * pageSize / 4}] = ${toHex(view[2 * pageSize / 4])}`);
- console.log(`heap[${pageSize - 4}] = ${toHex(load(pageSize - 4))}`);
- console.log(`heap[${2 * pageSize - 4}] = ${toHex(load(2 * pageSize - 4))}`);
- testOutOfBounds(2 * pageSize - 3);
- testOutOfBounds(2 * pageSize - 2);
- testOutOfBounds(2 * pageSize - 1);
- testOutOfBounds(2 * pageSize);
- }
- try {
- memory.grow(1);
- console.log("Should have trap when growing past maximum");
- } catch (e) {
- if (e instanceof RangeError) {
- console.log("Correctly trap when growing past maximum");
- } else {
- console.log(`Unexpected Error: ${e}`);
- }
- }
- }
- async function testTableApi(baseModule) {
- //new WebAssembly.Table({element: "anyfunc", initial: 30, maximum: 100})
- const table = new WebAssembly.Table({element: "anyfunc", initial: 30, maximum: 100});
- console.log("\nWebAssembly.Table tests");
- await testInvalidCases([
- () => WebAssembly.Table({element: "anyfunc", initial: 30, maximum: 100}),
- () => new WebAssembly.Table(),
- () => new WebAssembly.Table(0),
- () => new WebAssembly.Table(""),
- () => new WebAssembly.Table({element: "notanyfunc", initial: 30, maximum: 100}),
- () => Reflect.apply(WebAssembly.Table.prototype.length, null, []),
- () => Reflect.apply(WebAssembly.Table.prototype.length, {}, []),
- () => Reflect.apply(WebAssembly.Table.prototype.length, baseModule, []),
- () => Reflect.apply(WebAssembly.Table.prototype.grow, null, []),
- () => Reflect.apply(WebAssembly.Table.prototype.grow, {}, []),
- () => Reflect.apply(WebAssembly.Table.prototype.grow, baseModule, []),
- () => Reflect.apply(WebAssembly.Table.prototype.get, null, [0]),
- () => Reflect.apply(WebAssembly.Table.prototype.get, {}, [0]),
- () => Reflect.apply(WebAssembly.Table.prototype.get, baseModule, [0]),
- () => table.get(30),
- () => table.get(100),
- () => Reflect.apply(WebAssembly.Table.prototype.get, table, [30]),
- () => Reflect.apply(WebAssembly.Table.prototype.get, table, [100]),
- ]);
- const {instance: {exports: {"f32.load": load}}} = await WebAssembly.instantiate(readbuffer("f32address.wasm"), {});
- const {exports: {fn, call_i32, call_f32}} = new WebAssembly.Instance(baseModule, overrideImports({table, fn2: load}));
- const {exports: {fn: myFn}} = new WebAssembly.Instance(baseModule, overrideImports({fn: () => 123456}));
- await testInvalidCases([
- () => table.set(30, fn),
- () => table.set(100, fn),
- () => table.set(0),
- () => table.set(0, {}),
- () => table.set(0, ""),
- () => table.set(0, 123),
- () => table.set(0, function(){}),
- () => table.set(0, () => 2),
- () => call_i32(0),
- () => call_f32(0),
- () => call_i32(29),
- () => call_i32(30),
- () => call_i32(2),
- () => call_f32(1),
- ]);
- console.log(`Current length: ${table.length}`);
- table.length = 456;
- console.log(`Length after attempt to modify : ${table.length}`);
- console.log(`Is element in table the same as the one exported: ${table.get(1) === fn}`);
- console.log(`Unset element should be null: ${table.get(0)}`);
- table.set(0, myFn);
- console.log(`call_i32(0): ${call_i32(0)}`);
- console.log(`call_i32(1): ${call_i32(1)}`);
- console.log(`call_f32(2): ${call_f32(2)}`);
- table.set(0, fn);
- console.log(`call_i32(0): ${call_i32(0)}`);
- table.set(29, myFn);
- table.grow(1);
- table.set(30, myFn);
- console.log(`call_i32(29): ${call_i32(29)}`);
- console.log(`call_i32(30): ${call_i32(30)}`);
- const table2 = new WebAssembly.Table({element: "anyfunc"});
- table.grow(0);
- try {table2.get(0); console.log("Failed. Unexpected successfull call to table2.get(0)");} catch (e) {}
- table.grow(1);
- table.set(0, myFn);
- console.log(`table2[0](): ${table.get(0)()}`);
- }
- async function main() {
- const args = WScript.Arguments;
- verbose = args.includes("-v");
- doInvalid = !args.includes("-i");
- const [start = 0, end = 99] = args
- .filter(a => !a.startsWith("-"))
- .map(a => parseInt(a));
- const baseModule = new WebAssembly.Module(buf);
- const tests = [
- /*0*/ testValidate,
- /*1*/ testCompile,
- /*2*/ testInstantiate,
- /*3*/ testModuleConstructor,
- /*4*/ testModuleApi,
- /*5*/ testModuleCustomSection,
- /*6*/ testInstanceConstructor,
- /*7*/ testMemoryApi,
- /*8*/ testTableApi,
- ];
- for (let i = 0; i < tests.length; ++i) {
- if (i >= start && i <= end) {
- try {
- await tests[i](baseModule);
- } catch (e) {
- console.log("Unexpected error");
- console.log(e.stack);
- }
- }
- }
- }
- main().then(() => console.log("done"), err => {
- console.log(err);
- console.log(err.stack);
- });
|