diff options
-rwxr-xr-x | scripts/fuzz_opt.py | 11 | ||||
-rw-r--r-- | scripts/fuzz_shell.js | 49 | ||||
-rw-r--r-- | src/tools/execution-results.h | 14 | ||||
-rw-r--r-- | src/tools/js-wrapper.h | 134 | ||||
-rw-r--r-- | src/tools/wasm-opt.cpp | 24 | ||||
-rw-r--r-- | test/lit/exec/no-compare-refs.wast | 4 | ||||
-rw-r--r-- | test/lit/help/wasm-opt.test | 4 | ||||
-rw-r--r-- | test/lit/unicode-filenames.wast | 2 | ||||
-rw-r--r-- | test/passes/emit-js-wrapper=a.js.txt | 84 | ||||
-rw-r--r-- | test/passes/emit-js-wrapper=a.js.wast | 37 | ||||
-rw-r--r-- | test/passes/emit-js-wrapper=a.js.wast.js | 79 | ||||
-rw-r--r-- | test/passes/fuzz-exec_all-features.txt | 4 | ||||
-rw-r--r-- | test/passes/simplify-globals_all-features_fuzz-exec.txt | 4 |
13 files changed, 62 insertions, 388 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 8e80c2bb9..8dc87cb7a 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -665,8 +665,11 @@ def run_d8_js(js, args=[], liftoff=True): return run_vm(cmd) +FUZZ_SHELL_JS = in_binaryen('scripts', 'fuzz_shell.js') + + def run_d8_wasm(wasm, liftoff=True): - return run_d8_js(in_binaryen('scripts', 'fuzz_shell.js'), [wasm], liftoff=liftoff) + return run_d8_js(FUZZ_SHELL_JS, [wasm], liftoff=liftoff) def all_disallowed(features): @@ -746,8 +749,7 @@ class CompareVMs(TestCaseHandler): name = 'd8' def run(self, wasm, extra_d8_flags=[]): - run([in_bin('wasm-opt'), wasm, '--emit-js-wrapper=' + wasm + '.js'] + FEATURE_OPTS) - return run_vm([shared.V8, wasm + '.js'] + shared.V8_OPTS + extra_d8_flags + ['--', wasm]) + return run_vm([shared.V8, FUZZ_SHELL_JS] + shared.V8_OPTS + extra_d8_flags + ['--', wasm]) def can_run(self, wasm): # INITIAL_CONTENT is disallowed because some initial spec testcases @@ -1036,7 +1038,8 @@ class Wasm2JS(TestCaseHandler): compare_between_vms(before, interpreter, 'Wasm2JS (vs interpreter)') def run(self, wasm): - wrapper = run([in_bin('wasm-opt'), wasm, '--emit-js-wrapper=/dev/stdout'] + FEATURE_OPTS) + with open(FUZZ_SHELL_JS) as f: + wrapper = f.read() cmd = [in_bin('wasm2js'), wasm, '--emscripten'] # avoid optimizations if we have nans, as we don't handle them with # full precision and optimizations can change things diff --git a/scripts/fuzz_shell.js b/scripts/fuzz_shell.js index 4a3b42d9f..743e838b5 100644 --- a/scripts/fuzz_shell.js +++ b/scripts/fuzz_shell.js @@ -38,15 +38,34 @@ var detrand = (function() { }; })(); -// Fuzz integration. -function logValue(x, y) { +// Print out a value in a way that works well for fuzzing. +function printed(x, y) { if (typeof y !== 'undefined') { - console.log('[LoggingExternalInterface logging ' + x + ' ' + y + ']'); + // A pair of i32s which are a legalized i64. + return x + ' ' + y; + } else if (x === null) { + // JS has just one null. Print that out rather than typeof null which is + // 'object', below. + return 'null'; + } else if (typeof x !== 'number' && typeof x !== 'string') { + // Something that is not a number or string, like a reference. We can't + // print a reference because it could look different after opts - imagine + // that a function gets renamed internally (that is, the problem is that + // JS printing will emit some info about the reference and not a stable + // external representation of it). In those cases just print the type, + // which will be 'object' or 'function'. + return typeof x; } else { - console.log('[LoggingExternalInterface logging ' + x + ']'); + // A number. Print the whole thing. + return '' + x; } } +// Fuzzer integration. +function logValue(x, y) { + console.log('[LoggingExternalInterface logging ' + printed(x, y) + ']'); +} + // Set up the imports. var imports = { 'fuzzing-support': { @@ -94,21 +113,25 @@ function refreshView() { } // Run the wasm. -var sortedExports = []; for (var e in exports) { - sortedExports.push(e); -} -sortedExports.sort(); -sortedExports.forEach(function(e) { - if (typeof exports[e] !== 'function') return; + if (typeof exports[e] !== 'function') { + continue; + } + // Send the function a null for each parameter. Null can be converted without + // error to both a number and a reference. + var func = exports[e]; + var args = []; + for (var i = 0; i < func.length; i++) { + args.push(null); + } try { console.log('[fuzz-exec] calling ' + e); - var result = exports[e](); + var result = func.apply(null, args); if (typeof result !== 'undefined') { - console.log('[fuzz-exec] note result: $' + e + ' => ' + result); + console.log('[fuzz-exec] note result: ' + e + ' => ' + printed(result)); } } catch (e) { console.log('exception!');// + [e, e.stack]); } -}); +} diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index 0cd40959e..add7cd3a5 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -119,8 +119,18 @@ struct ExecutionResults { if (resultType.isRef() && !resultType.isString()) { // Don't print reference values, as funcref(N) contains an index // for example, which is not guaranteed to remain identical after - // optimizations. - std::cout << resultType << '\n'; + // optimizations. Do not print the type in detail (as even that + // may change due to closed-world optimizations); just print a + // simple type like JS does, 'object' or 'function', but also + // print null for a null (so a null function does not get + // printed as object, as in JS we have typeof null == 'object'). + if (values->size() == 1 && (*values)[0].isNull()) { + std::cout << "null\n"; + } else if (resultType.isFunction()) { + std::cout << "function\n"; + } else { + std::cout << "object\n"; + } } else { // Non-references can be printed in full. So can strings, since we // always know how to print them and there is just one string diff --git a/src/tools/js-wrapper.h b/src/tools/js-wrapper.h deleted file mode 100644 index edee301ca..000000000 --- a/src/tools/js-wrapper.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2017 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. - */ - -// -// Emit a JavaScript wrapper to run a wasm module with some test -// values, useful for fuzzing. -// - -#include "wasm-type.h" -#include "wasm.h" -#include <string> - -namespace wasm { - -inline std::string generateJSWrapper(Module& wasm) { - std::string ret; - ret += "if (typeof console === 'undefined') {\n" - " console = { log: print };\n" - "}\n" - "var tempRet0;\n" - "var binary;\n" - "if (typeof process === 'object' && typeof require === 'function' /* " - "node.js detection */) {\n" - " var args = process.argv.slice(2);\n" - " binary = require('fs').readFileSync(args[0]);\n" - " if (!binary.buffer) binary = new Uint8Array(binary);\n" - "} else {\n" - " var args;\n" - " if (typeof scriptArgs != 'undefined') {\n" - " args = scriptArgs;\n" - " } else if (typeof arguments != 'undefined') {\n" - " args = arguments;\n" - " }\n" - " if (typeof readbuffer === 'function') {\n" - " binary = new Uint8Array(readbuffer(args[0]));\n" - " } else {\n" - " binary = read(args[0], 'binary');\n" - " }\n" - "}\n" - "function literal(x, type) {\n" - " var ret = '';\n" - " switch (type) {\n" - " case 'i32': ret += (x | 0); break;\n" - " case 'f32':\n" - " case 'f64': {\n" - " if (x == 0 && (1 / x) < 0) ret += '-';\n" - " ret += Number(x).toString();\n" - " break;\n" - " }\n" - " // For anything else, just print the type.\n" - " default: ret += type; break;\n" - " }\n" - " return ret;\n" - "}\n" - "var instance = new WebAssembly.Instance(new " - "WebAssembly.Module(binary), {\n" - " 'fuzzing-support': {\n" - " 'log-i32': function(x) { " - "console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') " - "+ ']') },\n" - " 'log-i64': function(x, y) { " - "console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') " - "+ ' ' + literal(y, 'i32') + ']') },\n" // legalization: two i32s - " 'log-f32': function(x) { " - "console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') " - "+ ']') },\n" // legalization: an f64 - " 'log-f64': function(x) { " - "console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') " - "+ ']') },\n" - " },\n" - " 'env': {\n" - " 'setTempRet0': function(x) { tempRet0 = x },\n" - " 'getTempRet0': function() { return tempRet0 },\n" - " },\n" - "});\n"; - for (auto& exp : wasm.exports) { - if (exp->kind != ExternalKind::Function) { - continue; // something exported other than a function - } - auto* func = wasm.getFunction(exp->value); - ret += "try {\n"; - ret += std::string(" console.log('[fuzz-exec] calling ") + - exp->name.toString() + "');\n"; - if (func->getResults() != Type::none) { - ret += std::string(" console.log('[fuzz-exec] note result: ") + - exp->name.toString() + " => ' + literal("; - } else { - ret += " "; - } - ret += std::string("instance.exports.") + exp->name.toString() + "("; - bool first = true; - for (auto param : func->getParams()) { - // zeros in arguments TODO more? - if (first) { - first = false; - } else { - ret += ", "; - } - if (param.isRef()) { - ret += "null"; - } else { - ret += "0"; - if (param == Type::i64) { - ret += ", 0"; - } - } - } - ret += ")"; - if (func->getResults() != Type::none) { - ret += ", '" + func->getResults().toString() + "'))"; - // TODO: getTempRet - } - ret += ";\n"; - ret += "} catch (e) {\n"; - ret += " console.log('exception!' /* + e */);\n"; - ret += "}\n"; - } - return ret; -} - -} // namespace wasm diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp index 0b0ea2cd4..54a83d15a 100644 --- a/src/tools/wasm-opt.cpp +++ b/src/tools/wasm-opt.cpp @@ -23,7 +23,6 @@ #include "execution-results.h" #include "fuzzing.h" -#include "js-wrapper.h" #include "optimization-options.h" #include "pass.h" #include "shell-interface.h" @@ -86,7 +85,6 @@ int main(int argc, const char* argv[]) { bool fuzzPasses = false; bool fuzzMemory = true; bool fuzzOOB = true; - std::string emitJSWrapper; std::string emitSpecWrapper; std::string emitWasm2CWrapper; std::string inputSourceMapFilename; @@ -180,15 +178,6 @@ int main(int argc, const char* argv[]) { WasmOptOption, Options::Arguments::Zero, [&](Options* o, const std::string& arguments) { fuzzOOB = false; }) - .add("--emit-js-wrapper", - "-ejw", - "Emit a JavaScript wrapper file that can run the wasm with some test " - "values, useful for fuzzing", - WasmOptOption, - Options::Arguments::One, - [&](Options* o, const std::string& arguments) { - emitJSWrapper = arguments; - }) .add("--emit-spec-wrapper", "-esw", "Emit a wasm spec interpreter wrapper file that can run the wasm with " @@ -329,24 +318,11 @@ int main(int argc, const char* argv[]) { } } - if (emitJSWrapper.size() > 0) { - // As the code will run in JS, we must legalize it. - PassRunner runner(&wasm); - runner.add("legalize-js-interface"); - runner.run(); - } - ExecutionResults results; if (fuzzExecBefore) { results.get(wasm); } - if (emitJSWrapper.size() > 0) { - std::ofstream outfile; - outfile.open(wasm::Path::to_path(emitJSWrapper), std::ofstream::out); - outfile << generateJSWrapper(wasm); - outfile.close(); - } if (emitSpecWrapper.size() > 0) { std::ofstream outfile; outfile.open(wasm::Path::to_path(emitSpecWrapper), std::ofstream::out); diff --git a/test/lit/exec/no-compare-refs.wast b/test/lit/exec/no-compare-refs.wast index bfa317a53..646ec1550 100644 --- a/test/lit/exec/no-compare-refs.wast +++ b/test/lit/exec/no-compare-refs.wast @@ -12,11 +12,11 @@ ;; The type of the reference this function returns will change as a result of ;; signature pruning. The fuzzer should not complain about this. ;; CHECK: [fuzz-exec] calling return-ref - ;; CHECK-NEXT: [fuzz-exec] note result: return-ref => funcref + ;; CHECK-NEXT: [fuzz-exec] note result: return-ref => function (func $return-ref (export "return-ref") (result funcref) (ref.func $no-use-param) ) ) ;; CHECK: [fuzz-exec] calling return-ref -;; CHECK-NEXT: [fuzz-exec] note result: return-ref => funcref +;; CHECK-NEXT: [fuzz-exec] note result: return-ref => function ;; CHECK-NEXT: [fuzz-exec] comparing return-ref diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test index be5924cf7..5e3a322ed 100644 --- a/test/lit/help/wasm-opt.test +++ b/test/lit/help/wasm-opt.test @@ -53,10 +53,6 @@ ;; CHECK-NEXT: loads/stores/indirect calls when ;; CHECK-NEXT: fuzzing ;; CHECK-NEXT: -;; CHECK-NEXT: --emit-js-wrapper,-ejw Emit a JavaScript wrapper file -;; CHECK-NEXT: that can run the wasm with some -;; CHECK-NEXT: test values, useful for fuzzing -;; CHECK-NEXT: ;; CHECK-NEXT: --emit-spec-wrapper,-esw Emit a wasm spec interpreter ;; CHECK-NEXT: wrapper file that can run the ;; CHECK-NEXT: wasm with some test values, diff --git a/test/lit/unicode-filenames.wast b/test/lit/unicode-filenames.wast index f30ad9c09..a3fbabed0 100644 --- a/test/lit/unicode-filenames.wast +++ b/test/lit/unicode-filenames.wast @@ -1,6 +1,6 @@ ;; RUN: wasm-as %s -o %t-❤.wasm --source-map %t-🗺️.map ;; RUN: cat %t-🗺️.map | filecheck %s --check-prefix SOURCEMAP -;; RUN: wasm-opt %t-❤.wasm -o %t-🤬.wasm --emit-js-wrapper %t-❤.js --input-source-map %t-🗺️.map --output-source-map %t-🗺️.out.map +;; RUN: wasm-opt %t-❤.wasm -o %t-🤬.wasm --emit-spec-wrapper %t-❤.js --input-source-map %t-🗺️.map --output-source-map %t-🗺️.out.map ;; RUN: cat %t-🗺️.out.map | filecheck %s --check-prefix SOURCEMAP ;; RUN: wasm-dis %t-🤬.wasm | filecheck %s --check-prefix MODULE diff --git a/test/passes/emit-js-wrapper=a.js.txt b/test/passes/emit-js-wrapper=a.js.txt deleted file mode 100644 index 4a72da8eb..000000000 --- a/test/passes/emit-js-wrapper=a.js.txt +++ /dev/null @@ -1,84 +0,0 @@ -(module - (type $0 (func (param i32 i32) (result i32))) - (type $1 (func (param i32))) - (type $2 (func (param i32 i64 f32 f64))) - (type $3 (func (param i32 f32 f64))) - (type $4 (func (param i32 f32 f64) (result i64))) - (type $5 (func (param i32 i32 i32 f32 f64))) - (type $6 (func (param i32 f32 f64) (result i32))) - (import "env" "setTempRet0" (func $setTempRet0 (param i32))) - (memory $0 256 256) - (export "add" (func $add)) - (export "no_return" (func $no-return)) - (export "types" (func $legalstub$types)) - (export "types2" (func $types2)) - (export "types3" (func $legalstub$types3)) - (func $add (param $x i32) (param $y i32) (result i32) - (i32.add - (local.get $x) - (local.get $y) - ) - ) - (func $unexported (param $x i32) (param $y i32) (result i32) - (i32.add - (local.get $x) - (local.get $y) - ) - ) - (func $no-return (param $x i32) - (drop - (i32.add - (local.get $x) - (local.get $x) - ) - ) - ) - (func $types (param $x i32) (param $y i64) (param $z f32) (param $w f64) - (nop) - ) - (func $types2 (param $x i32) (param $z f32) (param $w f64) - (nop) - ) - (func $types3 (param $x i32) (param $z f32) (param $w f64) (result i64) - (i64.const 1) - ) - (func $legalstub$types (param $0 i32) (param $1 i32) (param $2 i32) (param $3 f32) (param $4 f64) - (call $types - (local.get $0) - (i64.or - (i64.extend_i32_u - (local.get $1) - ) - (i64.shl - (i64.extend_i32_u - (local.get $2) - ) - (i64.const 32) - ) - ) - (local.get $3) - (local.get $4) - ) - ) - (func $legalstub$types3 (param $0 i32) (param $1 f32) (param $2 f64) (result i32) - (local $3 i64) - (local.set $3 - (call $types3 - (local.get $0) - (local.get $1) - (local.get $2) - ) - ) - (call $setTempRet0 - (i32.wrap_i64 - (i64.shr_u - (local.get $3) - (i64.const 32) - ) - ) - ) - (i32.wrap_i64 - (local.get $3) - ) - ) -) diff --git a/test/passes/emit-js-wrapper=a.js.wast b/test/passes/emit-js-wrapper=a.js.wast deleted file mode 100644 index 8ebd83b0b..000000000 --- a/test/passes/emit-js-wrapper=a.js.wast +++ /dev/null @@ -1,37 +0,0 @@ -(module - (memory $0 256 256) - (export "add" (func $add)) - (export "no_return" (func $no-return)) ;; note exported name is slightly different - (export "types" (func $types)) - (export "types2" (func $types2)) - (export "types3" (func $types3)) - (func $add (param $x i32) (param $y i32) (result i32) - (i32.add - (local.get $x) - (local.get $y) - ) - ) - (func $unexported (param $x i32) (param $y i32) (result i32) - (i32.add - (local.get $x) - (local.get $y) - ) - ) - (func $no-return (param $x i32) - (drop - (i32.add - (local.get $x) - (local.get $x) - ) - ) - ) - (func $types (param $x i32) (param $y i64) (param $z f32) (param $w f64) - (nop) - ) - (func $types2 (param $x i32) (param $z f32) (param $w f64) - (nop) - ) - (func $types3 (param $x i32) (param $z f32) (param $w f64) (result i64) - (i64.const 1) - ) -) diff --git a/test/passes/emit-js-wrapper=a.js.wast.js b/test/passes/emit-js-wrapper=a.js.wast.js deleted file mode 100644 index 916f029f5..000000000 --- a/test/passes/emit-js-wrapper=a.js.wast.js +++ /dev/null @@ -1,79 +0,0 @@ -if (typeof console === 'undefined') { - console = { log: print }; -} -var tempRet0; -var binary; -if (typeof process === 'object' && typeof require === 'function' /* node.js detection */) { - var args = process.argv.slice(2); - binary = require('fs').readFileSync(args[0]); - if (!binary.buffer) binary = new Uint8Array(binary); -} else { - var args; - if (typeof scriptArgs != 'undefined') { - args = scriptArgs; - } else if (typeof arguments != 'undefined') { - args = arguments; - } - if (typeof readbuffer === 'function') { - binary = new Uint8Array(readbuffer(args[0])); - } else { - binary = read(args[0], 'binary'); - } -} -function literal(x, type) { - var ret = ''; - switch (type) { - case 'i32': ret += (x | 0); break; - case 'f32': - case 'f64': { - if (x == 0 && (1 / x) < 0) ret += '-'; - ret += Number(x).toString(); - break; - } - // For anything else, just print the type. - default: ret += type; break; - } - return ret; -} -var instance = new WebAssembly.Instance(new WebAssembly.Module(binary), { - 'fuzzing-support': { - 'log-i32': function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') + ']') }, - 'log-i64': function(x, y) { console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') + ' ' + literal(y, 'i32') + ']') }, - 'log-f32': function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') + ']') }, - 'log-f64': function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') + ']') }, - }, - 'env': { - 'setTempRet0': function(x) { tempRet0 = x }, - 'getTempRet0': function() { return tempRet0 }, - }, -}); -try { - console.log('[fuzz-exec] calling add'); - console.log('[fuzz-exec] note result: add => ' + literal(instance.exports.add(0, 0), 'i32')); -} catch (e) { - console.log('exception!' /* + e */); -} -try { - console.log('[fuzz-exec] calling no_return'); - instance.exports.no_return(0); -} catch (e) { - console.log('exception!' /* + e */); -} -try { - console.log('[fuzz-exec] calling types'); - instance.exports.types(0, 0, 0, 0, 0); -} catch (e) { - console.log('exception!' /* + e */); -} -try { - console.log('[fuzz-exec] calling types2'); - instance.exports.types2(0, 0, 0); -} catch (e) { - console.log('exception!' /* + e */); -} -try { - console.log('[fuzz-exec] calling types3'); - console.log('[fuzz-exec] note result: types3 => ' + literal(instance.exports.types3(0, 0, 0), 'i32')); -} catch (e) { - console.log('exception!' /* + e */); -} diff --git a/test/passes/fuzz-exec_all-features.txt b/test/passes/fuzz-exec_all-features.txt index 22ea5f06a..fcb8e8a3c 100644 --- a/test/passes/fuzz-exec_all-features.txt +++ b/test/passes/fuzz-exec_all-features.txt @@ -212,7 +212,7 @@ [LoggingExternalInterface logging 214] [fuzz-exec] comparing rmw-reads-modifies-and-writes-asymmetrical [fuzz-exec] calling func -[fuzz-exec] note result: func => funcref +[fuzz-exec] note result: func => function (module (type $0 (func (result funcref))) (elem declare func $func) @@ -222,5 +222,5 @@ ) ) [fuzz-exec] calling func -[fuzz-exec] note result: func => funcref +[fuzz-exec] note result: func => function [fuzz-exec] comparing func diff --git a/test/passes/simplify-globals_all-features_fuzz-exec.txt b/test/passes/simplify-globals_all-features_fuzz-exec.txt index fbbcc30c4..cb097d587 100644 --- a/test/passes/simplify-globals_all-features_fuzz-exec.txt +++ b/test/passes/simplify-globals_all-features_fuzz-exec.txt @@ -1,5 +1,5 @@ [fuzz-exec] calling export -[fuzz-exec] note result: export => funcref +[fuzz-exec] note result: export => function (module (type $0 (func (param f32 i31ref i64 f64 funcref))) (type $1 (func (result funcref))) @@ -17,5 +17,5 @@ ) ) [fuzz-exec] calling export -[fuzz-exec] note result: export => funcref +[fuzz-exec] note result: export => function [fuzz-exec] comparing export |