denoland / deno_core

The core engine at the heart of Deno
MIT License
233 stars 76 forks source link

op2 object wraps #805

Open littledivy opened 6 days ago

littledivy commented 6 days ago

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:

Future:

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
codecov-commenter commented 6 days ago

Codecov Report

Attention: Patch coverage is 95.63636% with 12 lines in your changes missing coverage. Please review.

Project coverage is 81.89%. Comparing base (0c7f83e) to head (19e91c6). Report is 50 commits behind head on main.

Files Patch % Lines
testing/checkin/runner/ops.rs 82.22% 8 Missing :warning:
core/extensions.rs 87.50% 1 Missing :warning:
ops/op2/config.rs 90.90% 1 Missing :warning:
ops/op2/dispatch_slow.rs 85.71% 1 Missing :warning:
ops/op2/object_wrap.rs 98.33% 1 Missing :warning:
Additional details and impacted files ```diff @@ Coverage Diff @@ ## main #805 +/- ## ========================================== + Coverage 81.43% 81.89% +0.45% ========================================== Files 97 99 +2 Lines 23877 24565 +688 ========================================== + Hits 19445 20117 +672 - Misses 4432 4448 +16 ```

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

ry commented 5 days ago

Have you done any performance benchmarks?

littledivy commented 5 days ago
$ target/release/dcore bench.js
JS   x(): 87ms
Rust x(): 285ms
JS   create: 904ms
Rust create: 1706ms
Benchmark ```js 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.