Repository: denoland/deno_core
Pull request #1053 (2025-01-22 12:24:38)

Add `#[this] _: v8::Global<v8::Object>`
Example usage:

```rust
#[op2]
impl UnsafePointerView {
  #[cppgc]
  fn get_context(&self, #[this] this: v8::Global<v8::Object>) -> GPUCanvasContext {
    GPUCanvasContext { view: this }
  }
}

struct GPUCanvasContext {
  view: v8::Global<v8::Object>,
}

#[op2]
impl GPUCanvasContext {
  fn canvas(&self) -> v8::Global<v8::Object> { self.view.clone() }
}
```

`#[this]` works on both slow and fast calls.


Patch:
diff --git a/ops/op2/dispatch_fast.rs b/ops/op2/dispatch_fast.rs
index cb477f3..d804e14 100644
--- a/ops/op2/dispatch_fast.rs
+++ b/ops/op2/dispatch_fast.rs
@@ -757,10 +757,10 @@ fn map_v8_fastcall_arg_to_arg(
         let #arg_ident = #arg_ident.try_borrow_mut::<#state>();
       }
     }
-    Arg::VarArgs => {
-      quote! {
-        let #arg_ident = None;
-      }
+    Arg::VarArgs => quote!(let #arg_ident = None;),
+    Arg::This => {
+      *needs_fast_isolate = true;
+      quote!(let #arg_ident = deno_core::v8::Global::new(&mut #scope, this);)
     }
     Arg::String(Strings::RefStr) => {
       quote! {
@@ -886,6 +886,7 @@ fn map_arg_to_v8_fastcall_type(
     | Arg::Ref(RefType::Ref, Special::JsRuntimeState)
     | Arg::State(..)
     | Arg::VarArgs
+    | Arg::This
     | Arg::Special(Special::Isolate)
     | Arg::OptionState(..) => V8FastCallType::Virtual,
     // Other types + ref types are not handled
diff --git a/ops/op2/dispatch_slow.rs b/ops/op2/dispatch_slow.rs
index feb7bfb..f545899 100644
--- a/ops/op2/dispatch_slow.rs
+++ b/ops/op2/dispatch_slow.rs
@@ -502,6 +502,12 @@ pub fn from_arg(
         let #arg_ident = Some(&#fn_args);
       })
     }
+    Arg::This => {
+      *needs_isolate = true;
+      gs_quote!(generator_state(scope, fn_args) => {
+        let #arg_ident = deno_core::v8::Global::new(&mut #scope, #fn_args.this());
+      })
+    }
     Arg::Buffer(buffer_type, mode, source) => {
       // Explicit temporary lifetime extension so we can take a reference
       let temp = format_ident!("{}_temp", arg_ident);
diff --git a/ops/op2/signature.rs b/ops/op2/signature.rs
index 3e09841..13cfc34 100644
--- a/ops/op2/signature.rs
+++ b/ops/op2/signature.rs
@@ -300,6 +300,7 @@ pub enum Arg {
   ToV8(String),
   WebIDL(String, Vec<WebIDLPairs>, Option<WebIDLDefault>),
   VarArgs,
+  This,
 }
 
 impl Arg {
@@ -373,7 +374,7 @@ impl Arg {
         | Special::HandleScope,
       ) => true,
       Self::State(..) | Self::OptionState(..) => true,
-      Self::VarArgs => true,
+      Self::This | Self::VarArgs => true,
       _ => false,
     }
   }
@@ -787,6 +788,8 @@ pub enum AttributeModifier {
   Ignore,
   /// Varaible-length arguments.
   VarArgs,
+  /// The `this` receiver.
+  This,
 }
 
 impl AttributeModifier {
@@ -806,6 +809,7 @@ impl AttributeModifier {
       AttributeModifier::CppGcResource => "cppgc",
       AttributeModifier::Ignore => "ignore",
       AttributeModifier::VarArgs => "varargs",
+      AttributeModifier::This => "this",
     }
   }
 }
@@ -1278,6 +1282,7 @@ fn parse_attribute(
       (#[arraybuffer(copy)]) => Some(AttributeModifier::Buffer(BufferMode::Copy, BufferSource::ArrayBuffer)),
       (#[arraybuffer(detach)]) => Some(AttributeModifier::Buffer(BufferMode::Detach, BufferSource::ArrayBuffer)),
       (#[global]) => Some(AttributeModifier::Global),
+      (#[this]) => Some(AttributeModifier::This),
       (#[cppgc]) => Some(AttributeModifier::CppGcResource),
       (#[to_v8]) => Some(AttributeModifier::ToV8),
       (#[from_v8]) => Some(AttributeModifier::FromV8),
@@ -1650,6 +1655,15 @@ pub(crate) fn parse_type(
       | AttributeModifier::Global => {
         // We handle this as part of the normal parsing process
       }
+      AttributeModifier::This => {
+        if position == Position::RetVal {
+          return Err(ArgError::InvalidAttributePosition(
+            primary.name(),
+            "argument",
+          ));
+        }
+        return Ok(Arg::This);
+      }
       AttributeModifier::Number => match ty {
         Type::Path(of) => {
           match parse_type_path(


Test patch:
diff --git a/testing/checkin/runner/ops.rs b/testing/checkin/runner/ops.rs
index 83b7c5b..a8cab08 100644
--- a/testing/checkin/runner/ops.rs
+++ b/testing/checkin/runner/ops.rs
@@ -96,6 +96,9 @@ impl TestObjectWrap {
   #[rename("with_RENAME")]
   fn with_rename(&self) {}
 
+  #[fast]
+  fn with_this(&self, #[this] _: v8::Global<v8::Object>) {}
+
   #[async_method]
   async fn with_async_fn(&self, #[smi] ms: u32) -> Result<(), JsErrorBox> {
     tokio::time::sleep(std::time::Duration::from_millis(ms as u64)).await;
diff --git a/testing/ops.d.ts b/testing/ops.d.ts
index e603d0b..008d837 100644
--- a/testing/ops.d.ts
+++ b/testing/ops.d.ts
@@ -41,6 +41,7 @@ export class TestObjectWrap {
   withVarargs(...args: any[]): number;
   with_RENAME(): void;
   withAsyncFn(ms: number): Promise<void>;
+  withThis(): void;
 }
 
 export class TestEnumWrap {}
diff --git a/testing/unit/resource_test.ts b/testing/unit/resource_test.ts
index 3819239..0214f91 100644
--- a/testing/unit/resource_test.ts
+++ b/testing/unit/resource_test.ts
@@ -104,6 +104,7 @@ test(async function testDomPoint() {
   assertEquals(wrap.withVarargs(), 0);
   assertEquals(wrap.withVarargs(undefined), 1);
 
+  wrap.withThis();
   wrap.with_RENAME();
 
   const promise = wrap.withAsyncFn(10);