-
Notifications
You must be signed in to change notification settings - Fork 827
[EH] Make interpreter handle uncaught exceptions #4369
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,7 +87,11 @@ struct LoggingExternalInterface : public ShellExternalInterface { | |
| // we can only get results when there are no imports. we then call each method | ||
| // that has a result, with some values | ||
| struct ExecutionResults { | ||
| std::map<Name, Literals> results; | ||
| struct FunctionResult { | ||
| Literals values; | ||
| bool exception; // Whether an exception is uncaught and the function crashes | ||
| }; | ||
| std::map<Name, FunctionResult> results; | ||
| Loggings loggings; | ||
|
|
||
| // If set, we should ignore this and not compare it to anything. | ||
|
|
@@ -106,26 +110,20 @@ struct ExecutionResults { | |
| } | ||
| std::cout << "[fuzz-exec] calling " << exp->name << "\n"; | ||
| auto* func = wasm.getFunction(exp->value); | ||
| if (func->getResults() != Type::none) { | ||
| // this has a result | ||
| Literals ret = run(func, wasm, instance); | ||
| results[exp->name] = ret; | ||
| // ignore the result if we hit an unreachable and returned no value | ||
| if (ret.size() > 0) { | ||
| std::cout << "[fuzz-exec] note result: " << exp->name << " => "; | ||
| auto resultType = func->getResults(); | ||
| if (resultType.isRef()) { | ||
| // 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'; | ||
| } else { | ||
| std::cout << ret << '\n'; | ||
| } | ||
| FunctionResult ret = run(func, wasm, instance); | ||
| results[exp->name] = ret; | ||
| // ignore the result if we hit an unreachable and returned no value | ||
| if (ret.values.size() > 0) { | ||
| std::cout << "[fuzz-exec] note result: " << exp->name << " => "; | ||
| auto resultType = func->getResults(); | ||
| if (resultType.isRef()) { | ||
| // 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'; | ||
| } else { | ||
| std::cout << ret.values << '\n'; | ||
| } | ||
| } else { | ||
| // no result, run it anyhow (it might modify memory etc.) | ||
| run(func, wasm, instance); | ||
| } | ||
| } | ||
| } catch (const TrapException&) { | ||
|
|
@@ -191,7 +189,10 @@ struct ExecutionResults { | |
| return false; | ||
| } | ||
| std::cout << "[fuzz-exec] comparing " << name << '\n'; | ||
| if (!areEqual(results[name], other.results[name])) { | ||
| if (!areEqual(results[name].values, other.results[name].values)) { | ||
| return false; | ||
| } | ||
| if (results[name].exception != other.results[name].exception) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
@@ -209,7 +210,7 @@ struct ExecutionResults { | |
|
|
||
| bool operator!=(ExecutionResults& other) { return !((*this) == other); } | ||
|
|
||
| Literals run(Function* func, Module& wasm) { | ||
| FunctionResult run(Function* func, Module& wasm) { | ||
| LoggingExternalInterface interface(loggings); | ||
| try { | ||
| ModuleInstance instance(wasm, &interface); | ||
|
|
@@ -220,7 +221,7 @@ struct ExecutionResults { | |
| } | ||
| } | ||
|
|
||
| Literals run(Function* func, Module& wasm, ModuleInstance& instance) { | ||
| FunctionResult run(Function* func, Module& wasm, ModuleInstance& instance) { | ||
| try { | ||
| LiteralList arguments; | ||
| // init hang support, if present | ||
|
|
@@ -236,9 +237,12 @@ struct ExecutionResults { | |
| } | ||
| arguments.push_back(Literal::makeZero(param)); | ||
| } | ||
| return instance.callFunction(func->name, arguments); | ||
| return {instance.callFunction(func->name, arguments), false}; | ||
| } catch (const TrapException&) { | ||
| return {}; | ||
| } catch (const WasmException& e) { | ||
| std::cout << "[exception thrown: " << e << "]" << std::endl; | ||
| return {{}, true}; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Separate from this PR, I like this idea of tracking an exception, and I think maybe we should track traps too. That is, we could have an enum "normal, exception, trap", and then line 242 would mark "trap" like this line would mark "exception".
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mind if I change to that in a followup?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, followup sounds better.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in #4405. |
||
| } catch (const HostLimitException&) { | ||
| // This should be ignored and not compared with, as optimizations can | ||
| // change whether a host limit is reached. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| ;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited. | ||
|
|
||
| ;; RUN: wasm-opt %s -all --fuzz-exec -q -o /dev/null 2>&1 | filecheck %s | ||
|
|
||
| (module | ||
| (tag $e-i32 (param i32)) | ||
|
|
||
| ;; CHECK: [fuzz-exec] calling throw | ||
| ;; CHECK-NEXT: [exception thrown: e-i32 1] | ||
| (func "throw" | ||
| (throw $e-i32 (i32.const 1)) | ||
| ) | ||
|
|
||
| ;; CHECK: [fuzz-exec] calling try-catch | ||
| (func "try-catch" | ||
| (try | ||
| (do | ||
| (throw $e-i32 (i32.const 2)) | ||
| ) | ||
| (catch $e-i32 | ||
| (drop (pop i32)) | ||
| ) | ||
| ) | ||
| ) | ||
|
|
||
| ;; CHECK: [fuzz-exec] calling catchless-try | ||
| ;; CHECK-NEXT: [exception thrown: e-i32 3] | ||
| (func "catchless-try" | ||
| (try | ||
| (do | ||
| (throw $e-i32 (i32.const 3)) | ||
| ) | ||
| ) | ||
| ) | ||
|
|
||
| ;; CHECK: [fuzz-exec] calling try-delegate | ||
| ;; CHECK-NEXT: [exception thrown: e-i32 4] | ||
| (func "try-delegate" | ||
| (try $l0 | ||
| (do | ||
| (try | ||
| (do | ||
| (throw $e-i32 (i32.const 4)) | ||
| ) | ||
| (delegate $l0) | ||
| ) | ||
| ) | ||
| ) | ||
| ) | ||
| ) | ||
| ;; CHECK: [fuzz-exec] calling throw | ||
| ;; CHECK-NEXT: [exception thrown: e-i32 1] | ||
|
|
||
| ;; CHECK: [fuzz-exec] calling try-catch | ||
|
|
||
| ;; CHECK: [fuzz-exec] calling catchless-try | ||
| ;; CHECK-NEXT: [exception thrown: e-i32 3] | ||
|
|
||
| ;; CHECK: [fuzz-exec] calling try-delegate | ||
| ;; CHECK-NEXT: [exception thrown: e-i32 4] | ||
| ;; CHECK-NEXT: [fuzz-exec] comparing catchless-try | ||
| ;; CHECK-NEXT: [fuzz-exec] comparing throw | ||
| ;; CHECK-NEXT: [fuzz-exec] comparing try-catch | ||
| ;; CHECK-NEXT: [fuzz-exec] comparing try-delegate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part is mostly indentation changes due to the removal of
if