summaryrefslogtreecommitdiff
path: root/scripts
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 /scripts
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 'scripts')
-rwxr-xr-xscripts/fuzz_opt.py11
-rw-r--r--scripts/fuzz_shell.js49
2 files changed, 43 insertions, 17 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]);
}
-});
+}