From 43af50c7bab572e7d25f42a4a08b6b6f653f4bc7 Mon Sep 17 00:00:00 2001 From: Cyan Changes Date: Sun, 20 Jul 2025 23:27:13 +0800 Subject: [PATCH 1/3] perf: resolve `Promise`s natively for async ops --- Cargo.lock | 8 +- Cargo.toml | 1 + core/00_infra.js | 312 ------------------ core/01_core.js | 28 +- core/Cargo.toml | 1 + core/lib.rs | 28 +- core/ops.rs | 32 ++ core/ops_builtin.rs | 2 + core/ops_builtin_v8.rs | 55 ++- core/rebuild_async_stubs.js | 81 ----- core/runtime/bindings.rs | 57 +--- core/runtime/jsruntime.rs | 59 ++-- .../op_driver/futures_unordered_driver.rs | 58 +++- core/runtime/op_driver/mod.rs | 68 +++- core/runtime/tests/misc.rs | 2 +- core/runtime/v8_static_strings.rs | 2 +- ops/op2/dispatch_async.rs | 214 ++++++++++-- ops/op2/dispatch_fast.rs | 34 +- ops/op2/dispatch_slow.rs | 4 + ops/op2/signature.rs | 9 +- ops/op2/test_cases/async/async_arg_return.out | 10 +- .../async/async_arg_return_result.out | 15 +- ops/op2/test_cases/async/async_cppgc.out | 37 ++- ops/op2/test_cases/async/async_deferred.out | 31 +- ops/op2/test_cases/async/async_jsbuffer.out | 15 +- ops/op2/test_cases/async/async_lazy.out | 31 +- .../test_cases/async/async_op_metadata.out | 16 +- ops/op2/test_cases/async/async_opstate.out | 13 +- .../async/async_precise_capture.out | 15 +- ops/op2/test_cases/async/async_result.out | 13 +- .../test_cases/async/async_result_impl.out | 16 +- ops/op2/test_cases/async/async_result_smi.out | 19 +- .../test_cases/async/async_stack_trace.out | 7 +- ops/op2/test_cases/async/async_v8_global.out | 9 +- ops/op2/test_cases/async/async_void.out | 8 +- 35 files changed, 584 insertions(+), 726 deletions(-) delete mode 100755 core/rebuild_async_stubs.js diff --git a/Cargo.lock b/Cargo.lock index 48a74e232..c4d815e5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -669,6 +669,7 @@ dependencies = [ "serde", "serde_json", "serde_v8", + "slab", "smallvec", "sourcemap", "static_assertions", @@ -2206,12 +2207,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" diff --git a/Cargo.toml b/Cargo.toml index ca2a0f203..55815c861 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ rstest = "0" serde = { version = "1", features = ["derive"] } serde_bytes = "0.11" serde_json = "1" +slab = "0.4.10" smallvec = "1.14" sourcemap = "9.1.2" static_assertions = "1" diff --git a/core/00_infra.js b/core/00_infra.js index c515e2955..33ce01b18 100644 --- a/core/00_infra.js +++ b/core/00_infra.js @@ -29,15 +29,6 @@ URIError, } = window.__bootstrap.primordials; - let nextPromiseId = 0; - const promiseMap = new SafeMap(); - const RING_SIZE = 4 * 1024; - const NO_PROMISE = null; // Alias to null is faster than plain nulls - const promiseRing = ArrayPrototypeFill(new Array(RING_SIZE), NO_PROMISE); - // TODO(bartlomieju): in the future use `v8::Private` so it's not visible - // to users. Currently missing bindings. - const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId"); - let isLeakTracingEnabled = false; let submitLeakTrace; let eventLoopTick; @@ -129,305 +120,6 @@ errorMap[className] = errorBuilder; } - function setPromise(promiseId) { - const idx = promiseId % RING_SIZE; - // Move old promise from ring to map - const oldPromise = promiseRing[idx]; - if (oldPromise !== NO_PROMISE) { - const oldPromiseId = promiseId - RING_SIZE; - MapPrototypeSet(promiseMap, oldPromiseId, oldPromise); - } - - const promise = new Promise((resolve, reject) => { - promiseRing[idx] = [resolve, reject]; - }); - const wrappedPromise = PromisePrototypeCatch( - promise, - (res) => { - // recreate the stacktrace and strip eventLoopTick() calls from stack trace - ErrorCaptureStackTrace(res, eventLoopTick); - throw res; - }, - ); - wrappedPromise[promiseIdSymbol] = promiseId; - return wrappedPromise; - } - - function __resolvePromise(promiseId, res, isOk) { - // Check if out of ring bounds, fallback to map - const outOfBounds = promiseId < nextPromiseId - RING_SIZE; - if (outOfBounds) { - const promise = MapPrototypeGet(promiseMap, promiseId); - if (!promise) { - throw "Missing promise in map @ " + promiseId; - } - MapPrototypeDelete(promiseMap, promiseId); - if (isOk) { - promise[0](res); - } else { - promise[1](res); - } - } else { - // Otherwise take from ring - const idx = promiseId % RING_SIZE; - const promise = promiseRing[idx]; - if (!promise) { - throw "Missing promise in ring @ " + promiseId; - } - promiseRing[idx] = NO_PROMISE; - if (isOk) { - promise[0](res); - } else { - promise[1](res); - } - } - } - - function hasPromise(promiseId) { - // Check if out of ring bounds, fallback to map - const outOfBounds = promiseId < nextPromiseId - RING_SIZE; - if (outOfBounds) { - return MapPrototypeHas(promiseMap, promiseId); - } - // Otherwise check it in ring - const idx = promiseId % RING_SIZE; - return promiseRing[idx] != NO_PROMISE; - } - - function setUpAsyncStub(opName, originalOp, maybeProto) { - let fn; - - // The body of this switch statement can be generated using the script above. - switch (originalOp.length - 1) { - /* BEGIN TEMPLATE setUpAsyncStub */ - /* DO NOT MODIFY: use rebuild_async_stubs.js to regenerate */ - case 0: - fn = function async_op_0() { - const id = nextPromiseId; - try { - // deno-fmt-ignore - const maybeResult = originalOp.call(this, id); - if (maybeResult !== undefined) { - return PromiseResolve(maybeResult); - } - } catch (err) { - ErrorCaptureStackTrace(err, async_op_0); - return PromiseReject(err); - } - if (isLeakTracingEnabled) { - submitLeakTrace(id); - } - nextPromiseId = (id + 1) & 0xffffffff; - return setPromise(id); - }; - break; - case 1: - fn = function async_op_1(a) { - const id = nextPromiseId; - try { - // deno-fmt-ignore - const maybeResult = originalOp.call(this, id, a); - if (maybeResult !== undefined) { - return PromiseResolve(maybeResult); - } - } catch (err) { - ErrorCaptureStackTrace(err, async_op_1); - return PromiseReject(err); - } - if (isLeakTracingEnabled) { - submitLeakTrace(id); - } - nextPromiseId = (id + 1) & 0xffffffff; - return setPromise(id); - }; - break; - case 2: - fn = function async_op_2(a, b) { - const id = nextPromiseId; - try { - // deno-fmt-ignore - const maybeResult = originalOp.call(this, id, a, b); - if (maybeResult !== undefined) { - return PromiseResolve(maybeResult); - } - } catch (err) { - ErrorCaptureStackTrace(err, async_op_2); - return PromiseReject(err); - } - if (isLeakTracingEnabled) { - submitLeakTrace(id); - } - nextPromiseId = (id + 1) & 0xffffffff; - return setPromise(id); - }; - break; - case 3: - fn = function async_op_3(a, b, c) { - const id = nextPromiseId; - try { - // deno-fmt-ignore - const maybeResult = originalOp.call(this, id, a, b, c); - if (maybeResult !== undefined) { - return PromiseResolve(maybeResult); - } - } catch (err) { - ErrorCaptureStackTrace(err, async_op_3); - return PromiseReject(err); - } - if (isLeakTracingEnabled) { - submitLeakTrace(id); - } - nextPromiseId = (id + 1) & 0xffffffff; - return setPromise(id); - }; - break; - case 4: - fn = function async_op_4(a, b, c, d) { - const id = nextPromiseId; - try { - // deno-fmt-ignore - const maybeResult = originalOp.call(this, id, a, b, c, d); - if (maybeResult !== undefined) { - return PromiseResolve(maybeResult); - } - } catch (err) { - ErrorCaptureStackTrace(err, async_op_4); - return PromiseReject(err); - } - if (isLeakTracingEnabled) { - submitLeakTrace(id); - } - nextPromiseId = (id + 1) & 0xffffffff; - return setPromise(id); - }; - break; - case 5: - fn = function async_op_5(a, b, c, d, e) { - const id = nextPromiseId; - try { - // deno-fmt-ignore - const maybeResult = originalOp.call(this, id, a, b, c, d, e); - if (maybeResult !== undefined) { - return PromiseResolve(maybeResult); - } - } catch (err) { - ErrorCaptureStackTrace(err, async_op_5); - return PromiseReject(err); - } - if (isLeakTracingEnabled) { - submitLeakTrace(id); - } - nextPromiseId = (id + 1) & 0xffffffff; - return setPromise(id); - }; - break; - case 6: - fn = function async_op_6(a, b, c, d, e, f) { - const id = nextPromiseId; - try { - // deno-fmt-ignore - const maybeResult = originalOp.call(this, id, a, b, c, d, e, f); - if (maybeResult !== undefined) { - return PromiseResolve(maybeResult); - } - } catch (err) { - ErrorCaptureStackTrace(err, async_op_6); - return PromiseReject(err); - } - if (isLeakTracingEnabled) { - submitLeakTrace(id); - } - nextPromiseId = (id + 1) & 0xffffffff; - return setPromise(id); - }; - break; - case 7: - fn = function async_op_7(a, b, c, d, e, f, g) { - const id = nextPromiseId; - try { - // deno-fmt-ignore - const maybeResult = originalOp.call(this, id, a, b, c, d, e, f, g); - if (maybeResult !== undefined) { - return PromiseResolve(maybeResult); - } - } catch (err) { - ErrorCaptureStackTrace(err, async_op_7); - return PromiseReject(err); - } - if (isLeakTracingEnabled) { - submitLeakTrace(id); - } - nextPromiseId = (id + 1) & 0xffffffff; - return setPromise(id); - }; - break; - case 8: - fn = function async_op_8(a, b, c, d, e, f, g, h) { - const id = nextPromiseId; - try { - // deno-fmt-ignore - const maybeResult = originalOp.call(this, id, a, b, c, d, e, f, g, h); - if (maybeResult !== undefined) { - return PromiseResolve(maybeResult); - } - } catch (err) { - ErrorCaptureStackTrace(err, async_op_8); - return PromiseReject(err); - } - if (isLeakTracingEnabled) { - submitLeakTrace(id); - } - nextPromiseId = (id + 1) & 0xffffffff; - return setPromise(id); - }; - break; - case 9: - fn = function async_op_9(a, b, c, d, e, f, g, h, i) { - const id = nextPromiseId; - try { - // deno-fmt-ignore - const maybeResult = originalOp.call(this, id, a, b, c, d, e, f, g, h, i); - if (maybeResult !== undefined) { - return PromiseResolve(maybeResult); - } - } catch (err) { - ErrorCaptureStackTrace(err, async_op_9); - return PromiseReject(err); - } - if (isLeakTracingEnabled) { - submitLeakTrace(id); - } - nextPromiseId = (id + 1) & 0xffffffff; - return setPromise(id); - }; - break; - /* END TEMPLATE */ - - default: - throw new Error( - `Too many arguments for async op codegen (length of ${opName} was ${ - originalOp.length - 1 - })`, - ); - } - ObjectDefineProperty(fn, "name", { - value: opName, - configurable: false, - writable: false, - }); - - if (maybeProto) { - ObjectDefineProperty(fn, "prototype", { - value: maybeProto.prototype, - configurable: false, - writable: false, - }); - maybeProto.prototype[opName] = fn; - } - - return fn; - } - // Extra Deno.core.* exports const core = ObjectAssign(globalThis.Deno.core, { build, @@ -435,13 +127,9 @@ registerErrorBuilder, buildCustomError, registerErrorClass, - setUpAsyncStub, - hasPromise, - promiseIdSymbol, }); const infra = { - __resolvePromise, __setLeakTracingEnabled, __isLeakTracingEnabled, __initializeCoreMethods, diff --git a/core/01_core.js b/core/01_core.js index 913237467..82bef0293 100755 --- a/core/01_core.js +++ b/core/01_core.js @@ -36,7 +36,6 @@ __setLeakTracingEnabled, __isLeakTracingEnabled, __initializeCoreMethods, - __resolvePromise, } = window.__infra; const { op_abort_wasm_streaming, @@ -60,6 +59,7 @@ op_print, op_queue_microtask, op_ref_op, + op_ref_op_promise, op_resources, op_run_microtasks, op_serialize, @@ -76,6 +76,7 @@ op_timer_ref, op_timer_unref, op_unref_op, + op_unref_op_promise, op_cancel_handle, op_leak_tracing_enable, op_leak_tracing_submit, @@ -160,17 +161,9 @@ // if there's a "next tick" scheduled by the Node.js compat layer. Arguments // before last are alternating integers and any values that describe the // responses of async ops. - function eventLoopTick() { - // First respond to all pending ops. - for (let i = 0; i < arguments.length - 3; i += 3) { - const promiseId = arguments[i]; - const isOk = arguments[i + 1]; - const res = arguments[i + 2]; - - __resolvePromise(promiseId, res, isOk); - } + function eventLoopTick(rejections, timers, hasTickScheduled) { // Drain nextTick queue if there's a tick scheduled. - if (arguments[arguments.length - 1]) { + if (hasTickScheduled) { for (let i = 0; i < nextTickCallbacks.length; i++) { nextTickCallbacks[i](); } @@ -199,8 +192,8 @@ } } - const timers = arguments[arguments.length - 2]; if (timers) { + op_print(`${timers.join("%")}`) timersRunning = true; for (let i = 0; i < timers.length; i += 3) { timerDepth = timers[i]; @@ -222,7 +215,6 @@ } // If we have any rejections for this tick, attempt to process them - const rejections = arguments[arguments.length - 3]; if (rejections) { for (let i = 0; i < rejections.length; i += 2) { const handled = unhandledPromiseRejectionHandler( @@ -238,25 +230,19 @@ } function refOp(promiseId) { - if (!hasPromise(promiseId)) { - return; - } op_ref_op(promiseId); } function unrefOp(promiseId) { - if (!hasPromise(promiseId)) { - return; - } op_unref_op(promiseId); } function refOpPromise(promise) { - refOp(promise[promiseIdSymbol]); + op_ref_op_promise(promise); } function unrefOpPromise(promise) { - unrefOp(promise[promiseIdSymbol]); + op_unref_op_promise(promise); } function resources() { diff --git a/core/Cargo.toml b/core/Cargo.toml index 35b298ad8..b50d5b5c9 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -46,6 +46,7 @@ pin-project.workspace = true serde.workspace = true serde_json = { workspace = true, features = ["float_roundtrip", "preserve_order"] } serde_v8.workspace = true +slab.workspace = true smallvec.workspace = true sourcemap.workspace = true static_assertions.workspace = true diff --git a/core/lib.rs b/core/lib.rs index 7e75e9201..a8f57a317 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -130,6 +130,7 @@ pub use crate::ops::OpMetadata; pub use crate::ops::OpStackTraceCallback; pub use crate::ops::OpState; pub use crate::ops::PromiseId; +pub use crate::ops::PromiseResolver; pub use crate::ops_builtin::op_close; pub use crate::ops_builtin::op_print; pub use crate::ops_builtin::op_resources; @@ -242,31 +243,4 @@ mod tests { }; assert_eq!(&name[..expected.len()], expected); } - - // If the deno command is available, we ensure the async stubs are correctly rebuilt. - #[test] - fn test_rebuild_async_stubs() { - // Check for deno first - if let Err(e) = Command::new("deno") - .arg("--version") - .stderr(Stdio::null()) - .stdout(Stdio::null()) - .status() - { - #[allow(clippy::print_stderr)] - { - eprintln!("Ignoring test because we couldn't find deno: {e:?}"); - } - } - let status = Command::new("deno") - .args(["run", "-A", "rebuild_async_stubs.js", "--check"]) - .stderr(Stdio::null()) - .stdout(Stdio::null()) - .status() - .unwrap(); - assert!( - status.success(), - "Async stubs were not updated, or 'rebuild_async_stubs.js' failed for some other reason" - ); - } } diff --git a/core/ops.rs b/core/ops.rs index e0faa3c10..295f61218 100644 --- a/core/ops.rs +++ b/core/ops.rs @@ -9,6 +9,7 @@ use crate::ops_metrics::OpMetricsFn; use crate::runtime::JsRuntimeState; use crate::runtime::OpDriverImpl; use crate::runtime::UnrefedOps; +use crate::runtime::op_driver::OpDriver; use futures::task::AtomicWaker; use std::cell::RefCell; use std::collections::HashSet; @@ -22,6 +23,7 @@ use v8::Isolate; use v8::fast_api::CFunction; pub type PromiseId = i32; +pub type PromiseResolver = v8::Global; pub type OpId = u16; #[cfg(debug_assertions)] @@ -198,6 +200,36 @@ impl OpCtx { &self.op_driver } + pub fn create_promise(&self, scope: &mut v8::HandleScope) -> PromiseId { + self.op_driver.create_promise(scope) + } + + pub fn get_promise<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + promise_id: PromiseId, + ) -> Option> { + self.op_driver.get_promise(scope, promise_id) + } + + pub fn resolve_promise( + &self, + scope: &mut v8::HandleScope, + promise_id: PromiseId, + value: v8::Local, + ) { + self.op_driver.resolve_promise(scope, promise_id, value) + } + + pub fn reject_promise( + &self, + scope: &mut v8::HandleScope, + promise_id: PromiseId, + reason: v8::Local, + ) { + self.op_driver.reject_promise(scope, promise_id, reason) + } + /// Get the [`JsRuntimeState`] for this op. pub(crate) fn runtime_state(&self) -> &JsRuntimeState { // SAFETY: JsRuntimeState outlives OpCtx diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs index b981ad8f1..1318fe7b9 100644 --- a/core/ops_builtin.rs +++ b/core/ops_builtin.rs @@ -104,7 +104,9 @@ builtin_ops! { ops_builtin_v8::op_timer_ref, ops_builtin_v8::op_timer_unref, ops_builtin_v8::op_ref_op, + ops_builtin_v8::op_ref_op_promise, ops_builtin_v8::op_unref_op, + ops_builtin_v8::op_unref_op_promise, ops_builtin_v8::op_lazy_load_esm, ops_builtin_v8::op_run_microtasks, ops_builtin_v8::op_has_tick_scheduled, diff --git a/core/ops_builtin_v8.rs b/core/ops_builtin_v8.rs index a5bb703e6..f3edb2b46 100644 --- a/core/ops_builtin_v8.rs +++ b/core/ops_builtin_v8.rs @@ -13,6 +13,7 @@ use crate::ops_builtin::WasmStreamingResource; use crate::resolve_url; use crate::runtime::JsRealm; use crate::runtime::JsRuntimeState; +use crate::runtime::op_driver::OpDriver; use crate::source_map::SourceMapApplication; use crate::stats::RuntimeActivityType; use deno_error::JsErrorBox; @@ -44,18 +45,64 @@ pub fn op_set_handled_promise_rejection_handler( *exception_state.js_handled_promise_rejection_cb.borrow_mut() = f; } +#[op2(fast)] +pub fn op_ref_op_promise( + scope: &mut v8::HandleScope, + promise: v8::Local, +) { + let context_state = JsRealm::state_from_scope(scope); + let promise_id = match context_state + .pending_ops + .promise_id_from_promise(scope, promise) + { + Some(promise_id) => promise_id, + None => return, + }; + context_state.unrefed_ops.borrow_mut().remove(&promise_id); +} + +#[op2(fast)] +pub fn op_unref_op_promise( + scope: &mut v8::HandleScope, + promise: v8::Local, +) { + let context_state = JsRealm::state_from_scope(scope); + let promise_id = match context_state + .pending_ops + .promise_id_from_promise(scope, promise) + { + Some(promise_id) => promise_id, + None => return, + }; + context_state.unrefed_ops.borrow_mut().insert(promise_id); +} + #[op2(fast)] pub fn op_ref_op(scope: &mut v8::HandleScope, promise_id: i32) { - let context_state = JsRealm::state_from_scope(scope); - context_state.unrefed_ops.borrow_mut().remove(&promise_id); + let context_state = JsRealm::state_from_scope(scope); + if context_state.pending_ops.has_promise(promise_id) { + context_state.unrefed_ops.borrow_mut().remove(&promise_id); + } } #[op2(fast)] pub fn op_unref_op(scope: &mut v8::HandleScope, promise_id: i32) { - let context_state = JsRealm::state_from_scope(scope); - context_state.unrefed_ops.borrow_mut().insert(promise_id); + let context_state = JsRealm::state_from_scope(scope); + if context_state.pending_ops.has_promise(promise_id) { + context_state.unrefed_ops.borrow_mut().insert(promise_id); + } } +#[op2] +pub fn op_promise_promise_id( + scope: &mut v8::HandleScope, + promise: v8::Local, +) -> Option { + let context_state = JsRealm::state_from_scope(scope); + context_state + .pending_ops + .promise_id_from_promise(scope, promise) +} #[op2(fast)] pub fn op_leak_tracing_enable(scope: &mut v8::HandleScope, enabled: bool) { let context_state = JsRealm::state_from_scope(scope); diff --git a/core/rebuild_async_stubs.js b/core/rebuild_async_stubs.js deleted file mode 100755 index e92d466e4..000000000 --- a/core/rebuild_async_stubs.js +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env deno run --allow-read --allow-write -// Copyright 2018-2025 the Deno authors. MIT license. - -const doNotModify = - "/* DO NOT MODIFY: use rebuild_async_stubs.js to regenerate */\n"; - -// The template function we build op_async_N functions from -function __TEMPLATE__(__ARGS_PARAM__) { - const id = nextPromiseId; - try { - // deno-fmt-ignore - const maybeResult = __OP__.call(this, __ARGS__); - if (maybeResult !== undefined) { - return PromiseResolve(maybeResult); - } - } catch (err) { - __ERR__; - ErrorCaptureStackTrace(err, __TEMPLATE__); - return PromiseReject(err); - } - if (isLeakTracingEnabled) { - submitLeakTrace(id); - } - nextPromiseId = (id + 1) & 0xffffffff; - return setPromise(id); -} - -const infraJsPath = new URL("00_infra.js", import.meta.url); -const infraJs = Deno.readTextFileSync(infraJsPath); - -const infraPristine = infraJs.replaceAll( - /\/\* BEGIN TEMPLATE ([^ ]+) \*\/.*?\/\* END TEMPLATE \*\//smg, - "TEMPLATE-$1", -); -const templateString = __TEMPLATE__.toString(); -let asyncStubCases = "/* BEGIN TEMPLATE setUpAsyncStub */\n"; -asyncStubCases += doNotModify; -const vars = "abcdefghijklm"; -for (let i = 0; i < 10; i++) { - let args = "id"; - for (let j = 0; j < i; j++) { - args += `, ${vars[j]}`; - } - const name = `async_op_${i}`; - // Replace the name and args, and add a two-space indent - const func = `fn = ${templateString}` - .replaceAll(/__TEMPLATE__/g, name) - .replaceAll(/__ARGS__/g, args) - .replaceAll(/__ARGS_PARAM__/g, args.replace(/id(, )?/, "")) - .replaceAll(/__OP__/g, "originalOp") - .replaceAll(/[\s]*__ERR__;/g, "") - .replaceAll(/^/gm, " "); - asyncStubCases += ` -case ${i}: -${func}; - break; - `.trim() + "\n"; -} -asyncStubCases += "/* END TEMPLATE */"; - -const asyncStubIndent = - infraPristine.match(/^([\t ]+)(?=TEMPLATE-setUpAsyncStub)/m)[0]; - -const infraOutput = infraPristine - .replace( - /[\t ]+TEMPLATE-setUpAsyncStub/, - asyncStubCases.replaceAll(/^/gm, asyncStubIndent), - ); - -if (Deno.args[0] === "--check") { - if (infraOutput !== infraJs) { - Deno.writeTextFileSync("/tmp/mismatch.txt", infraOutput); - throw new Error( - "Mismatch between pristine and updated source (wrote mismatch to /tmp/mismatch.txt)", - ); - } else { - console.log("✅ Templated sections would not change"); - } -} else { - Deno.writeTextFileSync(infraJsPath, infraOutput); -} diff --git a/core/runtime/bindings.rs b/core/runtime/bindings.rs index 25db9edc5..60e3e4328 100644 --- a/core/runtime/bindings.rs +++ b/core/runtime/bindings.rs @@ -366,14 +366,6 @@ pub(crate) fn initialize_deno_core_ops_bindings<'s>( let deno_core_ops_obj: v8::Local = get(scope, deno_core_obj, OPS, "Deno.core.ops"); - let set_up_async_stub_fn: v8::Local = get( - scope, - deno_core_obj, - SET_UP_ASYNC_STUB, - "Deno.core.setUpAsyncStub", - ); - - let undefined = v8::undefined(scope); let mut index = 0; for decl in op_method_decls { @@ -407,14 +399,7 @@ pub(crate) fn initialize_deno_core_ops_bindings<'s>( continue; } - op_ctx_template_or_accessor( - &accessor_store, - set_up_async_stub_fn, - scope, - prototype, - tmpl, - method, - ); + op_ctx_template_or_accessor(&accessor_store, scope, prototype, method); } index += decl.methods.len(); @@ -433,14 +418,7 @@ pub(crate) fn initialize_deno_core_ops_bindings<'s>( // Register async methods at the end since we need to create the template instance. for method in method_ctxs.iter() { if method.decl.is_async { - op_ctx_template_or_accessor( - &accessor_store, - set_up_async_stub_fn, - scope, - prototype, - tmpl, - method, - ); + op_ctx_template_or_accessor(&accessor_store, scope, prototype, method); } } @@ -459,20 +437,9 @@ pub(crate) fn initialize_deno_core_ops_bindings<'s>( let op_ctxs = &op_ctxs[index..]; for op_ctx in op_ctxs { - let mut op_fn = - op_ctx_function(scope, op_ctx, v8::ConstructorBehavior::Allow); + let op_fn = op_ctx_function(scope, op_ctx, v8::ConstructorBehavior::Allow); let key = op_ctx.decl.name_fast.v8_string(scope).unwrap(); - // For async ops we need to set them up, by calling `Deno.core.setUpAsyncStub` - - // this call will generate an optimized function that binds to the provided - // op, while keeping track of promises and error remapping. - if op_ctx.decl.is_async { - let result = set_up_async_stub_fn - .call(scope, undefined.into(), &[key.into(), op_fn.into()]) - .unwrap(); - op_fn = result.try_into().unwrap() - } - deno_core_ops_obj.set(scope, key.into(), op_fn.into()); index += 1; @@ -481,31 +448,13 @@ pub(crate) fn initialize_deno_core_ops_bindings<'s>( fn op_ctx_template_or_accessor<'s>( accessor_store: &AccessorStore, - set_up_async_stub_fn: v8::Local, scope: &mut v8::HandleScope<'s>, tmpl: v8::Local<'s, v8::ObjectTemplate>, - constructor: v8::Local<'s, v8::FunctionTemplate>, op_ctx: &OpCtx, ) { if !op_ctx.decl.is_accessor() { let op_fn = op_ctx_template(scope, op_ctx, v8::ConstructorBehavior::Throw); let method_key = name_key(scope, &op_ctx.decl); - if op_ctx.decl.is_async { - let undefined = v8::undefined(scope); - let op_fn = op_fn.get_function(scope).unwrap(); - - let tmpl_fn = constructor.get_function(scope).unwrap(); - - let _result = set_up_async_stub_fn - .call( - scope, - undefined.into(), - &[method_key.into(), op_fn.into(), tmpl_fn.into()], - ) - .unwrap(); - - return; - } tmpl.set(method_key, op_fn.into()); diff --git a/core/runtime/jsruntime.rs b/core/runtime/jsruntime.rs index f2f049aaf..fb7720036 100644 --- a/core/runtime/jsruntime.rs +++ b/core/runtime/jsruntime.rs @@ -2616,27 +2616,7 @@ impl JsRuntime { } } - // We return async responses to JS in bounded batches. Note that because - // we're passing these to JS as arguments, it is possible to overflow the - // JS stack by just passing too many. - const MAX_VEC_SIZE_FOR_OPS: usize = 1024; - - // each batch is a flat vector of tuples: - // `[promise_id1, op_result1, promise_id2, op_result2, ...]` - // promise_id is a simple integer, op_result is an ops::OpResult - // which contains a value OR an error, encoded as a tuple. - // This batch is received in JS via the special `arguments` variable - // and then each tuple is used to resolve or reject promises - let mut args: SmallVec<[v8::Local; 32]> = - SmallVec::with_capacity(32); - loop { - if args.len() >= MAX_VEC_SIZE_FOR_OPS { - // We have too many, bail for now but re-wake the waker - cx.waker().wake_by_ref(); - break; - } - let Poll::Ready((promise_id, op_id, res)) = context_state.pending_ops.poll_ready(cx) else { @@ -2645,25 +2625,28 @@ impl JsRuntime { let res = res.unwrap(scope); - { - let op_ctx = &context_state.op_ctxs[op_id as usize]; - if op_ctx.metrics_enabled() { - if res.is_ok() { - dispatch_metrics_async(op_ctx, OpMetricsEvent::CompletedAsync); - } else { - dispatch_metrics_async(op_ctx, OpMetricsEvent::ErrorAsync); + let op_driver = { + let op_ctx = &context_state.op_ctxs[op_id as usize]; + if op_ctx.metrics_enabled() { + if res.is_ok() { + dispatch_metrics_async(op_ctx, OpMetricsEvent::CompletedAsync); + } else { + dispatch_metrics_async(op_ctx, OpMetricsEvent::ErrorAsync); + } } - } - } + op_ctx.op_driver() + }; context_state.unrefed_ops.borrow_mut().remove(&promise_id); context_state - .activity_traces - .complete(RuntimeActivityType::AsyncOp, promise_id as _); + .activity_traces + .complete(RuntimeActivityType::AsyncOp, promise_id as _); + match res { + Ok(value) => op_driver.resolve_promise(scope, promise_id, value), + Err(reason) => op_driver.reject_promise(scope, promise_id, reason), + } + dispatched_ops |= true; - args.push(v8::Integer::new(scope, promise_id).into()); - args.push(v8::Boolean::new(scope, res.is_ok()).into()); - args.push(res.unwrap_or_else(std::convert::identity)); } let undefined: v8::Local = v8::undefined(scope).into(); @@ -2717,8 +2700,6 @@ impl JsRuntime { undefined }; - args.push(rejections); - // TODO(mmastrac): timer dispatch should be done via direct function call, but we will have to start // storing the exception-reporting callback. let timers = match context_state.timers.poll_timers(cx) { @@ -2745,17 +2726,15 @@ impl JsRuntime { } _ => undefined, }; - args.push(timers); - let has_tick_scheduled = v8::Boolean::new(scope, has_tick_scheduled); - args.push(has_tick_scheduled.into()); + let has_tick_scheduled = v8::Boolean::new(scope, has_tick_scheduled).into(); let tc_scope = &mut v8::TryCatch::new(scope); let js_event_loop_tick_cb = context_state.js_event_loop_tick_cb.borrow(); let js_event_loop_tick_cb = js_event_loop_tick_cb.as_ref().unwrap().open(tc_scope); - js_event_loop_tick_cb.call(tc_scope, undefined, args.as_slice()); + js_event_loop_tick_cb.call(tc_scope, undefined, &[rejections, timers, has_tick_scheduled]); if let Some(exception) = tc_scope.exception() { return exception_to_err_result(tc_scope, exception, false, true); diff --git a/core/runtime/op_driver/futures_unordered_driver.rs b/core/runtime/op_driver/futures_unordered_driver.rs index 4c4a26d7d..cb7da00dd 100644 --- a/core/runtime/op_driver/futures_unordered_driver.rs +++ b/core/runtime/op_driver/futures_unordered_driver.rs @@ -5,8 +5,8 @@ use super::OpInflightStats; use super::future_arena::FutureAllocation; use super::future_arena::FutureArena; use super::op_results::*; -use crate::OpId; use crate::PromiseId; +use crate::{OpId, PromiseResolver}; use bit_set::BitSet; use deno_error::JsErrorClass; use deno_unsync::JoinHandle; @@ -63,6 +63,7 @@ pub struct FuturesUnorderedDriver< completed_ops: Rc>>>, completed_waker: Rc, arena: FutureArena, PendingOpInfo>, + promises: RefCell>, } impl Drop for FuturesUnorderedDriver { @@ -91,6 +92,7 @@ impl Default for FuturesUnorderedDriver { queue, completed_waker, arena: Default::default(), + promises: Default::default(), } } } @@ -123,6 +125,56 @@ impl FuturesUnorderedDriver { } impl OpDriver for FuturesUnorderedDriver { + fn create_promise<'s>(&self, scope: &mut v8::HandleScope) -> PromiseId { + let promise_resolver = v8::PromiseResolver::new(scope).unwrap(); + let promise_resolver: PromiseResolver = + v8::Global::new(scope, promise_resolver); + let promise_id = self.promises.borrow_mut().insert(promise_resolver); + promise_id.try_into().expect("promise id overflow") + } + + fn has_promise(&self, promise_id: PromiseId) -> bool { + self.promises.borrow().get(promise_id as usize).is_some() + } + + fn _get_promise<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + promise_id: PromiseId, + ) -> Option> { + self + .promises + .borrow() + .get(promise_id as usize) + .map(move |x| x.open(scope).get_promise(scope)) + } + + fn resolve_promise( + &self, + scope: &mut v8::HandleScope<'_>, + promise_id: PromiseId, + value: v8::Local, + ) { + let maybe_resolver = self.promises.borrow_mut().try_remove(promise_id as usize); + if let Some(resolver) = maybe_resolver + { + resolver.open(scope).resolve(scope, value); + } + } + + fn reject_promise( + &self, + scope: &mut v8::HandleScope<'_>, + promise_id: PromiseId, + reason: v8::Local, + ) { + let maybe_resolver = self.promises.borrow_mut().try_remove(promise_id as usize); + if let Some(resolver) = maybe_resolver + { + resolver.open(scope).reject(scope, reason); + } + } + fn submit_op_fallible< R: 'static, E: JsErrorClass + 'static, @@ -131,7 +183,7 @@ impl OpDriver for FuturesUnorderedDriver { >( &self, op_id: OpId, - promise_id: i32, + promise_id: PromiseId, op: impl Future> + 'static, rv_map: C::MappingFn, ) -> Option> { @@ -172,7 +224,7 @@ impl OpDriver for FuturesUnorderedDriver { >( &self, op_id: OpId, - promise_id: i32, + promise_id: PromiseId, op: impl Future + 'static, rv_map: C::MappingFn, ) -> Option { diff --git a/core/runtime/op_driver/mod.rs b/core/runtime/op_driver/mod.rs index f8d1c20d2..4a9eaa8e0 100644 --- a/core/runtime/op_driver/mod.rs +++ b/core/runtime/op_driver/mod.rs @@ -21,6 +21,7 @@ pub use self::op_results::OpResult; use self::op_results::PendingOpInfo; pub use self::op_results::V8OpMappingContext; pub use self::op_results::V8RetValMapper; +use crate::runtime::v8_static_strings::INTERNAL_PROMISE_ID; #[derive(Default)] /// Returns a set of stats on inflight ops. @@ -50,11 +51,70 @@ pub enum OpScheduling { pub(crate) trait OpDriver: Default { + fn get_private_promise_id_symbol<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + ) -> v8::Local<'s, v8::Private> { + let name = INTERNAL_PROMISE_ID.v8_string(scope).unwrap(); + v8::Private::for_api(scope, Some(name)) + } + + fn _get_promise<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + promise_id: PromiseId, + ) -> Option>; + + fn has_promise(&self, promise_id: PromiseId) -> bool; + + fn promise_id_from_promise( + &self, + scope: &mut v8::HandleScope<'_>, + promise: v8::Local, + ) -> Option { + let symbol = self.get_private_promise_id_symbol(scope); + let value = promise.get_private(scope, symbol); + value + .and_then(|x| TryInto::>::try_into(x).ok()) + .map(|x| x.int32_value(scope).unwrap()) + } + + /// Get the promise with the `promise_id`, set a private promiseIdSymbol to the promise id + fn get_promise<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + promise_id: PromiseId, + ) -> Option> { + let maybe_promise = self._get_promise(scope, promise_id); + maybe_promise.map(|promise| { + let id = v8::Integer::new(scope, promise_id); + let symbol = self.get_private_promise_id_symbol(scope); + promise.set_private(scope, symbol, id.into()); + promise + }) + } + + fn resolve_promise( + &self, + scope: &mut v8::HandleScope, + promise_id: PromiseId, + value: v8::Local, + ); + + fn reject_promise( + &self, + scope: &mut v8::HandleScope, + promise_id: PromiseId, + reason: v8::Local, + ); + + fn create_promise(&self, scope: &mut v8::HandleScope) -> PromiseId; + /// Submits an operation that is expected to complete successfully without errors. fn submit_op_infallible( &self, op_id: OpId, - promise_id: i32, + promise_id: PromiseId, op: impl Future + 'static, rv_map: C::MappingFn, ) -> Option; @@ -65,7 +125,7 @@ pub(crate) trait OpDriver: &self, scheduling: OpScheduling, op_id: OpId, - promise_id: i32, + promise_id: PromiseId, op: impl Future + 'static, rv_map: C::MappingFn, ) -> Option { @@ -91,7 +151,7 @@ pub(crate) trait OpDriver: >( &self, op_id: OpId, - promise_id: i32, + promise_id: PromiseId, op: impl Future> + 'static, rv_map: C::MappingFn, ) -> Option>; @@ -103,7 +163,7 @@ pub(crate) trait OpDriver: &self, scheduling: OpScheduling, op_id: OpId, - promise_id: i32, + promise_id: PromiseId, op: impl Future> + 'static, rv_map: C::MappingFn, ) -> Option> { diff --git a/core/runtime/tests/misc.rs b/core/runtime/tests/misc.rs index b98206d40..2af0e561a 100644 --- a/core/runtime/tests/misc.rs +++ b/core/runtime/tests/misc.rs @@ -1084,7 +1084,7 @@ async fn test_dynamic_import_module_error_stack() { }; assert_eq!( js_error.to_string(), - "TypeError: foo + "TypeError: fo1 at async file:///import.js:1:43" ); } diff --git a/core/runtime/v8_static_strings.rs b/core/runtime/v8_static_strings.rs index a826acc1b..ec3ac67ae 100644 --- a/core/runtime/v8_static_strings.rs +++ b/core/runtime/v8_static_strings.rs @@ -33,7 +33,7 @@ v8_static_strings!( NAME = "name", OPS = "ops", RESOLVE = "resolve", - SET_UP_ASYNC_STUB = "setUpAsyncStub", + INTERNAL_PROMISE_ID = "Promise#Deno.core.internalPromiseId", STACK = "stack", URL = "url", WASM_INSTANCE = "WasmInstance", diff --git a/ops/op2/dispatch_async.rs b/ops/op2/dispatch_async.rs index 03599aa0a..12042e7c7 100644 --- a/ops/op2/dispatch_async.rs +++ b/ops/op2/dispatch_async.rs @@ -4,8 +4,6 @@ use super::V8MappingError; use super::V8SignatureMappingError; use super::config::MacroConfig; use super::dispatch_slow::generate_dispatch_slow_call; -use super::dispatch_slow::return_value_infallible; -use super::dispatch_slow::return_value_result; use super::dispatch_slow::return_value_v8_value; use super::dispatch_slow::throw_exception; use super::dispatch_slow::with_fn_args; @@ -17,10 +15,185 @@ use super::dispatch_slow::with_self; use super::dispatch_slow::with_stack_trace; use super::generator_state::GeneratorState; use super::generator_state::gs_quote; -use super::signature::ParsedSignature; use super::signature::RetVal; +use super::signature::{Arg, ArgMarker, ArgSlowRetval, ParsedSignature}; use proc_macro2::TokenStream; -use quote::quote; +use quote::{format_ident, quote}; + +pub fn resolve_value_infallible( + generator_state: &mut GeneratorState, + ret_type: &Arg, +) -> Result { + // In the future we may be able to make this false for void again + generator_state.needs_retval = true; + + let result = match ret_type.marker() { + ArgMarker::ArrayBuffer => { + gs_quote!(generator_state(result) => (deno_core::_ops::RustToV8Marker::::from(#result))) + } + ArgMarker::Serde => { + gs_quote!(generator_state(result) => (deno_core::_ops::RustToV8Marker::::from(#result))) + } + ArgMarker::Smi => { + gs_quote!(generator_state(result) => (deno_core::_ops::RustToV8Marker::::from(#result))) + } + ArgMarker::Number => { + gs_quote!(generator_state(result) => (deno_core::_ops::RustToV8Marker::::from(#result))) + } + ArgMarker::Cppgc if generator_state.use_this_cppgc => { + generator_state.needs_isolate = true; + let wrap_object = match ret_type { + Arg::CppGcProtochain(chain) => { + let wrap_object = format_ident!("wrap_object{}", chain.len()); + quote!(#wrap_object) + } + _ => { + if generator_state.use_proto_cppgc { + quote!(wrap_object1) + } else { + quote!(wrap_object) + } + } + }; + gs_quote!(generator_state(result, scope) => ( + Some(deno_core::cppgc::#wrap_object(&mut #scope, args.this(), #result)) + )) + } + ArgMarker::Cppgc if generator_state.use_proto_cppgc => { + let marker = quote!(deno_core::_ops::RustToV8Marker::::from); + if ret_type.is_option() { + gs_quote!(generator_state(result) => (#result.map(#marker))) + } else { + gs_quote!(generator_state(result) => (#marker(#result))) + } + } + ArgMarker::Cppgc => { + let marker = quote!(deno_core::_ops::RustToV8Marker::::from); + if ret_type.is_option() { + gs_quote!(generator_state(result) => (#result.map(#marker))) + } else { + gs_quote!(generator_state(result) => (#marker(#result))) + } + } + ArgMarker::ToV8 => { + gs_quote!(generator_state(result) => (deno_core::_ops::RustToV8Marker::::from(#result))) + } + ArgMarker::Undefined => { + gs_quote!(generator_state(scope) => (deno_core::v8::undefined(&mut #scope))) + } + ArgMarker::None => { + gs_quote!(generator_state(result) => (#result)) + } + }; + generator_state.needs_scope = true; + generator_state.needs_opctx = true; + let res = match ret_type.slow_retval() { + ArgSlowRetval::RetVal => { + gs_quote!(generator_state(scope, opctx, promise_id) => { + let value = deno_core::_ops::RustToV8::to_v8(#result, &mut #scope); + #opctx.resolve_promise(&mut #scope, #promise_id, value); + }) + } + ArgSlowRetval::RetValFallible => { + let err = format_ident!("{}_err", generator_state.retval); + + gs_quote!(generator_state(scope, opctx, promise_id) => (match deno_core::_ops::RustToV8Fallible::to_v8_fallible(#result, &mut #scope) { + Ok(v) => #opctx.resolve_promise(&mut #scope, #promise_id, v), + Err(#err) => { + let exception = deno_core::error::to_v8_error( + &mut #scope, + &#err, + ); + #opctx.reject_promise(&mut #scope, #promise_id, exception) + }, + })) + } + ArgSlowRetval::V8Local => { + gs_quote!(generator_state(scope, opctx, promise_id) => { + let value = deno_core::_ops::RustToV8::to_v8(#result, &mut #scope); + #opctx.resolve_promise(&mut #scope, #promise_id, value); + }) + } + ArgSlowRetval::V8LocalNoScope => { + gs_quote!(generator_state(scope, opctx, promise_id) => { + let value = deno_core::_ops::RustToV8NoScope::to_v8(#result); + #opctx.resolve_promise(&mut #scope, #promise_id, value); + }) + } + ArgSlowRetval::V8LocalFalliable => { + let err = format_ident!("{}_err", generator_state.retval); + + gs_quote!(generator_state(scope, opctx, promise_id) => (match deno_core::_ops::RustToV8Fallible::to_v8_fallible(#result, &mut #scope) { + Ok(v) => #opctx.resolve_promise(&mut #scope, #promise_id, v), + Err(#err) => { + let exception = deno_core::error::to_v8_error( + &mut #scope, + &#err, + ); + #opctx.reject_promise(&mut #scope, #promise_id, exception) + }, + })) + } + ArgSlowRetval::None => return Err("a slow return value"), + }; + + Ok(res) +} + +/// Generates code to reject an error, adding required additional dependencies as needed. +pub(crate) fn reject_error( + generator_state: &mut GeneratorState, +) -> TokenStream { + let maybe_scope = if generator_state.needs_scope { + quote!() + } else { + with_scope(generator_state) + }; + + let maybe_opctx = if generator_state.needs_opctx { + quote!() + } else { + with_opctx(generator_state) + }; + + let maybe_args = if generator_state.needs_args { + quote!() + } else { + with_fn_args(generator_state) + }; + + gs_quote!(generator_state(scope, opctx, promise_id) => { + #maybe_scope + #maybe_args + #maybe_opctx + let exception = deno_core::error::to_v8_error( + &mut #scope, + &err, + ); + #opctx.reject_promise(&mut #scope, #promise_id, exception); + return 1; + }) +} + +pub fn resolve_value_result( + generator_state: &mut GeneratorState, + ret_type: &Arg, +) -> Result { + let infallible = resolve_value_infallible(generator_state, ret_type)?; + let exception = reject_error(generator_state); + + let tokens = gs_quote!(generator_state(result) => ( + match #result { + Ok(#result) => { + #infallible + } + Err(err) => { + #exception + } + }; + )); + Ok(tokens) +} pub(crate) fn map_async_return_type( generator_state: &mut GeneratorState, @@ -30,16 +203,14 @@ pub(crate) fn map_async_return_type( let (mapper, return_value_immediate) = match ret_val { RetVal::Infallible(r, true) | RetVal::Future(r) - | RetVal::ResultFuture(r) => ( - quote!(map_async_op_infallible), - return_value_infallible(generator_state, r)?, - ), + | RetVal::ResultFuture(r) => (quote!(map_async_op_infallible), { + resolve_value_infallible(generator_state, r)? + }), RetVal::Result(r, true) | RetVal::FutureResult(r) - | RetVal::ResultFutureResult(r) => ( - quote!(map_async_op_fallible), - return_value_result(generator_state, r)?, - ), + | RetVal::ResultFutureResult(r) => (quote!(map_async_op_fallible), { + resolve_value_result(generator_state, r)? + }), RetVal::Infallible(_, false) | RetVal::Result(_, false) => { return Err("an async return"); } @@ -60,10 +231,8 @@ pub(crate) fn generate_dispatch_async( quote!() }; - // Set input_index = 1 when we don't want promise ID as the first arg. - let input_index = if config.promise_id { 0 } else { 1 }; - let args = - generate_dispatch_slow_call(generator_state, signature, input_index)?; + generator_state.needs_scope = true; + let args = generate_dispatch_slow_call(generator_state, signature, 0)?; // Always need context and args generator_state.needs_opctx = true; @@ -99,19 +268,24 @@ pub(crate) fn generate_dispatch_async( })); } + output.extend( + gs_quote!(generator_state(retval, opctx, scope, promise_id) => { + let #promise_id = #opctx.create_promise(&mut #scope); + #retval.set(#opctx.get_promise(&mut #scope, #promise_id).unwrap().into()); + }), + ); + if config.async_lazy || config.async_deferred { let lazy = config.async_lazy; let deferred = config.async_deferred; - output.extend(gs_quote!(generator_state(promise_id, fn_args, result, opctx, scope) => { - let #promise_id = deno_core::_ops::to_i32_option(&#fn_args.get(0)).unwrap_or_default(); + output.extend(gs_quote!(generator_state(promise_id, result, opctx, scope) => { // Lazy and deferred results will always return None deno_core::_ops::#mapper(#opctx, #lazy, #deferred, #promise_id, #result, |#scope, #result| { #return_value }); })); } else { - output.extend(gs_quote!(generator_state(promise_id, fn_args, result, opctx, scope) => { - let #promise_id = deno_core::_ops::to_i32_option(&#fn_args.get(0)).unwrap_or_default(); + output.extend(gs_quote!(generator_state(promise_id, result, opctx, scope) => { if let Some(#result) = deno_core::_ops::#mapper(#opctx, false, false, #promise_id, #result, |#scope, #result| { #return_value }) { diff --git a/ops/op2/dispatch_fast.rs b/ops/op2/dispatch_fast.rs index 2d508d754..668f7dd1a 100644 --- a/ops/op2/dispatch_fast.rs +++ b/ops/op2/dispatch_fast.rs @@ -40,7 +40,6 @@ pub(crate) enum FastArg { arg: Arg, }, CallbackOptions, - PromiseId, } #[derive(Clone)] @@ -60,7 +59,6 @@ impl FastSignature { .args .iter() .filter_map(|arg| match arg { - FastArg::PromiseId => Some(V8FastCallType::I32.quote_ctype()), FastArg::CallbackOptions => { Some(V8FastCallType::CallbackOptions.quote_ctype()) } @@ -82,10 +80,6 @@ impl FastSignature { generator_state.fast_api_callback_options.clone(), V8FastCallType::CallbackOptions.quote_rust_type(), )), - FastArg::PromiseId => Some(( - generator_state.promise_id.clone(), - V8FastCallType::I32.quote_rust_type(), - )), FastArg::Actual { arg_type, name_in, .. } => Some((format_ident!("{name_in}"), arg_type.quote_rust_type())), @@ -114,7 +108,7 @@ impl FastSignature { generator_state.needs_scope = true; } } - FastArg::CallbackOptions | FastArg::PromiseId => {} + FastArg::CallbackOptions => {} } } Ok(call_args) @@ -132,7 +126,7 @@ impl FastSignature { call_names.push(quote!(#name_out)); } } - FastArg::CallbackOptions | FastArg::PromiseId => {} + FastArg::CallbackOptions => {} } } call_names @@ -166,10 +160,6 @@ impl FastSignature { self.args.push(FastArg::CallbackOptions); } } - - fn insert_promise_id(&mut self) { - self.args.insert(0, FastArg::PromiseId) - } } #[allow(unused)] @@ -403,10 +393,6 @@ pub(crate) fn generate_dispatch_fast( _ => quote!(), }; - if signature.ret_val.is_async() { - fastsig.insert_promise_id(); - } - // Note that this triggers needs_* values in generator_state let call_args = fastsig.call_args(generator_state)?; @@ -558,6 +544,14 @@ pub(crate) fn generate_dispatch_fast( let (fastcall_names, fastcall_types): (Vec<_>, Vec<_>) = fastsig.input_args(generator_state).into_iter().unzip(); + let maybe_promise = if config.r#async { + gs_quote!(generator_state(scope, opctx, promise_id) => ( + let #promise_id = #opctx.create_promise(&mut #scope); + )) + } else { + quote!() + }; + let fast_fn = gs_quote!(generator_state(result, fast_api_callback_options, fast_function, fast_function_metrics) => { #[allow(clippy::too_many_arguments)] extern "C" fn #fast_function_metrics<'s>( @@ -597,6 +591,7 @@ pub(crate) fn generate_dispatch_fast( #(#call_args)* #call (#(#call_names),*) }; + #maybe_promise #handle_error #handle_result } @@ -645,6 +640,7 @@ fn map_v8_fastcall_arg_to_arg( js_runtime_state, scope, needs_opctx, + needs_scope, needs_fast_api_callback_options, needs_fast_isolate, needs_js_runtime_state, @@ -722,6 +718,11 @@ fn map_v8_fastcall_arg_to_arg( let #arg_ident = #fast_api_callback_options.isolate; }) } + Arg::Special(Special::PromiseId) => { + *needs_opctx = true; + *needs_scope = true; + quote!(let #arg_ident = #opctx.create_promise(&mut #scope);) + } Arg::Ref(RefType::Ref, Special::OpState) => { *needs_opctx = true; quote!(let #arg_ident = &::std::cell::RefCell::borrow(&#opctx.state);) @@ -908,6 +909,7 @@ fn map_arg_to_v8_fastcall_type( | Arg::VarArgs | Arg::This | Arg::Special(Special::Isolate) + | Arg::Special(Special::PromiseId) | Arg::OptionState(..) => V8FastCallType::Virtual, // Other types + ref types are not handled Arg::OptionNumeric(..) diff --git a/ops/op2/dispatch_slow.rs b/ops/op2/dispatch_slow.rs index 1920a9b5f..8df5906d1 100644 --- a/ops/op2/dispatch_slow.rs +++ b/ops/op2/dispatch_slow.rs @@ -588,6 +588,10 @@ pub fn from_arg( Arg::External(External::Ptr(_)) => { from_arg_option(generator_state, &arg_ident, "external") } + Arg::Special(Special::PromiseId) => { + *needs_opctx = true; + quote!(let #arg_ident = #opctx.create_promise(&mut #scope);) + } Arg::Special(Special::Isolate) => { *needs_opctx = true; quote!(let #arg_ident = #opctx.isolate;) diff --git a/ops/op2/signature.rs b/ops/op2/signature.rs index dfbd5f5c8..f934d09dc 100644 --- a/ops/op2/signature.rs +++ b/ops/op2/signature.rs @@ -147,6 +147,7 @@ pub enum Special { JsRuntimeState, FastApiCallbackOptions, Isolate, + PromiseId, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -360,7 +361,8 @@ impl Arg { | Special::OpState | Special::JsRuntimeState | Special::HandleScope - | Special::Isolate, + | Special::Isolate + | Special::PromiseId, ) => true, Self::Ref( _, @@ -1444,6 +1446,7 @@ fn parse_type_path( } ( OpState ) => Ok(CBare(TSpecial(Special::OpState))), ( JsRuntimeState ) => Ok(CBare(TSpecial(Special::JsRuntimeState))), + ( PromiseId ) => Ok(CBare(TSpecial(Special::PromiseId))), ( v8 :: Isolate ) => Ok(CBare(TSpecial(Special::Isolate))), ( v8 :: HandleScope $( < $_scope:lifetime >)? ) => Ok(CBare(TSpecial(Special::HandleScope))), ( v8 :: FastApiCallbackOptions ) => Ok(CBare(TSpecial(Special::FastApiCallbackOptions))), @@ -1483,7 +1486,9 @@ fn parse_type_path( // the easiest way to work with the 'rules!' macro above. match res { // OpState and JsRuntimeState appears in both ways - CBare(TSpecial(Special::OpState | Special::JsRuntimeState)) => {} + CBare(TSpecial( + Special::OpState | Special::JsRuntimeState | Special::PromiseId, + )) => {} CBare( TString(Strings::RefStr) | TSpecial(Special::HandleScope) | TV8(_), ) => { diff --git a/ops/op2/test_cases/async/async_arg_return.out b/ops/op2/test_cases/async/async_arg_return.out index acf77d78e..14ecc13e5 100644 --- a/ops/op2/test_cases/async/async_arg_return.out +++ b/ops/op2/test_cases/async/async_arg_return.out @@ -34,6 +34,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -45,7 +46,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { - let arg0 = args.get(1usize as i32); + let arg0 = args.get(0usize as i32); let Some(arg0) = deno_core::_ops::to_i32_option(&arg0) else { deno_core::_ops::throw_error_one_byte_info(&info, "expected i32"); return 1; @@ -53,8 +54,8 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { let arg0 = arg0 as _; Self::call(arg0) }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_infallible( opctx, false, @@ -63,7 +64,8 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { result, |scope, result| { Ok(deno_core::_ops::RustToV8::to_v8(result, scope)) }, ) { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv); + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); return 0; } return 2; diff --git a/ops/op2/test_cases/async/async_arg_return_result.out b/ops/op2/test_cases/async/async_arg_return_result.out index c740879d4..13a1111ce 100644 --- a/ops/op2/test_cases/async/async_arg_return_result.out +++ b/ops/op2/test_cases/async/async_arg_return_result.out @@ -34,6 +34,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -45,7 +46,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { - let arg0 = args.get(1usize as i32); + let arg0 = args.get(0usize as i32); let Some(arg0) = deno_core::_ops::to_i32_option(&arg0) else { deno_core::_ops::throw_error_one_byte_info(&info, "expected i32"); return 1; @@ -53,8 +54,8 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { let arg0 = arg0 as _; Self::call(arg0) }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_fallible( opctx, false, @@ -65,14 +66,12 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { ) { match result { Ok(result) => { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv) + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); } Err(err) => { - let mut scope = unsafe { - deno_core::v8::CallbackScope::new(info) - }; let exception = deno_core::error::to_v8_error(&mut scope, &err); - scope.throw_exception(exception); + opctx.reject_promise(&mut scope, promise_id, exception); return 1; } }; diff --git a/ops/op2/test_cases/async/async_cppgc.out b/ops/op2/test_cases/async/async_cppgc.out index 782a8b994..73dc8b401 100644 --- a/ops/op2/test_cases/async/async_cppgc.out +++ b/ops/op2/test_cases/async/async_cppgc.out @@ -46,8 +46,8 @@ const fn op_make_cppgc_object() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { Self::call() }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_infallible( opctx, false, @@ -66,15 +66,14 @@ const fn op_make_cppgc_object() -> ::deno_core::_ops::OpDecl { ) }, ) { - rv.set( - deno_core::_ops::RustToV8::to_v8( - deno_core::_ops::RustToV8Marker::< - deno_core::_ops::CppGcMarker, - _, - >::from(result), - &mut scope, - ), + let value = deno_core::_ops::RustToV8::to_v8( + deno_core::_ops::RustToV8Marker::< + deno_core::_ops::CppGcMarker, + _, + >::from(result), + &mut scope, ); + opctx.resolve_promise(&mut scope, promise_id, value); return 0; } return 2; @@ -171,7 +170,7 @@ const fn op_use_cppgc_object() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { - let arg0 = args.get(1usize as i32); + let arg0 = args.get(0usize as i32); let Some(mut arg0) = deno_core::_ops::try_unwrap_cppgc_object::< Wrap, >(&mut scope, arg0) else { @@ -184,8 +183,8 @@ const fn op_use_cppgc_object() -> ::deno_core::_ops::OpDecl { Self::call(arg0).await } }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_infallible( opctx, false, @@ -194,7 +193,8 @@ const fn op_use_cppgc_object() -> ::deno_core::_ops::OpDecl { result, |scope, result| { Ok(deno_core::_ops::RustToV8::to_v8(result, scope)) }, ) { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv); + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); return 0; } return 2; @@ -289,7 +289,7 @@ const fn op_use_optional_cppgc_object() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { - let arg0 = args.get(1usize as i32); + let arg0 = args.get(0usize as i32); let arg0 = if arg0.is_null_or_undefined() { None } else if let Some(mut arg0) = deno_core::_ops::try_unwrap_cppgc_object::< @@ -306,8 +306,8 @@ const fn op_use_optional_cppgc_object() -> ::deno_core::_ops::OpDecl { Self::call(arg0).await } }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_infallible( opctx, false, @@ -316,7 +316,8 @@ const fn op_use_optional_cppgc_object() -> ::deno_core::_ops::OpDecl { result, |scope, result| { Ok(deno_core::_ops::RustToV8::to_v8(result, scope)) }, ) { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv); + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); return 0; } return 2; diff --git a/ops/op2/test_cases/async/async_deferred.out b/ops/op2/test_cases/async/async_deferred.out index 7a512ffff..a127a1f98 100644 --- a/ops/op2/test_cases/async/async_deferred.out +++ b/ops/op2/test_cases/async/async_deferred.out @@ -23,14 +23,7 @@ pub const fn op_async_deferred() -> ::deno_core::_ops::OpDecl { Self::v8_fn_ptr_fast as _, &deno_core::v8::fast_api::CFunctionInfo::new( CType::Void.as_info(), - &[ - CType::V8Value.as_info(), - v8::fast_api::CTypeInfo::new( - CType::Int32, - v8::fast_api::Flags::Clamp, - ), - CType::CallbackOptions.as_info(), - ], + &[CType::V8Value.as_info(), CType::CallbackOptions.as_info()], deno_core::v8::fast_api::Int64Representation::BigInt, ), ) @@ -42,14 +35,7 @@ pub const fn op_async_deferred() -> ::deno_core::_ops::OpDecl { Self::v8_fn_ptr_fast_metrics as _, &deno_core::v8::fast_api::CFunctionInfo::new( CType::Void.as_info(), - &[ - CType::V8Value.as_info(), - v8::fast_api::CTypeInfo::new( - CType::Int32, - v8::fast_api::Flags::Clamp, - ), - CType::CallbackOptions.as_info(), - ], + &[CType::V8Value.as_info(), CType::CallbackOptions.as_info()], deno_core::v8::fast_api::Int64Representation::BigInt, ), ) @@ -66,7 +52,6 @@ pub const fn op_async_deferred() -> ::deno_core::_ops::OpDecl { #[allow(clippy::too_many_arguments)] extern "C" fn v8_fn_ptr_fast_metrics<'s>( this: deno_core::v8::Local, - promise_id: i32, fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions< 's, >, @@ -84,7 +69,7 @@ pub const fn op_async_deferred() -> ::deno_core::_ops::OpDecl { opctx, deno_core::_ops::OpMetricsEvent::Dispatched, ); - let res = Self::v8_fn_ptr_fast(this, promise_id, fast_api_callback_options); + let res = Self::v8_fn_ptr_fast(this, fast_api_callback_options); deno_core::_ops::dispatch_metrics_fast( opctx, deno_core::_ops::OpMetricsEvent::Completed, @@ -94,7 +79,6 @@ pub const fn op_async_deferred() -> ::deno_core::_ops::OpDecl { #[allow(clippy::too_many_arguments)] extern "C" fn v8_fn_ptr_fast<'s>( this: deno_core::v8::Local, - promise_id: i32, fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions< 's, >, @@ -106,6 +90,9 @@ pub const fn op_async_deferred() -> ::deno_core::_ops::OpDecl { let fast_api_callback_options: &'s mut _ = unsafe { &mut *fast_api_callback_options }; + let mut scope = unsafe { + deno_core::v8::CallbackScope::new(&*fast_api_callback_options) + }; let opctx: &'s _ = unsafe { &*(deno_core::v8::Local::< deno_core::v8::External, @@ -113,6 +100,7 @@ pub const fn op_async_deferred() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { Self::call() }; + let promise_id = opctx.create_promise(&mut scope); deno_core::_ops::map_async_op_fallible( opctx, false, @@ -129,6 +117,7 @@ pub const fn op_async_deferred() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -140,8 +129,8 @@ pub const fn op_async_deferred() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { Self::call() }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); deno_core::_ops::map_async_op_fallible( opctx, false, diff --git a/ops/op2/test_cases/async/async_jsbuffer.out b/ops/op2/test_cases/async/async_jsbuffer.out index a66c8a248..4407e5673 100644 --- a/ops/op2/test_cases/async/async_jsbuffer.out +++ b/ops/op2/test_cases/async/async_jsbuffer.out @@ -46,7 +46,7 @@ pub const fn op_async_v8_buffer() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { - let arg0 = args.get(1usize as i32); + let arg0 = args.get(0usize as i32); let mut arg0_temp; arg0_temp = match unsafe { deno_core::_ops::to_v8_slice::(arg0) } { Ok(arg0) => arg0, @@ -58,8 +58,8 @@ pub const fn op_async_v8_buffer() -> ::deno_core::_ops::OpDecl { let arg0 = deno_core::serde_v8::JsBuffer::from_parts(arg0_temp); Self::call(arg0) }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_infallible( opctx, false, @@ -74,10 +74,13 @@ pub const fn op_async_v8_buffer() -> ::deno_core::_ops::OpDecl { result, &mut scope, ) { - Ok(v) => rv.set(v), + Ok(v) => opctx.resolve_promise(&mut scope, promise_id, v), Err(rv_err) => { - deno_core::_ops::throw_error_js_error_class(&mut scope, &rv_err); - return 1; + let exception = deno_core::error::to_v8_error( + &mut scope, + &rv_err, + ); + opctx.reject_promise(&mut scope, promise_id, exception) } }; return 0; diff --git a/ops/op2/test_cases/async/async_lazy.out b/ops/op2/test_cases/async/async_lazy.out index 29239db86..e43e6a2b7 100644 --- a/ops/op2/test_cases/async/async_lazy.out +++ b/ops/op2/test_cases/async/async_lazy.out @@ -23,14 +23,7 @@ pub const fn op_async_lazy() -> ::deno_core::_ops::OpDecl { Self::v8_fn_ptr_fast as _, &deno_core::v8::fast_api::CFunctionInfo::new( CType::Void.as_info(), - &[ - CType::V8Value.as_info(), - v8::fast_api::CTypeInfo::new( - CType::Int32, - v8::fast_api::Flags::Clamp, - ), - CType::CallbackOptions.as_info(), - ], + &[CType::V8Value.as_info(), CType::CallbackOptions.as_info()], deno_core::v8::fast_api::Int64Representation::BigInt, ), ) @@ -42,14 +35,7 @@ pub const fn op_async_lazy() -> ::deno_core::_ops::OpDecl { Self::v8_fn_ptr_fast_metrics as _, &deno_core::v8::fast_api::CFunctionInfo::new( CType::Void.as_info(), - &[ - CType::V8Value.as_info(), - v8::fast_api::CTypeInfo::new( - CType::Int32, - v8::fast_api::Flags::Clamp, - ), - CType::CallbackOptions.as_info(), - ], + &[CType::V8Value.as_info(), CType::CallbackOptions.as_info()], deno_core::v8::fast_api::Int64Representation::BigInt, ), ) @@ -66,7 +52,6 @@ pub const fn op_async_lazy() -> ::deno_core::_ops::OpDecl { #[allow(clippy::too_many_arguments)] extern "C" fn v8_fn_ptr_fast_metrics<'s>( this: deno_core::v8::Local, - promise_id: i32, fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions< 's, >, @@ -84,7 +69,7 @@ pub const fn op_async_lazy() -> ::deno_core::_ops::OpDecl { opctx, deno_core::_ops::OpMetricsEvent::Dispatched, ); - let res = Self::v8_fn_ptr_fast(this, promise_id, fast_api_callback_options); + let res = Self::v8_fn_ptr_fast(this, fast_api_callback_options); deno_core::_ops::dispatch_metrics_fast( opctx, deno_core::_ops::OpMetricsEvent::Completed, @@ -94,7 +79,6 @@ pub const fn op_async_lazy() -> ::deno_core::_ops::OpDecl { #[allow(clippy::too_many_arguments)] extern "C" fn v8_fn_ptr_fast<'s>( this: deno_core::v8::Local, - promise_id: i32, fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions< 's, >, @@ -106,6 +90,9 @@ pub const fn op_async_lazy() -> ::deno_core::_ops::OpDecl { let fast_api_callback_options: &'s mut _ = unsafe { &mut *fast_api_callback_options }; + let mut scope = unsafe { + deno_core::v8::CallbackScope::new(&*fast_api_callback_options) + }; let opctx: &'s _ = unsafe { &*(deno_core::v8::Local::< deno_core::v8::External, @@ -113,6 +100,7 @@ pub const fn op_async_lazy() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { Self::call() }; + let promise_id = opctx.create_promise(&mut scope); deno_core::_ops::map_async_op_fallible( opctx, true, @@ -129,6 +117,7 @@ pub const fn op_async_lazy() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -140,8 +129,8 @@ pub const fn op_async_lazy() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { Self::call() }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); deno_core::_ops::map_async_op_fallible( opctx, true, diff --git a/ops/op2/test_cases/async/async_op_metadata.out b/ops/op2/test_cases/async/async_op_metadata.out index ccfaa8fef..fb09dee8a 100644 --- a/ops/op2/test_cases/async/async_op_metadata.out +++ b/ops/op2/test_cases/async/async_op_metadata.out @@ -36,6 +36,7 @@ const fn op_blob_read_part() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -47,8 +48,8 @@ const fn op_blob_read_part() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { Self::call() }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_infallible( opctx, false, @@ -57,7 +58,8 @@ const fn op_blob_read_part() -> ::deno_core::_ops::OpDecl { result, |scope, result| { Ok(deno_core::_ops::RustToV8::to_v8(result, scope)) }, ) { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv); + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); return 0; } return 2; @@ -144,6 +146,7 @@ const fn op_broadcast_recv() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -155,8 +158,8 @@ const fn op_broadcast_recv() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { Self::call() }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_infallible( opctx, false, @@ -165,7 +168,8 @@ const fn op_broadcast_recv() -> ::deno_core::_ops::OpDecl { result, |scope, result| { Ok(deno_core::_ops::RustToV8::to_v8(result, scope)) }, ) { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv); + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); return 0; } return 2; diff --git a/ops/op2/test_cases/async/async_opstate.out b/ops/op2/test_cases/async/async_opstate.out index f7f10d7be..c8c59e437 100644 --- a/ops/op2/test_cases/async/async_opstate.out +++ b/ops/op2/test_cases/async/async_opstate.out @@ -34,6 +34,7 @@ pub const fn op_async_opstate() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -49,8 +50,8 @@ pub const fn op_async_opstate() -> ::deno_core::_ops::OpDecl { let arg0 = opstate.clone(); Self::call(arg0) }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_fallible( opctx, false, @@ -61,14 +62,12 @@ pub const fn op_async_opstate() -> ::deno_core::_ops::OpDecl { ) { match result { Ok(result) => { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv) + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); } Err(err) => { - let mut scope = unsafe { - deno_core::v8::CallbackScope::new(info) - }; let exception = deno_core::error::to_v8_error(&mut scope, &err); - scope.throw_exception(exception); + opctx.reject_promise(&mut scope, promise_id, exception); return 1; } }; diff --git a/ops/op2/test_cases/async/async_precise_capture.out b/ops/op2/test_cases/async/async_precise_capture.out index 60f949e3f..453281577 100644 --- a/ops/op2/test_cases/async/async_precise_capture.out +++ b/ops/op2/test_cases/async/async_precise_capture.out @@ -34,6 +34,7 @@ pub const fn op_async_impl_use() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -45,7 +46,7 @@ pub const fn op_async_impl_use() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { - let arg0 = args.get(1usize as i32); + let arg0 = args.get(0usize as i32); let Some(arg0) = deno_core::_ops::to_i32_option(&arg0) else { deno_core::_ops::throw_error_one_byte_info(&info, "expected i32"); return 1; @@ -53,8 +54,8 @@ pub const fn op_async_impl_use() -> ::deno_core::_ops::OpDecl { let arg0 = arg0 as _; Self::call(arg0) }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_fallible( opctx, false, @@ -65,14 +66,12 @@ pub const fn op_async_impl_use() -> ::deno_core::_ops::OpDecl { ) { match result { Ok(result) => { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv) + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); } Err(err) => { - let mut scope = unsafe { - deno_core::v8::CallbackScope::new(info) - }; let exception = deno_core::error::to_v8_error(&mut scope, &err); - scope.throw_exception(exception); + opctx.reject_promise(&mut scope, promise_id, exception); return 1; } }; diff --git a/ops/op2/test_cases/async/async_result.out b/ops/op2/test_cases/async/async_result.out index 9d461ab99..8d1016b49 100644 --- a/ops/op2/test_cases/async/async_result.out +++ b/ops/op2/test_cases/async/async_result.out @@ -34,6 +34,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -45,8 +46,8 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { Self::call() }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_fallible( opctx, false, @@ -57,14 +58,12 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { ) { match result { Ok(result) => { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv) + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); } Err(err) => { - let mut scope = unsafe { - deno_core::v8::CallbackScope::new(info) - }; let exception = deno_core::error::to_v8_error(&mut scope, &err); - scope.throw_exception(exception); + opctx.reject_promise(&mut scope, promise_id, exception); return 1; } }; diff --git a/ops/op2/test_cases/async/async_result_impl.out b/ops/op2/test_cases/async/async_result_impl.out index bb186cb4c..9219401df 100644 --- a/ops/op2/test_cases/async/async_result_impl.out +++ b/ops/op2/test_cases/async/async_result_impl.out @@ -34,6 +34,7 @@ pub const fn op_async_result_impl() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -45,7 +46,7 @@ pub const fn op_async_result_impl() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { - let arg0 = args.get(1usize as i32); + let arg0 = args.get(0usize as i32); let Some(arg0) = deno_core::_ops::to_i32_option(&arg0) else { deno_core::_ops::throw_error_one_byte_info(&info, "expected i32"); return 1; @@ -56,14 +57,13 @@ pub const fn op_async_result_impl() -> ::deno_core::_ops::OpDecl { let result = match result { Ok(result) => result, Err(err) => { - let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let exception = deno_core::error::to_v8_error(&mut scope, &err); scope.throw_exception(exception); return 1; } }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_fallible( opctx, false, @@ -74,14 +74,12 @@ pub const fn op_async_result_impl() -> ::deno_core::_ops::OpDecl { ) { match result { Ok(result) => { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv) + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); } Err(err) => { - let mut scope = unsafe { - deno_core::v8::CallbackScope::new(info) - }; let exception = deno_core::error::to_v8_error(&mut scope, &err); - scope.throw_exception(exception); + opctx.reject_promise(&mut scope, promise_id, exception); return 1; } }; diff --git a/ops/op2/test_cases/async/async_result_smi.out b/ops/op2/test_cases/async/async_result_smi.out index 8daeb1395..664f0e489 100644 --- a/ops/op2/test_cases/async/async_result_smi.out +++ b/ops/op2/test_cases/async/async_result_smi.out @@ -34,6 +34,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -45,7 +46,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { - let arg0 = args.get(1usize as i32); + let arg0 = args.get(0usize as i32); let Some(arg0) = deno_core::_ops::to_i32_option(&arg0) else { deno_core::_ops::throw_error_one_byte_info(&info, "expected i32"); return 1; @@ -53,8 +54,8 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { let arg0 = arg0 as _; Self::call(arg0) }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_fallible( opctx, false, @@ -75,20 +76,18 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { ) { match result { Ok(result) => { - deno_core::_ops::RustToV8RetVal::to_v8_rv( + let value = deno_core::_ops::RustToV8::to_v8( deno_core::_ops::RustToV8Marker::< deno_core::_ops::SmiMarker, _, >::from(result), - &mut rv, - ) + &mut scope, + ); + opctx.resolve_promise(&mut scope, promise_id, value); } Err(err) => { - let mut scope = unsafe { - deno_core::v8::CallbackScope::new(info) - }; let exception = deno_core::error::to_v8_error(&mut scope, &err); - scope.throw_exception(exception); + opctx.reject_promise(&mut scope, promise_id, exception); return 1; } }; diff --git a/ops/op2/test_cases/async/async_stack_trace.out b/ops/op2/test_cases/async/async_stack_trace.out index f2a47df89..7fd9c6f98 100644 --- a/ops/op2/test_cases/async/async_stack_trace.out +++ b/ops/op2/test_cases/async/async_stack_trace.out @@ -60,8 +60,8 @@ pub const fn op_async_stack_trace() -> ::deno_core::_ops::OpDecl { op_state.op_stack_trace_callback.as_ref().unwrap()(js_error.frames) } let result = { Self::call() }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_infallible( opctx, false, @@ -70,7 +70,8 @@ pub const fn op_async_stack_trace() -> ::deno_core::_ops::OpDecl { result, |scope, result| { Ok(deno_core::_ops::RustToV8::to_v8(result, scope)) }, ) { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv); + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); return 0; } return 2; diff --git a/ops/op2/test_cases/async/async_v8_global.out b/ops/op2/test_cases/async/async_v8_global.out index 04f267f22..a505acf68 100644 --- a/ops/op2/test_cases/async/async_v8_global.out +++ b/ops/op2/test_cases/async/async_v8_global.out @@ -46,7 +46,7 @@ pub const fn op_async_v8_global() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { - let arg0 = args.get(1usize as i32); + let arg0 = args.get(0usize as i32); let Ok(mut arg0) = deno_core::_ops::v8_try_convert::< deno_core::v8::String, >(arg0) else { @@ -56,8 +56,8 @@ pub const fn op_async_v8_global() -> ::deno_core::_ops::OpDecl { let arg0 = deno_core::v8::Global::new(&mut scope, arg0); Self::call(arg0) }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_infallible( opctx, false, @@ -66,7 +66,8 @@ pub const fn op_async_v8_global() -> ::deno_core::_ops::OpDecl { result, |scope, result| { Ok(deno_core::_ops::RustToV8::to_v8(result, scope)) }, ) { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv); + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); return 0; } return 2; diff --git a/ops/op2/test_cases/async/async_void.out b/ops/op2/test_cases/async/async_void.out index df3ac7ac5..f8a2f1d5f 100644 --- a/ops/op2/test_cases/async/async_void.out +++ b/ops/op2/test_cases/async/async_void.out @@ -34,6 +34,7 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { let _reentrancy_check_guard = deno_core::_ops::reentrancy_check( &::DECL, ); + let mut scope = unsafe { deno_core::v8::CallbackScope::new(info) }; let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(info); let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( info, @@ -45,8 +46,8 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { .value() as *const deno_core::_ops::OpCtx) }; let result = { Self::call() }; - let promise_id = deno_core::_ops::to_i32_option(&args.get(0)) - .unwrap_or_default(); + let promise_id = opctx.create_promise(&mut scope); + rv.set(opctx.get_promise(&mut scope, promise_id).unwrap().into()); if let Some(result) = deno_core::_ops::map_async_op_infallible( opctx, false, @@ -55,7 +56,8 @@ pub const fn op_async() -> ::deno_core::_ops::OpDecl { result, |scope, result| { Ok(deno_core::_ops::RustToV8::to_v8(result, scope)) }, ) { - deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv); + let value = deno_core::_ops::RustToV8::to_v8(result, &mut scope); + opctx.resolve_promise(&mut scope, promise_id, value); return 0; } return 2; From eef350b7c7098254b514aa8005794edf6d1d84e1 Mon Sep 17 00:00:00 2001 From: Cyan Changes Date: Sun, 20 Jul 2025 23:54:24 +0800 Subject: [PATCH 2/3] format --- core/01_core.js | 2 +- core/ops_builtin_v8.rs | 72 +++++++++---------- core/runtime/jsruntime.rs | 30 ++++---- .../op_driver/futures_unordered_driver.rs | 12 ++-- 4 files changed, 60 insertions(+), 56 deletions(-) diff --git a/core/01_core.js b/core/01_core.js index 82bef0293..c40898850 100755 --- a/core/01_core.js +++ b/core/01_core.js @@ -193,7 +193,7 @@ } if (timers) { - op_print(`${timers.join("%")}`) + op_print(`${timers.join("%")}`); timersRunning = true; for (let i = 0; i < timers.length; i += 3) { timerDepth = timers[i]; diff --git a/core/ops_builtin_v8.rs b/core/ops_builtin_v8.rs index f3edb2b46..3a8eecfda 100644 --- a/core/ops_builtin_v8.rs +++ b/core/ops_builtin_v8.rs @@ -47,61 +47,61 @@ pub fn op_set_handled_promise_rejection_handler( #[op2(fast)] pub fn op_ref_op_promise( - scope: &mut v8::HandleScope, - promise: v8::Local, + scope: &mut v8::HandleScope, + promise: v8::Local, ) { - let context_state = JsRealm::state_from_scope(scope); - let promise_id = match context_state - .pending_ops - .promise_id_from_promise(scope, promise) - { - Some(promise_id) => promise_id, - None => return, - }; - context_state.unrefed_ops.borrow_mut().remove(&promise_id); + let context_state = JsRealm::state_from_scope(scope); + let promise_id = match context_state + .pending_ops + .promise_id_from_promise(scope, promise) + { + Some(promise_id) => promise_id, + None => return, + }; + context_state.unrefed_ops.borrow_mut().remove(&promise_id); } #[op2(fast)] pub fn op_unref_op_promise( - scope: &mut v8::HandleScope, - promise: v8::Local, + scope: &mut v8::HandleScope, + promise: v8::Local, ) { - let context_state = JsRealm::state_from_scope(scope); - let promise_id = match context_state - .pending_ops - .promise_id_from_promise(scope, promise) - { - Some(promise_id) => promise_id, - None => return, - }; - context_state.unrefed_ops.borrow_mut().insert(promise_id); + let context_state = JsRealm::state_from_scope(scope); + let promise_id = match context_state + .pending_ops + .promise_id_from_promise(scope, promise) + { + Some(promise_id) => promise_id, + None => return, + }; + context_state.unrefed_ops.borrow_mut().insert(promise_id); } #[op2(fast)] pub fn op_ref_op(scope: &mut v8::HandleScope, promise_id: i32) { - let context_state = JsRealm::state_from_scope(scope); - if context_state.pending_ops.has_promise(promise_id) { - context_state.unrefed_ops.borrow_mut().remove(&promise_id); - } + let context_state = JsRealm::state_from_scope(scope); + if context_state.pending_ops.has_promise(promise_id) { + context_state.unrefed_ops.borrow_mut().remove(&promise_id); + } } #[op2(fast)] pub fn op_unref_op(scope: &mut v8::HandleScope, promise_id: i32) { - let context_state = JsRealm::state_from_scope(scope); - if context_state.pending_ops.has_promise(promise_id) { - context_state.unrefed_ops.borrow_mut().insert(promise_id); - } + let context_state = JsRealm::state_from_scope(scope); + if context_state.pending_ops.has_promise(promise_id) { + context_state.unrefed_ops.borrow_mut().insert(promise_id); + } } #[op2] pub fn op_promise_promise_id( - scope: &mut v8::HandleScope, - promise: v8::Local, + scope: &mut v8::HandleScope, + promise: v8::Local, ) -> Option { - let context_state = JsRealm::state_from_scope(scope); - context_state - .pending_ops - .promise_id_from_promise(scope, promise) + let context_state = JsRealm::state_from_scope(scope); + context_state + .pending_ops + .promise_id_from_promise(scope, promise) } #[op2(fast)] pub fn op_leak_tracing_enable(scope: &mut v8::HandleScope, enabled: bool) { diff --git a/core/runtime/jsruntime.rs b/core/runtime/jsruntime.rs index fb7720036..ef8b178ec 100644 --- a/core/runtime/jsruntime.rs +++ b/core/runtime/jsruntime.rs @@ -2626,24 +2626,24 @@ impl JsRuntime { let res = res.unwrap(scope); let op_driver = { - let op_ctx = &context_state.op_ctxs[op_id as usize]; - if op_ctx.metrics_enabled() { - if res.is_ok() { - dispatch_metrics_async(op_ctx, OpMetricsEvent::CompletedAsync); - } else { - dispatch_metrics_async(op_ctx, OpMetricsEvent::ErrorAsync); - } + let op_ctx = &context_state.op_ctxs[op_id as usize]; + if op_ctx.metrics_enabled() { + if res.is_ok() { + dispatch_metrics_async(op_ctx, OpMetricsEvent::CompletedAsync); + } else { + dispatch_metrics_async(op_ctx, OpMetricsEvent::ErrorAsync); } - op_ctx.op_driver() + } + op_ctx.op_driver() }; context_state.unrefed_ops.borrow_mut().remove(&promise_id); context_state - .activity_traces - .complete(RuntimeActivityType::AsyncOp, promise_id as _); + .activity_traces + .complete(RuntimeActivityType::AsyncOp, promise_id as _); match res { - Ok(value) => op_driver.resolve_promise(scope, promise_id, value), - Err(reason) => op_driver.reject_promise(scope, promise_id, reason), + Ok(value) => op_driver.resolve_promise(scope, promise_id, value), + Err(reason) => op_driver.reject_promise(scope, promise_id, reason), } dispatched_ops |= true; @@ -2734,7 +2734,11 @@ impl JsRuntime { let js_event_loop_tick_cb = js_event_loop_tick_cb.as_ref().unwrap().open(tc_scope); - js_event_loop_tick_cb.call(tc_scope, undefined, &[rejections, timers, has_tick_scheduled]); + js_event_loop_tick_cb.call( + tc_scope, + undefined, + &[rejections, timers, has_tick_scheduled], + ); if let Some(exception) = tc_scope.exception() { return exception_to_err_result(tc_scope, exception, false, true); diff --git a/core/runtime/op_driver/futures_unordered_driver.rs b/core/runtime/op_driver/futures_unordered_driver.rs index cb7da00dd..6b5d3e209 100644 --- a/core/runtime/op_driver/futures_unordered_driver.rs +++ b/core/runtime/op_driver/futures_unordered_driver.rs @@ -155,9 +155,9 @@ impl OpDriver for FuturesUnorderedDriver { promise_id: PromiseId, value: v8::Local, ) { - let maybe_resolver = self.promises.borrow_mut().try_remove(promise_id as usize); - if let Some(resolver) = maybe_resolver - { + let maybe_resolver = + self.promises.borrow_mut().try_remove(promise_id as usize); + if let Some(resolver) = maybe_resolver { resolver.open(scope).resolve(scope, value); } } @@ -168,9 +168,9 @@ impl OpDriver for FuturesUnorderedDriver { promise_id: PromiseId, reason: v8::Local, ) { - let maybe_resolver = self.promises.borrow_mut().try_remove(promise_id as usize); - if let Some(resolver) = maybe_resolver - { + let maybe_resolver = + self.promises.borrow_mut().try_remove(promise_id as usize); + if let Some(resolver) = maybe_resolver { resolver.open(scope).reject(scope, reason); } } From 6caca3830183d1b366400c0f030073ebf76b1338 Mon Sep 17 00:00:00 2001 From: Cyan Date: Mon, 4 Aug 2025 23:32:19 +0800 Subject: [PATCH 3/3] remove debug print --- core/01_core.js | 1 - 1 file changed, 1 deletion(-) diff --git a/core/01_core.js b/core/01_core.js index c40898850..410d3ee45 100755 --- a/core/01_core.js +++ b/core/01_core.js @@ -193,7 +193,6 @@ } if (timers) { - op_print(`${timers.join("%")}`); timersRunning = true; for (let i = 0; i < timers.length; i += 3) { timerDepth = timers[i];