diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 9025832bc00..a948f258c65 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -1217,26 +1217,29 @@ EvalCtorOutcome evalCtor(EvallingModuleRunner& instance, if (flow.breakTo == RETURN_CALL_FLOW) { // The return-called function is stored in the last value. - func = wasm.getFunction(flow.values.back().getFunc()); + auto* nextFunc = wasm.getFunction(flow.values.back().getFunc()); flow.values.pop_back(); - params = std::move(flow.values); + auto nextParams = std::move(flow.values); // Serialize the arguments for the new function and save the module // state in case we fail to eval the new function. - localExprs.clear(); - for (auto& param : params) { + std::vector nextLocalExprs; + for (auto& param : nextParams) { auto* serialized = interface.getSerialization(param); if (!serialized) { break; } - localExprs.push_back(serialized); + nextLocalExprs.push_back(serialized); } - if (localExprs.size() < params.size()) { + if (nextLocalExprs.size() < nextParams.size()) { if (!quiet) { std::cout << " ...stopping due to non-serializable param\n"; } break; } + func = nextFunc; + params = std::move(nextParams); + localExprs = std::move(nextLocalExprs); interface.applyToModule(); goto start_eval; } diff --git a/test/lit/ctor-eval/return_call.wast b/test/lit/ctor-eval/return_call.wast index 20dace4dbc9..06cd3619d5f 100644 --- a/test/lit/ctor-eval/return_call.wast +++ b/test/lit/ctor-eval/return_call.wast @@ -484,3 +484,59 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call $import) ;; CHECK-NEXT: ) +(module + ;; Return call with a non-serializable parameter (continuation) stops + ;; evaluating without crashing when partially evaluating the caller. + ;; CHECK: (type $func (func)) + (type $func (func)) + ;; CHECK: (type $cont (cont $func)) + (type $cont (cont $func)) + + ;; CHECK: (type $2 (func (param (ref $cont)))) + + ;; CHECK: (global $g (mut i32) (i32.const 2)) + (global $g (export "g") (mut i32) (i32.const 0)) + + ;; CHECK: (elem declare func $callee) + (elem declare func $callee) + + ;; CHECK: (export "g" (global $g)) + + ;; CHECK: (export "test" (func $test_3)) + + ;; CHECK: (func $callee (type $func) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $callee (type $func) + (nop) + ) + + ;; CHECK: (func $target (type $2) (param $0 (ref $cont)) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $target (param (ref $cont)) + (nop) + ) + + (func $test (export "test") + (global.set $g + (i32.const 1) + ) + (global.set $g + (i32.const 2) + ) + (return_call $target + (cont.new $cont + (ref.func $callee) + ) + ) + ) +) + +;; CHECK: (func $test_3 (type $func) +;; CHECK-NEXT: (return_call $target +;; CHECK-NEXT: (cont.new $cont +;; CHECK-NEXT: (ref.func $callee) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: )