| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- // This file has been modified by Microsoft on [12/2016].
- /*
- * Copyright 2016 WebAssembly Community Group participants
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- const cliArgs = WScript.Arguments || [];
- WScript.Flag("-wasmI64");
- if (cliArgs.length < 1) {
- print("usage: <exe> spec.js -args <filename.json> [start index] [end index] [-verbose] [-nt] -endargs");
- WScript.Quit(0);
- }
- if (typeof IMPORTS_FROM_OTHER_SCRIPT === "undefined") {
- IMPORTS_FROM_OTHER_SCRIPT = {};
- }
- let passed = 0;
- let failed = 0;
- // Parse arguments
- const iVerbose = cliArgs.indexOf("-verbose");
- const verbose = iVerbose !== -1;
- if (verbose) {
- cliArgs.splice(iVerbose, 1);
- }
- const iNoTrap = cliArgs.indexOf("-nt");
- const noTrap = iNoTrap !== -1;
- const trap = !noTrap;
- if (noTrap) {
- cliArgs.splice(iNoTrap, 1);
- }
- let file = "";
- let iTest = 0;
- function getValueStr(value, type) {
- if (typeof value === "undefined") {
- return "undefined";
- }
- switch (type) {
- case "i64":
- if (typeof value === "object") {
- const {high, low} = value;
- const convert = a => (a >>> 0).toString(16);
- return `0x${convert(high)}${convert(low).padStart(8, "0")}`;
- }
- // Fallthrough
- case "i32": return value.toString();
- case "f64":
- // Fallthrough
- case "f32":
- if (Object.is(value, -0)) {
- return "-0";
- }
- // Fallthrough
- }
- // Default case
- return value.toString();
- }
- function getArgsStr(args) {
- return args
- .map(({type, value}) => `${type}:${getValueStr(mapWasmArg({type, value}), type)}`)
- .join(", ");
- }
- function getActionStr(action) {
- const moduleName = action.module || "$$";
- switch (action.type) {
- case "invoke": return `${moduleName}.${action.field}(${getArgsStr(action.args)})`;
- case "get": return `${moduleName}[${action.field}]`;
- default: return "Unkown action type";
- }
- }
- function getCommandStr(command) {
- const base = `(${iTest}) ${file}:${command.line}`;
- switch (command.type) {
- case "module": return `${base}: generate module ${command.name ? ` as ${command.name}` : ""}`;
- case "register": return `${base}: register module ${command.name || "$$"} as ${command.as}`;
- case "assert_malformed":
- case "assert_unlinkable":
- case "assert_uninstantiable":
- case "assert_invalid": return `${base}: ${command.type} module`;
- case "assert_return": return `${base}: assert_return(${getActionStr(command.action)} == ${getArgsStr(command.expected)})`;
- case "assert_return_canonical_nan": return `${base}: assert_return_canonical_nan(${getActionStr(command.action)}`;
- case "assert_return_arithmetic_nan": return `${base}: assert_return_arithmetic_nan(${getActionStr(command.action)})`;
- case "action":
- case "assert_trap":
- case "assert_return_nan":
- case "assert_exhaustion":
- return `${base}: ${command.type}(${getActionStr(command.action)})`;
- }
- return base;
- }
- const CompareType = {
- none: Symbol(),
- exact: Symbol(),
- arithmeticNan: Symbol(),
- canonicalNan: Symbol()
- };
- function run(inPath, iStart, iEnd) {
- const lastSlash = Math.max(inPath.lastIndexOf("/"), inPath.lastIndexOf("\\"));
- const inDir = lastSlash === -1 ? "." : inPath.slice(0, lastSlash);
- file = inPath;
- const data = read(inPath);
- const {commands} = WebAssembly.wabt.convertWast2Wasm(data, {spec: true});
- const registry = Object.assign({
- "not wasm": {
- overload() {}
- },
- spectest: {
- print,
- print_i32: console.log.bind(console),
- print_i32_f32: console.log.bind(console),
- print_f64_f64: console.log.bind(console),
- print_f32: console.log.bind(console),
- print_f64: console.log.bind(console),
- global_i32: 666,
- global_f32: 666,
- global_f64: 666,
- table: new WebAssembly.Table({initial: 10, maximum: 20, element: "anyfunc"}),
- memory: new WebAssembly.Memory({initial: 1, maximum: 2})
- }
- }, IMPORTS_FROM_OTHER_SCRIPT);
- const moduleRegistry = {};
- moduleRegistry.currentModule = null;
- for (const command of commands) {
- ++iTest;
- if (iTest < iStart) {
- // always run module/register commands that happens before iStart
- if (command.type !== "module" && command.type !== "register") {
- continue;
- }
- } else if (iTest > iEnd) {
- continue;
- }
- if (verbose) {
- print(`Running:${getCommandStr(command)}`);
- }
- switch (command.type) {
- case "module":
- moduleCommand(inDir, command, registry, moduleRegistry);
- break;
- case "assert_malformed":
- assertMalformed(inDir, command);
- break;
- case "assert_unlinkable":
- assertUnlinkable(inDir, command, registry);
- break;
- case "assert_uninstantiable":
- assertUninstantiable(inDir, command, registry);
- break;
- case "assert_invalid":
- assertMalformed(inDir, command, registry);
- break;
- case "register": {
- const {as, name = "currentModule"} = command;
- if (moduleRegistry[name]) {
- registry[as] = moduleRegistry[name].exports;
- }
- break;
- }
- case "action":
- runSimpleAction(moduleRegistry, command);
- break;
- case "assert_return":
- assertReturn(moduleRegistry, command, CompareType.exact);
- break;
- case "assert_return_canonical_nan":
- assertReturn(moduleRegistry, command, CompareType.canonicalNan);
- break;
- case "assert_return_arithmetic_nan":
- assertReturn(moduleRegistry, command, CompareType.arithmeticNan);
- break;
- case "assert_exhaustion":
- assertStackExhaustion(moduleRegistry, command);
- break;
- case "assert_trap":
- if (trap) {
- assertTrap(moduleRegistry, command);
- } else {
- print(`${getCommandStr(command)} skipped because it has traps`);
- }
- break;
- default:
- print(`Unknown command: ${JSON.stringify(command)}`);
- }
- }
- end();
- }
- function createModule(baseDir, buffer, registry, output) {
- if (verbose) {
- const u8a = new Uint8Array(buffer);
- console.log(u8a);
- }
- output.module = new WebAssembly.Module(buffer);
- // We'll know if an error occurs at instanciation because output.module will be set
- output.instance = new WebAssembly.Instance(output.module, registry || undefined);
- }
- function moduleCommand(baseDir, command, registry, moduleRegistry) {
- const {buffer, name} = command;
- try {
- const output = {};
- createModule(baseDir, buffer, registry, output);
- if (name) {
- moduleRegistry[name] = output.instance;
- }
- moduleRegistry.currentModule = output.instance;
- ++passed;
- } catch (e) {
- ++failed;
- print(`${getCommandStr(command)} failed. Unexpected Error: ${e}`);
- }
- return null;
- }
- function assertMalformed(baseDir, command) {
- const {buffer, text} = command;
- // Test hook to prevent deferred parsing
- WScript.Flag("-off:wasmdeferred");
- const output = {};
- try {
- createModule(baseDir, buffer, null, output);
- ++failed;
- print(`${getCommandStr(command)} failed. Should have had an error`);
- } catch (e) {
- if (output.module) {
- ++failed;
- print(`${getCommandStr(command)} failed. Had a linking error, expected a compile error: ${e}`);
- } else if (e instanceof WebAssembly.CompileError) {
- ++passed;
- if (verbose) {
- print(`${getCommandStr(command)} passed. Had compile error: ${e}`);
- print(` Spec expected error: ${text}`);
- }
- } else {
- ++failed;
- print(`${getCommandStr(command)} failed. Expected a compile error: ${e}`);
- }
- } finally {
- // Reset the test hook
- WScript.Flag("-on:wasmdeferred");
- }
- }
- function assertUnlinkable(baseDir, command, registry) {
- const {buffer, text} = command;
- // Test hook to prevent deferred parsing
- WScript.Flag("-off:wasmdeferred");
- const output = {};
- try {
- createModule(baseDir, buffer, registry, output);
- ++failed;
- print(`${getCommandStr(command)} failed. Should have had an error`);
- } catch (e) {
- if (e instanceof WebAssembly.LinkError) {
- ++passed;
- if (verbose) {
- print(`${getCommandStr(command)} passed. Had linking error: ${e}`);
- print(` Spec expected error: ${text}`);
- }
- } else {
- ++failed;
- print(`${getCommandStr(command)} failed. Expected a linking error: ${e}`);
- }
- } finally {
- // Reset the test hook
- WScript.Flag("-on:wasmdeferred");
- }
- }
- function assertUninstantiable(baseDir, command, registry) {
- const {buffer, text} = command;
- // Test hook to prevent deferred parsing
- WScript.Flag("-off:wasmdeferred");
- const output = {};
- try {
- createModule(baseDir, buffer, registry, output);
- ++failed;
- print(`${getCommandStr(command)} failed. Should have had an error`);
- } catch (e) {
- if (e instanceof WebAssembly.RuntimeError) {
- ++passed;
- if (verbose) {
- print(`${getCommandStr(command)} passed. Had instanciation error: ${e}`);
- print(` Spec expected error: ${text}`);
- }
- } else {
- ++failed;
- print(`${getCommandStr(command)} failed. Expected a instanciation error: ${e}`);
- }
- } finally {
- // Reset the test hook
- WScript.Flag("-on:wasmdeferred");
- }
- }
- function genConverters() {
- const buffer = WebAssembly.wabt.convertWast2Wasm(`
- (module
- (func (export "convertI64") (param i64) (result i64) (get_local 0))
- (func (export "toF32") (param i32) (result f32) (f32.reinterpret/i32 (get_local 0)))
- (func (export "toF64") (param i64) (result f64) (f64.reinterpret/i64 (get_local 0)))
- )`);
- const module = new WebAssembly.Module(buffer);
- const instance = new WebAssembly.Instance(module);
- return instance.exports;
- }
- const {toF32, toF64, convertI64} = genConverters();
- function mapWasmArg({type, value}) {
- switch (type) {
- case "i32": return parseInt(value)|0;
- case "i64": return convertI64(value); // will convert string to {high, low}
- case "f32": return toF32(parseInt(value));
- case "f64": return toF64(value);
- }
- throw new Error("Unknown argument type");
- }
- function runCompare(wasmFn, action, expected, compareType) {
- let moduleTxt;
- const argTypes = action.args.map(({type}) => type);
- const params = argTypes.length > 0 ? `(param ${argTypes.join(" ")})` : "";
- let returnType = "void";
- if (expected.length === 0) {
- if (compareType !== CompareType.exact) {
- throw new Error("Must have expected type to runCompare if not doing Exact comparison");
- }
- // Without expected result, we are just making sure we are able to call the function
- moduleTxt = `
- (module
- (import "test" "fn" (func $fn ${params}))
- (func (export "res"))
- (func (export "compare") (result i32)
- ${action.args.map(({type, value}) => {
- switch (type) {
- case "i32": return `(i32.const ${value})`;
- case "i64": return `(i64.const ${value})`;
- case "f32": return `(f32.reinterpret/i32 (i32.const ${value}))`;
- case "f64": return `(f64.reinterpret/i64 (i64.const ${value}))`;
- }
- throw new Error("Unknown argument type");
- }).join("\n")}
- (call $fn)
- (i32.const 1)
- )
- )`;
- } else {
- let compareTxt = "";
- const {type: expectedType, value: expectedValue} = expected[0];
- returnType = expectedType;
- const resultSize = expectedType.endsWith("32") ? 32 : 64;
- const matchingIntType = resultSize === 32 ? "i32" : "i64";
- switch (compareType) {
- case CompareType.exact:
- switch (expectedType) {
- case "i32":
- case "i64":
- compareTxt += `(${expectedType}.eq (${expectedType}.const ${expectedValue}))`;
- break;
- case "f32":
- compareTxt += `(i32.reinterpret/f32) (i32.eq (i32.const ${expectedValue}))`;
- break;
- case "f64":
- compareTxt += `(i64.reinterpret/f64) (i64.eq (i64.const ${expectedValue}))`;
- break;
- }
- break;
- case CompareType.arithmeticNan: {
- const expectedResult = resultSize === 32 ? "0x7f800000" : "0x7ff0000000000000";
- compareTxt = `
- (${matchingIntType}.reinterpret/${expectedType})
- (${matchingIntType}.and (${matchingIntType}.const ${expectedResult}))
- (${matchingIntType}.eq (${matchingIntType}.const ${expectedResult}))`;
- break;
- }
- case CompareType.canonicalNan: {
- const posNaN = resultSize === 32 ? "0x7fc00000" : "0x7ff8000000000000";
- const negNaN = resultSize === 32 ? "0xffc00000" : "0xfff8000000000000";
- compareTxt = `
- (${matchingIntType}.reinterpret/${expectedType})
- (tee_local $intNan)
- (${matchingIntType}.eq (${matchingIntType}.const ${posNaN}))
- (get_local $intNan)
- (${matchingIntType}.eq (${matchingIntType}.const ${negNaN}))
- (i32.or)`;
- break;
- }
- default:
- throw new Error("runCompare: Unsupported compare type");
- }
- moduleTxt = `
- (module
- (import "test" "fn" (func $fn ${params} (result ${expectedType})))
- (global $res (mut ${expectedType}) (${expectedType}.const 0))
- (func (export "res") (result ${expectedType}) (get_global $res))
- (func (export "compare") (result i32)
- (local $localRes ${expectedType}) (local $intNan ${matchingIntType})
- ${action.args.map(({type, value}) => {
- switch (type) {
- case "i32": return `(i32.const ${value})`;
- case "i64": return `(i64.const ${value})`;
- case "f32": return `(f32.reinterpret/i32 (i32.const ${value}))`;
- case "f64": return `(f64.reinterpret/i64 (i64.const ${value}))`;
- }
- throw new Error("Unknown argument type");
- }).join("\n")}
- (call $fn)
- (tee_local $localRes)
- (set_global $res)
- (get_local $localRes)
- ${compareTxt}
- )
- )`;
- }
- if (verbose) {
- console.log(moduleTxt);
- }
- const module = new WebAssembly.Module(WebAssembly.wabt.convertWast2Wasm(moduleTxt));
- const {exports: {compare, res}} = new WebAssembly.Instance(module, {test: {fn: wasmFn}});
- const hasPassed = compare() === 1;
- const original = res();
- return {passed: hasPassed, original: {value: original, type: returnType}};
- }
- function assertReturn(moduleRegistry, command, compareType) {
- const {action, expected} = command;
- try {
- let success = false;
- let originalResult;
- if (action.type === "invoke") {
- const wasmFn = getField(moduleRegistry, action);
- const res = runCompare(wasmFn, action, expected, compareType);
- originalResult = res.original;
- success = res.passed;
- } else if (action.type === "get") {
- const field = getField(moduleRegistry, action);
- originalResult = {value: field, type: expected[0].type};
- switch (compareType) {
- case CompareType.exact: success = field === mapWasmArg(expected[0]); break;
- case CompareType.arithmeticNan:
- case CompareType.canonicalNan: success = isNaN(field); break;
- default:
- throw new Error("assertReturn: Unsupported compare type");
- }
- }
- if (success) {
- passed++;
- if (verbose) {
- print(`${getCommandStr(command)} passed.`);
- }
- } else {
- print(`${getCommandStr(command)} failed. Returned ${getValueStr(originalResult.value, originalResult.type)}`);
- failed++;
- }
- } catch (e) {
- print(`${getCommandStr(command)} failed. Unexpectedly threw: ${e}`);
- failed++;
- }
- }
- function assertTrap(moduleRegistry, command) {
- const {action, text} = command;
- try {
- runAction(moduleRegistry, action);
- print(`${getCommandStr(command)} failed. Should have had an error`);
- failed++;
- } catch (e) {
- if (e instanceof WebAssembly.RuntimeError) {
- passed++;
- if (verbose) {
- print(`${getCommandStr(command)} passed. Error thrown: ${e}`);
- print(` Spec error message expected: ${text}`);
- }
- } else {
- failed++;
- print(`${getCommandStr(command)} failed. Unexpected error thrown: ${e}`);
- }
- }
- }
- let StackOverflow;
- function assertStackExhaustion(moduleRegistry, command) {
- if (!StackOverflow) {
- // Get the stack overflow exception type
- // Javascript and WebAssembly must throw the same type of error
- try { (function f() { 1 + f() })() } catch (e) { StackOverflow = e.constructor }
- }
- const {action} = command;
- try {
- runAction(moduleRegistry, action);
- print(`${getCommandStr(command)} failed. Should have exhausted the stack`);
- failed++;
- } catch (e) {
- if (e instanceof StackOverflow) {
- passed++;
- if (verbose) {
- print(`${getCommandStr(command)} passed. Had a StackOverflow`);
- }
- } else {
- failed++;
- print(`${getCommandStr(command)} failed. Unexpected error thrown: ${e}`);
- }
- }
- }
- function runSimpleAction(moduleRegistry, command) {
- try {
- const res = runAction(moduleRegistry, command.action);
- ++passed;
- if (verbose) {
- print(`${getCommandStr(command)} = ${res}`);
- }
- } catch (e) {
- ++failed;
- print(`${getCommandStr(command)} failed. Unexpectedly threw: ${e}`);
- }
- }
- function getField(moduleRegistry, action) {
- const moduleName = action.module !== undefined ? action.module : "currentModule";
- const m = moduleRegistry[moduleName];
- if (!m) {
- throw new Error("Module unavailable to run action");
- }
- const {field} = action;
- if (!(field in m.exports)) {
- throw new Error(`Unable to find field ${field} in ${moduleName}`)
- }
- return m.exports[field];
- }
- function runAction(moduleRegistry, action) {
- const field = getField(moduleRegistry, action);
- switch (action.type) {
- case "invoke": {
- const {args} = action;
- const mappedArgs = args.map(({value}) => value);
- const wasmFn = field;
- const res = wasmFn(...mappedArgs);
- return res;
- }
- case "get": {
- return field;
- }
- default:
- print(`Unknown action: ${JSON.stringify(action)}`);
- }
- }
- function end() {
- print(`${passed}/${passed + failed} tests passed.`);
- }
- run(cliArgs[0], cliArgs[1] | 0, cliArgs[2] === undefined ? undefined : cliArgs[2] | 0);
|