diff --git a/cli/tests/finalization_registry.js b/cli/tests/finalization_registry.js new file mode 100644 index 0000000000..f75979358e --- /dev/null +++ b/cli/tests/finalization_registry.js @@ -0,0 +1,20 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +"use strict"; + +function assertEquals(a, b) { + if (a === b) return; + throw a + " does not equal " + b; +} + +const registry = new FinalizationRegistry((value) => { + assertEquals(value, "called!"); + Deno.core.print("FinalizationRegistry called!\n"); +}); + +(function () { + let x = {}; + registry.register(x, "called!"); + x = null; +})(); + +gc(); diff --git a/cli/tests/finalization_registry.js.out b/cli/tests/finalization_registry.js.out new file mode 100644 index 0000000000..fee61413a3 --- /dev/null +++ b/cli/tests/finalization_registry.js.out @@ -0,0 +1 @@ +FinalizationRegistry called! diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs index 964d2dd7c3..025ee07e12 100644 --- a/cli/tests/integration/run_tests.rs +++ b/cli/tests/integration/run_tests.rs @@ -660,6 +660,12 @@ itest!(heapstats { output: "heapstats.js.out", }); +itest!(finalization_registry { + args: + "run --quiet --unstable --v8-flags=--expose-gc finalization_registry.js", + output: "finalization_registry.js.out", +}); + itest!(https_import { args: "run --quiet --reload --cert tls/RootCA.pem https_import.ts", output: "https_import.ts.out", diff --git a/core/runtime.rs b/core/runtime.rs index e734e0018f..c2dc09f825 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -591,6 +591,17 @@ impl JsRuntime { } } + fn pump_v8_message_loop(&mut self) { + let scope = &mut self.handle_scope(); + while v8::Platform::pump_message_loop( + &v8::V8::get_current_platform(), + scope, + false, // don't block if there are no tasks + ) { + // do nothing + } + } + /// Runs event loop to completion /// /// This future resolves when: @@ -647,6 +658,8 @@ impl JsRuntime { // Top level module self.evaluate_pending_module(); + self.pump_v8_message_loop(); + let state = state_rc.borrow(); let module_map = module_map_rc.borrow(); @@ -1954,6 +1967,56 @@ main(); }) } + #[test] + fn test_pump_message_loop() { + run_in_task(|cx| { + let mut runtime = JsRuntime::new(RuntimeOptions::default()); + runtime + .execute_script( + "pump_message_loop.js", + r#" +function assertEquals(a, b) { + if (a === b) return; + throw a + " does not equal " + b; +} +const sab = new SharedArrayBuffer(16); +const i32a = new Int32Array(sab); +globalThis.resolved = false; + +(function() { + const result = Atomics.waitAsync(i32a, 0, 0); + result.value.then( + (value) => { assertEquals("ok", value); globalThis.resolved = true; }, + () => { assertUnreachable(); + }); +})(); + +const notify_return_value = Atomics.notify(i32a, 0, 1); +assertEquals(1, notify_return_value); +"#, + ) + .unwrap(); + + match runtime.poll_event_loop(cx, false) { + Poll::Ready(Ok(())) => {} + _ => panic!(), + }; + + // noop script, will resolve promise from first script + runtime + .execute_script("pump_message_loop2.js", r#"assertEquals(1, 1);"#) + .unwrap(); + + // check that promise from `Atomics.waitAsync` has been resolved + runtime + .execute_script( + "pump_message_loop3.js", + r#"assertEquals(globalThis.resolved, true);"#, + ) + .unwrap(); + }) + } + #[test] fn test_core_js_stack_frame() { let mut runtime = JsRuntime::new(RuntimeOptions::default());