diff --git a/core/runtime.rs b/core/runtime.rs index 23fe730133..af51539a49 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -785,7 +785,7 @@ impl JsRuntime { } } - fn pump_v8_message_loop(&mut self) { + fn pump_v8_message_loop(&mut self) -> Result<(), Error> { let scope = &mut self.handle_scope(); while v8::Platform::pump_message_loop( &v8::V8::get_current_platform(), @@ -795,7 +795,12 @@ impl JsRuntime { // do nothing } - scope.perform_microtask_checkpoint(); + let tc_scope = &mut v8::TryCatch::new(scope); + tc_scope.perform_microtask_checkpoint(); + match tc_scope.exception() { + None => Ok(()), + Some(exception) => exception_to_err_result(tc_scope, exception, false), + } } pub fn poll_value( @@ -877,7 +882,7 @@ impl JsRuntime { state.waker.register(cx.waker()); } - self.pump_v8_message_loop(); + self.pump_v8_message_loop()?; // Ops { @@ -2273,6 +2278,56 @@ pub mod tests { ); } + #[test] + fn terminate_execution_webassembly() { + let (mut isolate, _dispatch_count) = setup(Mode::Async); + let v8_isolate_handle = isolate.v8_isolate().thread_safe_handle(); + + let terminator_thread = std::thread::spawn(move || { + // allow deno to boot and run + std::thread::sleep(std::time::Duration::from_millis(1000)); + + // terminate execution + let ok = v8_isolate_handle.terminate_execution(); + assert!(ok); + }); + + // Run an infinite loop in Webassemby code, which should be terminated. + isolate.execute_script("infinite_wasm_loop.js", + r#" + (async () => { + console.log("Begin"); + const wasmCode = new Uint8Array([ + 0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, + 96, 0, 0, 3, 2, 1, 0, 7, 17, 1, 13, + 105, 110, 102, 105, 110, 105, 116, 101, 95, 108, 111, + 111, 112, 0, 0, 10, 9, 1, 7, 0, 3, 64, + 12, 0, 11, 11, + ]); + const wasmModule = await WebAssembly.compile(wasmCode); + const wasmInstance = new WebAssembly.Instance(wasmModule); + wasmInstance.exports.infinite_loop(); + })(); + "#).expect("wasm infinite loop failed"); + match futures::executor::block_on(isolate.run_event_loop(false)) { + Ok(_) => panic!("execution should be terminated"), + Err(e) => { + assert_eq!(e.to_string(), "Uncaught Error: execution terminated") + } + } + // Cancel the execution-terminating exception in order to allow script + // execution again. + let ok = isolate.v8_isolate().cancel_terminate_execution(); + assert!(ok); + + // Verify that the isolate usable again. + isolate + .execute_script("simple.js", "1 + 1") + .expect("execution should be possible again"); + + terminator_thread.join().unwrap(); + } + #[test] fn terminate_execution() { let (mut isolate, _dispatch_count) = setup(Mode::Async);