summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2024-02-20 15:22:28 -0800
committerGitHub <noreply@github.com>2024-02-20 15:22:28 -0800
commit60b2daea5467e419899e9201a342cc817d6fd68e (patch)
treeb4bcdbc5f1fa99b58f9edc5726bc44f9e2e5949b /src
parent403153868b936250c52d150ef158419a1d67bf55 (diff)
downloadbinaryen-60b2daea5467e419899e9201a342cc817d6fd68e.tar.gz
binaryen-60b2daea5467e419899e9201a342cc817d6fd68e.tar.bz2
binaryen-60b2daea5467e419899e9201a342cc817d6fd68e.zip
Fuzzer: Remove --emit-js-shell logic and reuse fuzz_shell.js instead (#6310)
We had two JS files that could run a wasm file for fuzzing purposes: * --emit-js-shell, which emitted a custom JS file that runs the wasm. * scripts/fuzz_shell.js, which was a generic file that did the same. Both of those load the wasm and then call the exports in order and print out logging as it goes of their return values (if any), exceptions, etc. Then the fuzzer compares that output to running the same wasm in another VM, etc. The difference is that one was custom for the wasm file, and one was generic. Aside from that they are similar and duplicated a bunch of code. This PR improves things by removing 1 and using 2 in all places, that is, we now use the generic file everywhere. I believe we added 1 because we thought a generic file can't do all the things we need, like know the order of exports and the types of return values, but in practice there are ways to do those things: The exports are in fact in the proper order (JS order of iteration is deterministic, thankfully), and for the type we don't want to print type internals anyhow since that would limit fuzzing --closed-world. We do need to be careful with types in JS (see notes in the PR about the type of null) but it's not too bad. As for the types of params, it's fine to pass in null for them all anyhow (null converts to a number or a reference without error).
Diffstat (limited to 'src')
-rw-r--r--src/tools/execution-results.h14
-rw-r--r--src/tools/js-wrapper.h134
-rw-r--r--src/tools/wasm-opt.cpp24
3 files changed, 12 insertions, 160 deletions
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);