Skip to content

Commit

Permalink
Support async methods in object wrap (#1013)
Browse files Browse the repository at this point in the history
```rust
impl TestObjectWrap {
  #[async_method]
  async fn with_async_fn(&self)  {
    // ...
  }
}
```
  • Loading branch information
littledivy authored Dec 31, 2024
1 parent 18d4ed3 commit 56c88a8
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 16 deletions.
43 changes: 32 additions & 11 deletions core/00_infra.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
}

Expand Down
3 changes: 2 additions & 1 deletion core/rebuild_async_stubs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
27 changes: 26 additions & 1 deletion core/runtime/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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<v8::Function>,
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());

Expand Down
2 changes: 1 addition & 1 deletion ops/op2/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl MacroConfig {
.collect::<Vec<_>>();
} 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;
Expand Down
2 changes: 2 additions & 0 deletions ops/op2/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions testing/checkin/runner/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions testing/ops.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ export class TestObjectWrap {
constructor();
withVarargs(...args: any[]): number;
with_RENAME(): void;
withAsyncFn(ms: number): Promise<void>;
}
9 changes: 7 additions & 2 deletions testing/unit/resource_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Expand Down Expand Up @@ -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;
});

0 comments on commit 56c88a8

Please sign in to comment.