deno_core icon indicating copy to clipboard operation
deno_core copied to clipboard

op2 object wraps

Open littledivy opened this issue 1 year ago • 4 comments
trafficstars

Object wrap for Cppgc-backed objects

#[op2] will generate the glue code declarations for impl blocks to create JS objects in Rust using the op2 infra.

deno_core::extension!(
   // ...
  objects = [MyObject],
)

Currently supported bindings:

  • constructor
  • methods
  • static methods

Planned support:

  • getters
  • setters

Future:

  • webidl validation

Example:

// dompoint.rs
pub struct DOMPoint {
  x: f64,
  y: f64,
  z: f64,
  w: f64,
}

impl GarbageCollected for DOMPoint {}

#[op2]
impl DOMPoint {
  #[constructor]
  #[cppgc]
  fn new(
    x: Option<f64>,
    y: Option<f64>,
    z: Option<f64>,
    w: Option<f64>,
  ) -> DOMPoint {
    DOMPoint {
      x: x.unwrap_or(0.0),
      y: y.unwrap_or(0.0),
      z: z.unwrap_or(0.0),
      w: w.unwrap_or(0.0),
    }
  }

  #[static_method]
  #[cppgc]
  fn from_point(
    scope: &mut v8::HandleScope,
    other: v8::Local<v8::Object>,
  ) -> Result<DOMPoint, AnyError> {
    fn get(
      scope: &mut v8::HandleScope,
      other: v8::Local<v8::Object>,
      key: &str,
    ) -> Option<f64> {
      let key = v8::String::new(scope, key).unwrap();
      other
        .get(scope, key.into())
        .map(|x| x.to_number(scope).unwrap().value())
    }

    Ok(DOMPoint {
      x: get(scope, other, "x").unwrap_or(0.0),
      y: get(scope, other, "y").unwrap_or(0.0),
      z: get(scope, other, "z").unwrap_or(0.0),
      w: get(scope, other, "w").unwrap_or(0.0),
    })
  }

  #[fast]
  fn x(&self) -> f64 {
    self.x
  }
}
import { DOMPoint } from "ext:core/ops";

const p = new DOMPoint(200, 300);
p.x(); // 200.0

littledivy avatar Jul 04 '24 08:07 littledivy

Codecov Report

Attention: Patch coverage is 94.11765% with 18 lines in your changes missing coverage. Please review.

Project coverage is 81.47%. Comparing base (0c7f83e) to head (f6b3ca7). Report is 163 commits behind head on main.

Files with missing lines Patch % Lines
testing/checkin/runner/ops.rs 81.81% 8 Missing :warning:
ops/op2/dispatch_fast.rs 37.50% 5 Missing :warning:
ops/op2/object_wrap.rs 96.87% 2 Missing :warning:
core/extensions.rs 90.90% 1 Missing :warning:
ops/op2/config.rs 90.90% 1 Missing :warning:
ops/op2/dispatch_slow.rs 87.50% 1 Missing :warning:
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #805      +/-   ##
==========================================
+ Coverage   81.43%   81.47%   +0.04%     
==========================================
  Files          97       98       +1     
  Lines       23877    25257    +1380     
==========================================
+ Hits        19445    20579    +1134     
- Misses       4432     4678     +246     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

codecov-commenter avatar Jul 04 '24 09:07 codecov-commenter

Have you done any performance benchmarks?

ry avatar Jul 06 '24 02:07 ry

$ target/release/dcore bench.js
JS   x(): 87ms
Rust x(): 285ms
JS   create: 904ms
Rust create: 1706ms
Benchmark
const { DOMPoint } = Deno.core.ops;

function bench(name, fn, iterations = 1e7) {
  var start = Date.now();
  for (var i = 0; i < iterations; i++) {
    fn();
  }
  var end = Date.now();
  console.log(name + ': ' + (end - start) + 'ms');
}

class DOMPointJS {
  #x;
  #y;
  #z;
  #w;

  constructor(x, y, z, w) {
    this.#x = x || 0;
    this.#y = y || 0;
    this.#z = z || 0;
    this.#w = w || 1;
  }

  x() {
    return this.#x;
  }
}

const p = new DOMPointJS(100, 200);
function getXJS() {
   return p.x()
}

const p2 = new DOMPoint(100, 200);
function getXRust() {
   return p2.x();
}

bench('JS   x()', getXJS);
bench('Rust x()', getXRust);

function createPointJS() {
  return new DOMPointJS(100, 200);
}

function createPointRust() {
   return new DOMPoint(100, 200);
}

bench('JS   create', createPointJS);
bench('Rust create', createPointRust);

So...not great atm, working on it. Object::wrap and Object::unwrap can take Isolate instead of HandleScopes so we can avoid creating and dropping scope in most cases - which is 30% of the overhead.

littledivy avatar Jul 06 '24 04:07 littledivy

Update benchmarks:

JS   x(): 84ms
Rust x(): 191ms
JS   create: 953ms
Rust create: 1810ms

littledivy avatar Aug 15 '24 05:08 littledivy