Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EH] Fuzz catch+rethrow on the JS side #7287

Merged
merged 79 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
2a07050
fuzz.tag
kripken Feb 6, 2025
53d123f
fix
kripken Feb 6, 2025
93b68ae
test
kripken Feb 6, 2025
acbee3e
work
kripken Feb 6, 2025
10323cf
work
kripken Feb 6, 2025
c9df9c0
fix
kripken Feb 6, 2025
219e885
fix
kripken Feb 6, 2025
f93e130
test
kripken Feb 6, 2025
7be44d8
start
kripken Feb 6, 2025
15d90a4
work
kripken Feb 6, 2025
d3b5402
work
kripken Feb 7, 2025
a333277
clarify the expected behavior
kripken Feb 7, 2025
bf6b02c
Merge remote-tracking branch 'myself/fuzz.imported.tag' into fuzz.cat…
kripken Feb 7, 2025
3215f00
fix
kripken Feb 7, 2025
fe1dc4a
test
kripken Feb 7, 2025
d1e84c8
fix
kripken Feb 7, 2025
982dd16
fix
kripken Feb 7, 2025
8bd72b1
undo
kripken Feb 7, 2025
663194e
DEBUG
kripken Feb 7, 2025
060d3ac
DEBUG2
kripken Feb 7, 2025
a4da2c4
DEBUG3
kripken Feb 7, 2025
fd3ae28
DEBUG4
kripken Feb 7, 2025
24f45bb
UNDO
kripken Feb 7, 2025
2ab135e
Merge remote-tracking branch 'origin/main' into fuzz.imported.tag
kripken Feb 7, 2025
0e1b1ec
fix
kripken Feb 7, 2025
d191f3d
start
kripken Feb 7, 2025
9dfbeab
fix
kripken Feb 7, 2025
470f267
test
kripken Feb 7, 2025
cb053d5
fix
kripken Feb 7, 2025
9718b5f
fixes
kripken Feb 7, 2025
424cd2e
Merge remote-tracking branch 'origin/main' into fuzz.imported.tag2
kripken Feb 7, 2025
d63bfad
fix
kripken Feb 7, 2025
796108d
format
kripken Feb 7, 2025
454fc8f
work
kripken Feb 7, 2025
a551bd3
fix
kripken Feb 7, 2025
77d4422
mid
kripken Feb 7, 2025
921e441
work
kripken Feb 7, 2025
1907e8a
work
kripken Feb 7, 2025
9f4c2b4
work
kripken Feb 7, 2025
282ee57
fix
kripken Feb 7, 2025
260ba72
work
kripken Feb 7, 2025
317204f
format
kripken Feb 7, 2025
de6d3a9
show.bug
kripken Feb 8, 2025
86cb4ad
fix
kripken Feb 8, 2025
834a150
rename
kripken Feb 10, 2025
c06dc07
Merge remote-tracking branch 'myself/fuzz.imported.tag2' into fuzz.ca…
kripken Feb 10, 2025
0362ef8
work around issue with duplicate tag imports in fuzzer
kripken Feb 10, 2025
9084879
Merge remote-tracking branch 'myself/fuzz.imported.tag2' into fuzz.ca…
kripken Feb 10, 2025
5c8a082
fix
kripken Feb 10, 2025
be665df
work
kripken Feb 10, 2025
8dfbc5b
simpl
kripken Feb 10, 2025
ff6b555
Merge remote-tracking branch 'origin/main' into fuzz.catch-ref
kripken Feb 10, 2025
cf41934
fix
kripken Feb 10, 2025
70ae546
Merge remote-tracking branch 'myself/fix.jstag.fuzz' into fuzz.catch-ref
kripken Feb 10, 2025
1926f78
nicer
kripken Feb 10, 2025
4444485
fix
kripken Feb 10, 2025
b97216a
fix
kripken Feb 10, 2025
a4216b0
work
kripken Feb 10, 2025
89e3ae4
fix
kripken Feb 10, 2025
ece9449
fix
kripken Feb 10, 2025
2440f6a
Merge remote-tracking branch 'origin/main' into fuzz.catch-ref
kripken Feb 11, 2025
cbbcb59
typo
kripken Feb 11, 2025
797a6b5
typo
kripken Feb 11, 2025
ad988d7
typo
kripken Feb 11, 2025
4267acd
oops
kripken Feb 11, 2025
1730e96
oops
kripken Feb 11, 2025
60a8747
fix jspi
kripken Feb 11, 2025
c325697
simplify
kripken Feb 11, 2025
5533771
skip.jspi
kripken Feb 11, 2025
fcaa713
Merge remote-tracking branch 'origin/main' into fuzz.catch-ref
kripken Feb 11, 2025
1f680a2
throw objects from JS and execution-results, so both print out as 'ob…
kripken Feb 12, 2025
c83115a
test
kripken Feb 12, 2025
340f456
Merge remote-tracking branch 'origin/main' into fuzz.catch-ref
kripken Feb 12, 2025
fcaf2fd
Update test/lit/exec/fuzzing-api.wast
kripken Feb 13, 2025
bf621f5
more
kripken Feb 13, 2025
0126238
Merge remote-tracking branch 'myself/fuzz.catch-ref' into fuzz.catch-ref
kripken Feb 13, 2025
2015983
comment
kripken Feb 13, 2025
e8312bf
comments
kripken Feb 13, 2025
f9fd6c6
better
kripken Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 34 additions & 10 deletions scripts/fuzz_shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,10 @@ function callFunc(func) {
return func.apply(null, args);
}

