From 56c88a890caf5e585effed262bf334cd126e07f2 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 30 Dec 2024 20:18:12 -0800 Subject: [PATCH] Support async methods in object wrap (#1013) ```rust impl TestObjectWrap { #[async_method] async fn with_async_fn(&self) { // ... } } ``` --- core/00_infra.js | 43 ++++++++++++++++++++++++++--------- core/rebuild_async_stubs.js | 3 ++- core/runtime/bindings.rs | 27 +++++++++++++++++++++- ops/op2/config.rs | 2 +- ops/op2/signature.rs | 2 ++ testing/checkin/runner/ops.rs | 6 +++++ testing/ops.d.ts | 1 + testing/unit/resource_test.ts | 9 ++++++-- 8 files changed, 77 insertions(+), 16 deletions(-) diff --git a/core/00_infra.js b/core/00_infra.js index 13c82691a..b57358f57 100644 --- a/core/00_infra.js +++ b/core/00_infra.js @@ -190,8 +190,9 @@ }; } - function setUpAsyncStub(opName, originalOp) { + 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 */ @@ -200,7 +201,8 @@ fn = function async_op_0() { const id = nextPromiseId; try { - const maybeResult = originalOp(id); + // deno-fmt-ignore + const maybeResult = originalOp.call(this, id); if (maybeResult !== undefined) { return PromiseResolve(maybeResult); } @@ -219,7 +221,8 @@ fn = function async_op_1(a) { const id = nextPromiseId; try { - const maybeResult = originalOp(id, a); + // deno-fmt-ignore + const maybeResult = originalOp.call(this, id, a); if (maybeResult !== undefined) { return PromiseResolve(maybeResult); } @@ -238,7 +241,8 @@ fn = function async_op_2(a, b) { const id = nextPromiseId; try { - const maybeResult = originalOp(id, a, b); + // deno-fmt-ignore + const maybeResult = originalOp.call(this, id, a, b); if (maybeResult !== undefined) { return PromiseResolve(maybeResult); } @@ -257,7 +261,8 @@ fn = function async_op_3(a, b, c) { const id = nextPromiseId; try { - const maybeResult = originalOp(id, a, b, c); + // deno-fmt-ignore + const maybeResult = originalOp.call(this, id, a, b, c); if (maybeResult !== undefined) { return PromiseResolve(maybeResult); } @@ -276,7 +281,8 @@ fn = function async_op_4(a, b, c, d) { const id = nextPromiseId; try { - const maybeResult = originalOp(id, a, b, c, d); + // deno-fmt-ignore + const maybeResult = originalOp.call(this, id, a, b, c, d); if (maybeResult !== undefined) { return PromiseResolve(maybeResult); } @@ -295,7 +301,8 @@ fn = function async_op_5(a, b, c, d, e) { const id = nextPromiseId; try { - const maybeResult = originalOp(id, a, b, c, d, e); + // deno-fmt-ignore + const maybeResult = originalOp.call(this, id, a, b, c, d, e); if (maybeResult !== undefined) { return PromiseResolve(maybeResult); } @@ -314,7 +321,8 @@ fn = function async_op_6(a, b, c, d, e, f) { const id = nextPromiseId; try { - const maybeResult = originalOp(id, a, b, c, d, e, f); + // deno-fmt-ignore + const maybeResult = originalOp.call(this, id, a, b, c, d, e, f); if (maybeResult !== undefined) { return PromiseResolve(maybeResult); } @@ -333,7 +341,8 @@ fn = function async_op_7(a, b, c, d, e, f, g) { const id = nextPromiseId; try { - const maybeResult = originalOp(id, a, b, c, d, e, f, g); + // deno-fmt-ignore + const maybeResult = originalOp.call(this, id, a, b, c, d, e, f, g); if (maybeResult !== undefined) { return PromiseResolve(maybeResult); } @@ -352,7 +361,8 @@ fn = function async_op_8(a, b, c, d, e, f, g, h) { const id = nextPromiseId; try { - const maybeResult = originalOp(id, a, b, c, d, e, f, g, h); + // deno-fmt-ignore + const maybeResult = originalOp.call(this, id, a, b, c, d, e, f, g, h); if (maybeResult !== undefined) { return PromiseResolve(maybeResult); } @@ -371,7 +381,8 @@ fn = function async_op_9(a, b, c, d, e, f, g, h, i) { const id = nextPromiseId; try { - const maybeResult = originalOp(id, a, b, c, d, e, f, g, h, i); + // deno-fmt-ignore + const maybeResult = originalOp.call(this, id, a, b, c, d, e, f, g, h, i); if (maybeResult !== undefined) { return PromiseResolve(maybeResult); } @@ -400,6 +411,16 @@ configurable: false, writable: false, }); + + if (maybeProto) { + ObjectDefineProperty(fn, "prototype", { + value: maybeProto.prototype, + configurable: false, + writable: false, + }); + maybeProto.prototype[opName] = fn; + } + return fn; } diff --git a/core/rebuild_async_stubs.js b/core/rebuild_async_stubs.js index 7fbfdcba0..de60f2e4c 100755 --- a/core/rebuild_async_stubs.js +++ b/core/rebuild_async_stubs.js @@ -8,7 +8,8 @@ const doNotModify = function __TEMPLATE__(__ARGS_PARAM__) { const id = nextPromiseId; try { - const maybeResult = __OP__(__ARGS__); + // deno-fmt-ignore + const maybeResult = __OP__.call(this, __ARGS__); if (maybeResult !== undefined) { return PromiseResolve(maybeResult); } diff --git a/core/runtime/bindings.rs b/core/runtime/bindings.rs index 53ee8ee19..d24fe10c1 100644 --- a/core/runtime/bindings.rs +++ b/core/runtime/bindings.rs @@ -374,7 +374,14 @@ pub(crate) fn initialize_deno_core_ops_bindings<'s>( let accessor_store = create_accessor_store(method_ctxs); for method in method_ctxs.iter() { - op_ctx_template_or_accessor(&accessor_store, scope, prototype, method); + op_ctx_template_or_accessor( + &accessor_store, + set_up_async_stub_fn, + scope, + prototype, + tmpl, + method, + ); } index += decl.methods.len(); @@ -419,13 +426,31 @@ 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); let method_key = op_ctx.decl.name_fast.v8_string(scope).unwrap(); + 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.into(), op_fn.into()); diff --git a/ops/op2/config.rs b/ops/op2/config.rs index 526eb3db2..bbb033967 100644 --- a/ops/op2/config.rs +++ b/ops/op2/config.rs @@ -109,7 +109,7 @@ impl MacroConfig { .collect::>(); } else if flag == "nofast" { config.nofast = true; - } else if flag == "async" { + } else if flag == "async" || flag == "async_method" { config.r#async = true; } else if flag == "async(lazy)" { config.r#async = true; diff --git a/ops/op2/signature.rs b/ops/op2/signature.rs index 51caeb5e7..a2261f3c9 100644 --- a/ops/op2/signature.rs +++ b/ops/op2/signature.rs @@ -1259,6 +1259,8 @@ fn parse_attribute( (#[getter]) => Some(AttributeModifier::Ignore), (#[setter]) => Some(AttributeModifier::Ignore), (#[fast]) => Some(AttributeModifier::Ignore), + // async is a keyword and does not work as #[async] so we use #[async_method] instead + (#[async_method]) => Some(AttributeModifier::Ignore), (#[static_method]) => Some(AttributeModifier::Ignore), (#[constructor]) => Some(AttributeModifier::Ignore), (#[allow ($_rule:path)]) => None, diff --git a/testing/checkin/runner/ops.rs b/testing/checkin/runner/ops.rs index 102bbff48..f20a3579d 100644 --- a/testing/checkin/runner/ops.rs +++ b/testing/checkin/runner/ops.rs @@ -94,6 +94,12 @@ impl TestObjectWrap { #[fast] #[rename("with_RENAME")] fn with_rename(&self) {} + + #[async_method] + async fn with_async_fn(&self, #[smi] ms: u32) -> Result<(), AnyError> { + tokio::time::sleep(std::time::Duration::from_millis(ms as u64)).await; + Ok(()) + } } pub struct DOMPoint { diff --git a/testing/ops.d.ts b/testing/ops.d.ts index 5561fabe6..a3d6fdaa6 100644 --- a/testing/ops.d.ts +++ b/testing/ops.d.ts @@ -39,4 +39,5 @@ export class TestObjectWrap { constructor(); withVarargs(...args: any[]): number; with_RENAME(): void; + withAsyncFn(ms: number): Promise; } diff --git a/testing/unit/resource_test.ts b/testing/unit/resource_test.ts index 8c9c77ac0..89710791a 100644 --- a/testing/unit/resource_test.ts +++ b/testing/unit/resource_test.ts @@ -64,9 +64,9 @@ test(async function testCppgcAsync() { assertEquals(await op_async_get_cppgc_resource(resource), 42); }); -test(function testDomPoint() { - const p2 = new DOMPoint(); +test(async function testDomPoint() { const p1 = new DOMPoint(100, 100); + const p2 = new DOMPoint(); const p3 = DOMPoint.fromPoint({ x: 200 }); const p4 = DOMPoint.fromPoint({ x: 0, y: 100, z: 99.9, w: 100 }); const p5 = p1.fromPoint({ x: 200 }); @@ -102,4 +102,9 @@ test(function testDomPoint() { assertEquals(wrap.withVarargs(undefined), 1); wrap.with_RENAME(); + + const promise = wrap.withAsyncFn(10); + assert(promise instanceof Promise); + + await promise; });