// Calls a given function in a try-catch, swallowing JS exceptions, and return 1
// if we did in fact swallow an exception. Wasm traps are not swallowed (see
// details below).
/* async */ function tryCall(func) {
// Calls a given function in a try-catch. Return 1 if an exception was thrown.
// If |rethrow| is set, and an exception is thrown, it is caught and rethrown.
// Wasm traps are not swallowed (see details below).
/* async */ function tryCall(func, rethrow) {
try {
/* await */ func();
return 0;
Expand Down Expand Up @@ -208,7 +208,11 @@ function callFunc(func) {
}
}
// Otherwise, this is a normal exception we want to catch (a wasm
// exception, or a conversion error on the wasm/JS boundary, etc.).
// exception, or a conversion error on the wasm/JS boundary, etc.). Rethrow
// if we were asked to.
if (rethrow) {
throw e;
}
return 1;
}
}
Expand Down Expand Up @@ -271,7 +275,7 @@ var imports = {
'throw': (which) => {
if (!which) {
// Throw a JS exception.
throw 0;
throw new Error('js exception');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this effectively reverting #7286?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially, yes. That PR fixed us to throw similar things from JS and wasm, an i31 of 0 from wasm, and a JS number of 0. But it turns out that wasn't enough as the externref inside the exnref might end up logged out, and we need that logging to be identical. Throwing an object from both wasm and JS leads to both of them logging "object".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the results in this line says not object but js exception.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, the difference can't show up there, because the fuzzer treats exceptions as interchangeable. That is, it converts all text beginning with "exception thrown: " into the same thing, so they all compare the same. (because different VMs have different error messages)

The content of the exception can be read without throwing it, though, if we catch it and return the contents to the outside. Then this difference is noticeable. Here is the test, showing object:

https://github.com/WebAssembly/binaryen/pull/7287/files#diff-a32958975d55fbae4aa24cc650a4d85d1cecaa8f4a8fdb5955372eb77fb7efcaR392

} else {
// Throw a wasm exception.
throw new WebAssembly.Exception(wasmTag, [which]);
Expand All @@ -287,19 +291,39 @@ var imports = {
},

// Export operations.
'call-export': /* async */ (index) => {
/* await */ callFunc(exportList[index].value);
'call-export': /* async */ (index, flags) => {
var rethrow = flags & 1;
if (JSPI) {
// TODO: Figure out why JSPI fails here.
rethrow = 0;
}
if (!rethrow) {
/* await */ callFunc(exportList[index].value);
} else {
tryCall(/* async */ () => /* await */ callFunc(exportList[index].value),
rethrow);
}
Comment on lines +304 to +309
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are these two modes different? What happens we call callFunc and it throws? Doesn't it also get rethrown, because we don't catch anything there?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first arm of the if calls the function normally. If it throws, the exception keeps propagating out normally.

The second arm does the call inside a JS try-catch. If the call throws, we catch and rethrow it from JS.

VMs may implement those paths very different, I am told. This is just aiming to fuzz more things.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Then why do we pass a function directly to callFunc in the first case and callFunc(()->func)) and why do we need async and await here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the second case we want tryCall to do the call inside a try-catch. So we give it code to run, as a () => { .. } function, rather than do the call first and give tryCall the result.

(Another option would be to write the try-catch here, that is, inline tryCall into this function. The only reason tryCall is separate is to avoid code duplication, see all the stuff in the catch body there.)

And, because of how async-await works in JavaScript, every function on the call stack must be declared async and called with await. So that part is just a side effect of our defining a function here. (I am actually not totally sure why all functions need async, but otherwise d8 ends up error "Pending promise rejection" - something to do with exception catching from Promises...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I'm kind of confused why somewhere we print the contents and somewhere we don't, and when it matters and when it doesn't at this point 😵‍💫 But anyway it may not matter much.

Also async and await are all comments... Do you mean d8 errors out if you don't put those in comments?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general we don't print exceptions, because the error text (and stack trace) may vary between VMs. Everything else, including the externref inside an exception - which would be a JS value - we do print, if we extracted it from the exception. I might be forgetting something though.

The comments are documented earlier in the file:

// Whether we are fuzzing JSPI. In addition to this being set, the "async" and
// "await" keywords must be taken out of the /* KEYWORD */ comments (which they
// are normally in, so as not to affect normal fuzzing).
var JSPI;

We need async/await in JSPI mode, but don't want them to change anything in normal mode. So we have them in comments, and un-comment them in JSPI mode.

},
'call-export-catch': /* async */ (index) => {
return tryCall(/* async */ () => /* await */ callFunc(exportList[index].value));
},

// Funcref operations.
'call-ref': /* async */ (ref) => {
'call-ref': /* async */ (ref, flags) => {
// This is a direct function reference, and just like an export, it must
// be wrapped for JSPI.
ref = wrapExportForJSPI(ref);
/* await */ callFunc(ref);
var rethrow = flags & 1;
if (JSPI) {
// TODO: Figure out why JSPI fails here.
rethrow = 0;
}
if (!rethrow) {
/* await */ callFunc(ref);
} else {
tryCall(/* async */ () => /* await */ callFunc(ref),
rethrow);
}
},
'call-ref-catch': /* async */ (ref) => {
ref = wrapExportForJSPI(ref);
Expand Down
19 changes: 12 additions & 7 deletions src/tools/execution-results.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ struct LoggingExternalInterface : public ShellExternalInterface {
return {};
} else if (import->base == "call-export") {
callExportAsJS(arguments[0].geti32());
// The second argument determines if we should catch and rethrow
// exceptions. There is no observable difference in those two modes in
// the binaryen interpreter, so we don't need to do anything.
Comment on lines +135 to +137
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what this means. Does that mean we only rethrow an exception (in case it occurs) in JS, so we don't have any difference in Binaryen? If so, why do we rethrow only in JS?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We rethrow in JS because we hope to find VM bugs. They might create a new JS exception around the wasm one, or something like that, but I don't know the full details - this is a suggestion the VM people gave me.

We could catch and rethrow in Binaryen too I guess, but that would literally be just try-catch and rethrow (we have no stack traces or other info in exceptions).


// Return nothing. If we wanted to return a value we'd need to have
// multiple such functions, one for each signature.
return {};
Expand All @@ -143,9 +147,8 @@ struct LoggingExternalInterface : public ShellExternalInterface {
return {Literal(int32_t(1))};
}
} else if (import->base == "call-ref") {
// Similar to call-export*, but with a ref.
callRefAsJS(arguments[0]);
// Return nothing. If we wanted to return a value we'd need to have
// multiple such functions, one for each signature.
return {};
} else if (import->base == "call-ref-catch") {
try {
Expand Down Expand Up @@ -181,11 +184,13 @@ struct LoggingExternalInterface : public ShellExternalInterface {
}

void throwJSException() {
// JS exceptions contain an externref, which wasm can't read (so the actual
// value here does not matter, but it does need to match what the 'throw'
// import does in fuzz_shell.js, as the fuzzer will do comparisons).
Literal externref = Literal::makeI31(0, Unshared).externalize();
Literals arguments = {externref};
// JS exceptions contain an externref. Use the same type of value as a JS
// exception would have, which is a reference to an object, and which will
// print out "object" in the logging from JS. A trivial struct is enough for
// us to log the same thing here.
auto empty = HeapType(Struct{});
auto inner = Literal(std::make_shared<GCData>(empty, Literals{}), empty);
Literals arguments = {inner.externalize()};
auto payload = std::make_shared<ExnData>(jsTag, arguments);
throwException(WasmException{Literal(payload)});
}
Expand Down
28 changes: 24 additions & 4 deletions src/tools/fuzzing/fuzzing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -851,12 +851,16 @@ void TranslateToFuzzReader::addImportCallingSupport() {

if (choice & 1) {
// Given an export index, call it from JS.
// A second parameter has flags. The first bit determines whether we catch
// and rethrow all exceptions. (This ends up giving us the same signature
// and behavior as the normal mode, so we just add the flags here rather
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's "normal mode"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"normal" is when the bit is not set. I'll clarify the comment.

// than another export.)
callExportImportName = Names::getValidFunctionName(wasm, "call-export");
auto func = std::make_unique<Function>();
func->name = callExportImportName;
func->module = "fuzzing-support";
func->base = "call-export";
func->type = Signature({Type::i32}, Type::none);
func->type = Signature({Type::i32, Type::i32}, Type::none);
wasm.addFunction(std::move(func));
}

Expand Down Expand Up @@ -884,7 +888,10 @@ void TranslateToFuzzReader::addImportCallingSupport() {
func->name = callRefImportName;
func->module = "fuzzing-support";
func->base = "call-ref";
func->type = Signature({Type(HeapType::func, Nullable)}, Type::none);
// As call-export, there is a flags param that allows us to catch+rethrow
// all exceptions.
func->type =
Signature({Type(HeapType::func, Nullable), Type::i32}, Type::none);
wasm.addFunction(std::move(func));
}

Expand Down Expand Up @@ -1135,7 +1142,12 @@ Expression* TranslateToFuzzReader::makeImportCallCode(Type type) {
if ((catching && (!exportTarget || oneIn(2))) || (!catching && oneIn(4))) {
// Most of the time make a non-nullable funcref, to avoid errors.
auto refType = Type(HeapType::func, oneIn(10) ? Nullable : NonNullable);
return builder.makeCall(refTarget, {make(refType)}, type);
std::vector<Expression*> args = {make(refType)};
if (!catching) {
// The first bit matters here, so we can send anything.
args.push_back(make(Type::i32));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this mean? Shouldn't this be either 0 or 1 depending on whether to rthrow?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is future-proof, as random values in the other bits might be used later, and have no downside now. I'll clarify in a comment.

return builder.makeCall(refTarget, args, type);
}
}

Expand Down Expand Up @@ -1163,7 +1175,15 @@ Expression* TranslateToFuzzReader::makeImportCallCode(Type type) {
index = builder.makeBinary(
RemUInt32, index, builder.makeConst(int32_t(maxIndex)));
}
return builder.makeCall(exportTarget, {index}, type);

// The non-catching variants send a flags argument, which says whether to
// catch+rethrow.
std::vector<Expression*> args = {index};
if (!catching) {
// The first bit matters here, so we can send anything.
args.push_back(make(Type::i32));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(also updated this comment)

return builder.makeCall(exportTarget, args, type);
}

Expression* TranslateToFuzzReader::makeImportSleep(Type type) {
Expand Down
2 changes: 1 addition & 1 deletion test/lit/d8/fuzz_shell_exceptions.wast
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
;; RUN: v8 %S/../../../scripts/fuzz_shell.js -- %t.wasm | filecheck %s
;;
;; CHECK: [fuzz-exec] calling throwing-js
;; CHECK: exception thrown: 0
;; CHECK: exception thrown: Error: js exception
;; CHECK: [fuzz-exec] calling throwing-tag
;; CHECK: exception thrown: [object WebAssembly.Exception]

Expand Down
83 changes: 80 additions & 3 deletions test/lit/exec/fuzzing-api.wast
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
(import "fuzzing-support" "table-set" (func $table.set (param i32 funcref)))
(import "fuzzing-support" "table-get" (func $table.get (param i32) (result funcref)))

(import "fuzzing-support" "call-export" (func $call.export (param i32)))
(import "fuzzing-support" "call-export" (func $call.export (param i32 i32)))
(import "fuzzing-support" "call-export-catch" (func $call.export.catch (param i32) (result i32)))

(import "fuzzing-support" "call-ref" (func $call.ref (param funcref)))
(import "fuzzing-support" "call-ref" (func $call.ref (param funcref i32)))
(import "fuzzing-support" "call-ref-catch" (func $call.ref.catch (param funcref) (result i32)))

(import "fuzzing-support" "sleep" (func $sleep (param i32 i32) (result i32)))
Expand Down Expand Up @@ -110,10 +110,32 @@
;; At index 0 in the exports we have $logging, so we will do those loggings.
(call $call.export
(i32.const 0)
;; First bit unset in the flags means a normal call.
(i32.const 0)
)
;; At index 999 we have nothing, so we'll error.
(call $call.export
(i32.const 999)
(i32.const 0)
)
)

;; CHECK: [fuzz-exec] calling export.calling.rethrow
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]
(func $export.calling.rethrow (export "export.calling.rethrow")
;; As above, but the second param is different.
(call $call.export
(i32.const 0)
;; First bit set in the flags means a catch+rethrow. There is no visible
;; effect here, but there might be in JS VMs.
(i32.const 1)
)
;; At index 999 we have nothing, so we'll error.
(call $call.export
(i32.const 999)
(i32.const 1)
)
)

Expand Down Expand Up @@ -146,10 +168,31 @@
;; This will emit some logging.
(call $call.ref
(ref.func $logging)
;; Normal call.
(i32.const 0)
)
;; This will throw.
(call $call.ref
(ref.null func)
(i32.const 0)
)
)

;; CHECK: [fuzz-exec] calling ref.calling.rethrow
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]
(func $ref.calling.rethrow (export "ref.calling.rethrow")
;; As with calling an export, when we set the flags to 0 exceptions are
;; caught and rethrown, but there is no noticeable difference here.
(call $call.ref
(ref.func $logging)
(i32.const 1)
)
;; This will throw.
(call $call.ref
(ref.null func)
(i32.const 1)
)
)

Expand Down Expand Up @@ -195,6 +238,7 @@
;; logging from the function, "12".
(call $call.ref
(ref.func $legal)
(i32.const 1)
)
)

Expand Down Expand Up @@ -335,7 +379,6 @@

;; CHECK: [fuzz-exec] calling do-sleep
;; CHECK-NEXT: [fuzz-exec] note result: do-sleep => 42
;; CHECK-NEXT: warning: no passes specified, not doing any work
(func $do-sleep (export "do-sleep") (result i32)
(call $sleep
;; A ridiculous amount of ms, but in the interpreter it is ignored anyhow.
Expand All @@ -344,6 +387,24 @@
(i32.const 42)
)
)

;; CHECK: [fuzz-exec] calling return-externref-exception
;; CHECK-NEXT: [fuzz-exec] note result: return-externref-exception => object
;; CHECK-NEXT: warning: no passes specified, not doing any work
(func $return-externref-exception (export "return-externref-exception") (result externref)
;; Call JS table.set in a way that throws (on out of bounds). The JS exception
;; is caught and returned from the function, so we can see what it looks like
;; to the fuzzer, which should be "object" (an exception object).
(block $block (result externref)
(try_table (catch $imported-js-tag $block)
(call $table.set
(i32.const 99990)
(ref.null func)
)
)
(unreachable)
)
)
)
;; CHECK: [fuzz-exec] calling logging
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
Expand All @@ -368,6 +429,11 @@
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]

;; CHECK: [fuzz-exec] calling export.calling.rethrow
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]

;; CHECK: [fuzz-exec] calling export.calling.catching
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
Expand All @@ -379,6 +445,11 @@
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]

;; CHECK: [fuzz-exec] calling ref.calling.rethrow
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
;; CHECK-NEXT: [exception thrown: imported-js-tag externref]

;; CHECK: [fuzz-exec] calling ref.calling.catching
;; CHECK-NEXT: [LoggingExternalInterface logging 42]
;; CHECK-NEXT: [LoggingExternalInterface logging 3.14159]
Expand Down Expand Up @@ -413,10 +484,14 @@

;; CHECK: [fuzz-exec] calling do-sleep
;; CHECK-NEXT: [fuzz-exec] note result: do-sleep => 42

;; CHECK: [fuzz-exec] calling return-externref-exception
;; CHECK-NEXT: [fuzz-exec] note result: return-externref-exception => object
;; CHECK-NEXT: [fuzz-exec] comparing catch-js-tag
;; CHECK-NEXT: [fuzz-exec] comparing do-sleep
;; CHECK-NEXT: [fuzz-exec] comparing export.calling
;; CHECK-NEXT: [fuzz-exec] comparing export.calling.catching
;; CHECK-NEXT: [fuzz-exec] comparing export.calling.rethrow
;; CHECK-NEXT: [fuzz-exec] comparing logging
;; CHECK-NEXT: [fuzz-exec] comparing ref.calling
;; CHECK-NEXT: [fuzz-exec] comparing ref.calling.catching
Expand All @@ -426,7 +501,9 @@
;; CHECK-NEXT: [fuzz-exec] comparing ref.calling.illegal-v128
;; CHECK-NEXT: [fuzz-exec] comparing ref.calling.legal
;; CHECK-NEXT: [fuzz-exec] comparing ref.calling.legal-result
;; CHECK-NEXT: [fuzz-exec] comparing ref.calling.rethrow
;; CHECK-NEXT: [fuzz-exec] comparing ref.calling.trap
;; CHECK-NEXT: [fuzz-exec] comparing return-externref-exception
;; CHECK-NEXT: [fuzz-exec] comparing table.getting
;; CHECK-NEXT: [fuzz-exec] comparing table.setting
;; CHECK-NEXT: [fuzz-exec] comparing throwing
Expand Down