Repository: denoland/deno_core
Pull request #737 (2024-05-10 21:30:26)

chore: ARCHITECTURE.md
This is a first pass at `ARCHITECTURE.md`, a high-level overview of how `deno_core` works.


Patch:
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
new file mode 100644
index 0000000..4e09752
--- /dev/null
+++ b/ARCHITECTURE.md
@@ -0,0 +1,163 @@
+# Architecture
+
+## JsRuntime
+
+The heart of Deno is the `JsRuntime`, which wraps the V8 concepts of an
+`Isolate` and a `Context`. To create a runtime, an embedder instantiates a
+`JsRuntime` using a set of `extension`s, and then poll the event loop until the
+runtime has completed.
+
+Each `extension` consists of a number of JavaScript- or TypeScript-language
+ECMAScript modules, as well as a number of `op`s. This module code is loaded
+when the runtime is created, allowing user code to access whatever system
+services an embedder has decided to provide.
+
+The `op`s provided by `extension`s allow for simple and fast JavaScript-to-Rust
+function calls. An `op` may be sync or async (which makes it sync or async in
+both the Rust and JavaScript world), and fallible or infallible. Most
+fundamental V8 and Rust types are supported for `op` calls, and the system will
+generally choose the most performant way to transport data regardless of type.
+
+The runtime also provides persistent state in a number of forms: a
+general-purpose `OpState` used by Rust code to stash state, `Resource`s that can
+be read and written by both script and Rust code, and `cppgc` objects that can
+be accessed by the Rust side and held on the JavaScript side (but not directly
+accessed).
+
+## Runtime Services
+
+By default, v8 does not provide a fully-functional runtime. Many of the required
+pieces to get a useful runtime are provided by the embedder. `deno_core`
+provides a number of these useful services for you, including:
+
+0. Extensions and `op`s
+1. Resource management
+2. Error and promise rejection handling
+3. Event loop callbacks
+4. Timers
+5. Basic console support
+6. Snapshot management
+
+## Extensions and `op`s
+
+Extensions provide additional functionality to Deno beyond the core runtime.
+They can include `op`s for interacting with the runtime, ESM modules for
+JavaScript code, and other features like configuration parameters and middleware
+functions.
+
+Extensions are defined using the `extension!` macro in `deno_core`. The options
+available for defining an extension using the `extension!` macro allow for
+fine-grained control over its behavior and dependencies.
+
+When the runtime is initialized, it creates a V8 isolate and context, and then
+instantiates the provided extension code and ops into the new context. The ops
+are provided in a special, virtual module that can be imported from the
+extension module code, allowing embedders to create fully-fledged APIs that are
+backed by a combination of Rust, JavaScript or both types of code.
+
+In general, APIs for user code are provided in one of two forms: global APIs,
+available on `globalThis` (the equivalent of the `window` object in browsers),
+and module-style APIs.
+
+In the former style of API, the modules can add APIs to `globalThis` directly:
+
+```ts
+globalThis.Deno.myAPI = function myAPI() {
+  op_do_something();
+};
+```
+
+In the latter style of API, modules can be given friendly names in the
+`extension` definition, and user code can import them directly:
+
+```rust
+extension!(
+    ...
+    esm = [ "brand:api_module": "api.js" ]
+);
+```
+
+```ts
+import { myAPI } from "brand:api_module";
+myAPI();
+```
+
+## Resource management
+
+Deno provides the equivalent of system streams in the form of `Resource`s. A
+`Resource` is a Rust struct that implements a `Resource` trait. This trait
+allows for explicit lifetime management: a unique integer value is associated
+with the struct that can be used to explicitly close the resource when not
+needed.
+
+A `Resource` is instantiated inside of an `op`, and the integer handle is
+returned to the calling code.
+
+The `close` and `tryClose` operations are available as a `Deno.core` JS API, and
+both take the integer ID of the resource.
+
+A `Resource` may or may not implement stream read and write operations. These
+are exposed via `read*` and `write*` APIs on `Deno.core`, and the APIs are
+mapped to the `Resource`'s read and write trait methods.
+
+## Error and promise rejection handling
+
+Embedders make use of two callbacks for unhandled promise rejections and
+uncaught errors: `setReportExceptionCallback`,
+`setUnhandledPromiseRejectionHandler`.
+
+When an ES Module throws an exception at the top level, or code in a callback or
+promise continuation throws an exception that is not handled, `Deno.core`
+detects this as an unhandled exception and routes it to the
+`setReportExceptionCallback` or `setUnhandledPromiseRejectionHandler` callbacks.
+
+`Deno.core` automatically handles errors and promise rejects that bubble out of
+user code and into the top-level module and rejects the promise for the module
+itself.
+
+If an error ends up bubbling up to `reportUnhandledException` or
+`reportUnhandledPromiseRejection`, that error is exposed through the runtime's
+polling interface and runtime execution is halted.
+
+Custom error classes may be registered via the `registerErrorClass` API,
+allowing for Rust code to throw strongly-typed exceptions to user JS code. The
+`custom_error` function in `deno_core` may be used to return these custom error
+classes.
+
+## Event loop callbacks
+
+In async JavaScript, forward progress is made for module and promise resolution
+through event loop callbacks. These callbacks may be async `op` resolution,
+timer resolution, module loads, or internal callbacks from NAPI or FFI code.
+
+Event loop callbacks generally happen through `eventLoopTick` for convenience,
+though some other events are dispatched directly. The `eventLoopTick` method
+will often appear in stack traces.
+
+In `JsRuntime`, `do_js_event_loop_tick_realm` polls the various possible sources
+of forward progress and routes them to the V8 engine. While `deno_core` may
+allow for these async operations to complete at any time, the results are only
+forwarded to the V8 engine during the event dispatch phase of the event loop,
+preventing callbacks into the engine at unexpected times.
+
+## Timers
+
+`deno_core` provides a number of high-level timer support APIs for embedders to
+build on. `queueUserTimer`, `refTimer` and `unrefTimer` are sufficient to
+implement both web-compatible and node-compatible timers.
+
+Timers are dispatched as part of the `eventLoopTick` infrastructure, however
+this may change in the future.
+
+## Basic console support
+
+`deno_core` provides a basic implementation of the V8 console that integrates
+with the V8 inspector. Embedders are encouraged to supply their own more
+functional console implementation, however.
+
+## Snapshot management
+
+For embedders that wish to optimize `JsRuntime` startup, `JsRuntimeForSnapshot`
+allows for a runtime to be created that will be serialized to a V8 snapshot.
+This snapshot data may be passed to a `JsRuntime` as it boots, allowing it to
+pre-initialize a V8 isolate and context from earlier initialization work.
diff --git a/Cargo.lock b/Cargo.lock
index 013b5ac..41ff3b3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -511,7 +511,7 @@ dependencies = [
 
 [[package]]
 name = "deno_core"
-version = "0.280.0"
+version = "0.282.0"
 dependencies = [
  "anyhow",
  "bencher",
@@ -578,7 +578,7 @@ dependencies = [
 
 [[package]]
 name = "deno_ops"
-version = "0.156.0"
+version = "0.158.0"
 dependencies = [
  "pretty_assertions",
  "prettyplease",
@@ -1756,7 +1756,7 @@ dependencies = [
 
 [[package]]
 name = "serde_v8"
-version = "0.189.0"
+version = "0.191.0"
 dependencies = [
  "bencher",
  "num-bigint",
@@ -2636,9 +2636,9 @@ dependencies = [
 
 [[package]]
 name = "v8"
-version = "0.91.1"
+version = "0.92.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69026e2e8af55a4d2f20c0c17f690e8b31472bf76ab75b1205d3a0fab60c8f84"
+checksum = "234589219e37a7496cbce73d971586db8369871be2420372c45a579b6a919b15"
 dependencies = [
  "bitflags",
  "fslock",
diff --git a/Cargo.toml b/Cargo.toml
index b2cf88e..f1c0d5a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,12 +19,12 @@ repository = "https://github.com/denoland/deno_core"
 
 [workspace.dependencies]
 # Local dependencies
-deno_core = { version = "0.280.0", path = "./core" }
-deno_ops = { version = "0.156.0", path = "./ops" }
-serde_v8 = { version = "0.189.0", path = "./serde_v8" }
+deno_core = { version = "0.282.0", path = "./core" }
+deno_ops = { version = "0.158.0", path = "./ops" }
+serde_v8 = { version = "0.191.0", path = "./serde_v8" }
 deno_core_testing = { path = "./testing" }
 
-v8 = { version = "0.91.1", default-features = false }
+v8 = { version = "0.92.0", default-features = false }
 deno_ast = { version = "=0.35.3", features = ["transpiling"] }
 deno_unsync = "0.3.2"
 deno_core_icudata = "0.0.73"
diff --git a/core/Cargo.toml b/core/Cargo.toml
index e77ddb5..100d100 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -2,7 +2,7 @@
 
 [package]
 name = "deno_core"
-version = "0.280.0"
+version = "0.282.0"
 authors.workspace = true
 edition.workspace = true
 license.workspace = true
diff --git a/core/extension_set.rs b/core/extension_set.rs
index c56c7f4..938a771 100644
--- a/core/extension_set.rs
+++ b/core/extension_set.rs
@@ -63,6 +63,7 @@ pub fn init_ops(
   for core_op in deno_core_ops {
     ops.push(OpDecl {
       name: core_op.name,
+      name_fast: core_op.name_fast,
       ..macroware(*core_op)
     });
   }
@@ -72,6 +73,7 @@ pub fn init_ops(
     for ext_op in ext_ops {
       ops.push(OpDecl {
         name: ext_op.name,
+        name_fast: ext_op.name_fast,
         ..macroware(*ext_op)
       });
     }
diff --git a/core/modules/map.rs b/core/modules/map.rs
index 773953b..743b73b 100644
--- a/core/modules/map.rs
+++ b/core/modules/map.rs
@@ -1090,12 +1090,27 @@ impl ModuleMap {
           .then2(tc_scope, on_fulfilled.unwrap(), on_rejected.unwrap())
           .is_none()
       {
-        // The runtime might have been shut down, so we need to synthesize a promise result here
+        // There are two reasons we could be here:
+        // 1. The runtime is shutting down, and JS ops are disabled with termination exceptions.
+        // 2. User code has tampered with the runtime globals in some way that prevents us from
+        //    attaching `on_fulfilled`/`on_rejected` to `promise`.
+        // In these cases we still need to report something back, so synthesize the result from the
+        // promise.
+
+        // Unset pending mod evaluation as the handlers will never run. See debug_assert below.
+        self.pending_mod_evaluation.set(false);
+
         let mut sender = get_sender(evaluation.into());
         match promise.state() {
           PromiseState::Fulfilled => {
-            // Module loaded OK
-            sender.notify(tc_scope);
+            if let Some(exception) = tc_scope.exception() {
+              _ = sender.sender.take().unwrap().send(exception_to_err_result(
+                tc_scope, exception, true, false,
+              ));
+            } else {
+              // Module loaded OK
+              sender.notify(tc_scope);
+            }
           }
           PromiseState::Rejected => {
             // Module was rejected
@@ -1104,6 +1119,11 @@ impl ModuleMap {
             _ = sender.sender.take().unwrap().send(Err(err.into()));
           }
           PromiseState::Pending => {
+            // User code shouldn't be able to both cause the runtime to fail and leave the promise as
+            // pending because the only way to adopt a pending promise is to use `await` and
+            // `await` won't work if you've broken the runtime in such a way that `promise::then`
+            // didn't work.
+            debug_assert!(tc_scope.is_execution_terminating());
             // Module pending, just drop the sender at this point -- we can't do anything with a shut-down runtime.
             drop(sender);
           }
diff --git a/ops/Cargo.toml b/ops/Cargo.toml
index 04fd852..aa75b2a 100644
--- a/ops/Cargo.toml
+++ b/ops/Cargo.toml
@@ -2,7 +2,7 @@
 
 [package]
 name = "deno_ops"
-version = "0.156.0"
+version = "0.158.0"
 authors.workspace = true
 edition.workspace = true
 license.workspace = true
diff --git a/serde_v8/Cargo.toml b/serde_v8/Cargo.toml
index 4589087..b4bb75f 100644
--- a/serde_v8/Cargo.toml
+++ b/serde_v8/Cargo.toml
@@ -2,7 +2,7 @@
 
 [package]
 name = "serde_v8"
-version = "0.189.0"
+version = "0.191.0"
 authors.workspace = true
 edition.workspace = true
 license.workspace = true
@@ -28,11 +28,3 @@ serde_v8_utilities = { path = "./utilities" }
 
 [[example]]
 name = "basic"
-
-[[bench]]
-name = "de"
-harness = false
-
-[[bench]]
-name = "ser"
-harness = false
diff --git a/serde_v8/benches/de.rs b/serde_v8/benches/de.rs
deleted file mode 100644
index 46e307e..0000000
--- a/serde_v8/benches/de.rs
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use bencher::benchmark_group;
-use bencher::benchmark_main;
-use bencher::Bencher;
-
-use serde::Deserialize;
-
-use serde_v8::ByteString;
-use serde_v8_utilities::js_exec;
-use serde_v8_utilities::v8_do;
-
-#[derive(Debug, Deserialize, PartialEq)]
-struct MathOp {
-  arg1: u64,
-  arg2: u64,
-  operator: Option<String>,
-}
-
-fn dedo(
-  code: &str,
-  f: impl FnOnce(&mut v8::HandleScope, v8::Local<v8::Value>),
-) {
-  v8_do(|| {
-    let isolate = &mut v8::Isolate::new(v8::CreateParams::default());
-    let handle_scope = &mut v8::HandleScope::new(isolate);
-    let context = v8::Context::new(handle_scope);
-    let scope = &mut v8::ContextScope::new(handle_scope, context);
-    let v = js_exec(scope, code);
-
-    f(scope, v);
-  })
-}
-
-fn dedo_json(code: &str, f: impl FnOnce(String)) {
-  let code = format!("JSON.stringify({code})");
-  dedo(&code[..], |scope, v| {
-    let s: String = serde_v8::from_v8(scope, v).unwrap();
-    f(s);
-  })
-}
-
-fn de_struct_v8(b: &mut Bencher) {
-  dedo("({arg1: 10, arg2: 123 })", |scope, obj| {
-    let mut total = 0;
-    b.iter(move || {
-      let op: MathOp = serde_v8::from_v8(scope, obj).unwrap();
-      total = total + op.arg1 + op.arg2;
-    });
-  });
-}
-
-fn de_struct_v8_opt(b: &mut Bencher) {
-  dedo("({arg1: 10, arg2: 123 })", |scope, v| {
-    let k_arg1 = v8::String::new(scope, "arg1").unwrap().into();
-    let k_arg2 = v8::String::new(scope, "arg2").unwrap().into();
-    let obj = v8::Local::<v8::Object>::try_from(v).unwrap();
-    let mut total = 0;
-    b.iter(move || {
-      let v_arg1 = obj.get(scope, k_arg1).unwrap();
-      let v_arg2 = obj.get(scope, k_arg2).unwrap();
-      let op = MathOp {
-        arg1: serde_v8::from_v8(scope, v_arg1).unwrap(),
-        arg2: serde_v8::from_v8(scope, v_arg2).unwrap(),
-        operator: None,
-      };
-      total = total + op.arg1 + op.arg2;
-    });
-  });
-}
-
-fn de_struct_json(b: &mut Bencher) {
-  dedo_json("({arg1: 10, arg2: 123 })", |s| {
-    let mut total = 0;
-    b.iter(move || {
-      let op: MathOp = serde_json::from_str(&s).unwrap();
-      total = total + op.arg1 + op.arg2;
-    });
-  });
-}
-
-fn de_struct_json_deopt(b: &mut Bencher) {
-  // JSON.stringify() in loop (semi-simulating ABI loop)
-  dedo("({arg1: 10, arg2: 123 })", |scope, obj| {
-    let mut total = 0;
-    b.iter(move || {
-      let mut scope = v8::HandleScope::new(scope);
-      let s = v8::json::stringify(&mut scope, obj).unwrap();
-      let rs = s.to_rust_string_lossy(&mut scope);
-      let op: MathOp = serde_json::from_str(&rs).unwrap();
-      total = total + op.arg1 + op.arg2;
-    });
-  });
-}
-
-macro_rules! dualbench {
-  ($v8_fn:ident, $json_fn:ident, $src:expr, $t:ty) => {
-    fn $v8_fn(b: &mut Bencher) {
-      dedo($src, |scope, v| {
-        b.iter(move || {
-          let _: $t = serde_v8::from_v8(scope, v).unwrap();
-        });
-      });
-    }
-
-    fn $json_fn(b: &mut Bencher) {
-      dedo_json($src, |s| {
-        b.iter(move || {
-          let _: $t = serde_json::from_str(&s).unwrap();
-        });
-      });
-    }
-  };
-}
-
-dualbench!(de_bool_v8, de_bool_json, "true", bool);
-dualbench!(de_int_v8, de_int_json, "12345", u32);
-dualbench!(
-  de_array_v8,
-  de_array_json,
-  "[1,2,3,4,5,6,7,8,9,10]",
-  Vec<u32>
-);
-dualbench!(de_str_v8, de_str_json, "'hello world'", String);
-dualbench!(de_tuple_v8, de_tuple_json, "[1,false]", (u8, bool));
-
-fn de_tuple_v8_opt(b: &mut Bencher) {
-  dedo("[1,false]", |scope, obj| {
-    let arr = v8::Local::<v8::Array>::try_from(obj).unwrap();
-    let obj = v8::Local::<v8::Object>::from(arr);
-
-    b.iter(move || {
-      let v1 = obj.get_index(scope, 0).unwrap();
-      let v2 = obj.get_index(scope, 1).unwrap();
-      let _: (u8, bool) = (
-        serde_v8::from_v8(scope, v1).unwrap(),
-        serde_v8::from_v8(scope, v2).unwrap(),
-      );
-    });
-  });
-}
-
-fn de_bstr_v8_12_b(b: &mut Bencher) {
-  dedo(r#""hello world\n""#, |scope, v| {
-    b.iter(move || {
-      let _: ByteString = serde_v8::from_v8(scope, v).unwrap();
-    });
-  });
-}
-
-fn de_bstr_v8_1024_b(b: &mut Bencher) {
-  dedo(
-    r#""hello world\n".repeat(1e2).slice(0, 1024)"#,
-    |scope, v| {
-      b.iter(move || {
-        let _: ByteString = serde_v8::from_v8(scope, v).unwrap();
-      });
-    },
-  );
-}
-
-fn de_sob_str_6b(b: &mut Bencher) {
-  dedo("'byebye'", |scope, v| {
-    b.iter(move || {
-      let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
-    })
-  })
-}
-
-fn de_sob_str_1kb(b: &mut Bencher) {
-  dedo("'deno'.repeat(256)", |scope, v| {
-    b.iter(move || {
-      let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
-    })
-  })
-}
-
-fn de_sob_buf_1b(b: &mut Bencher) {
-  dedo("new Uint8Array([97])", |scope, v| {
-    b.iter(move || {
-      let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
-    })
-  })
-}
-
-fn de_sob_buf_1kb(b: &mut Bencher) {
-  dedo("(new Uint8Array(1*1024)).fill(42)", |scope, v| {
-    b.iter(move || {
-      let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
-    })
-  })
-}
-
-fn de_sob_buf_16kb(b: &mut Bencher) {
-  dedo("(new Uint8Array(16*1024)).fill(42)", |scope, v| {
-    b.iter(move || {
-      let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
-    })
-  })
-}
-
-fn de_sob_buf_512kb(b: &mut Bencher) {
-  dedo("(new Uint8Array(512*1024)).fill(42)", |scope, v| {
-    b.iter(move || {
-      let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
-    })
-  })
-}
-
-benchmark_group!(
-  benches,
-  de_struct_v8,
-  de_struct_v8_opt,
-  de_struct_json,
-  de_struct_json_deopt,
-  de_bool_v8,
-  de_bool_json,
-  de_int_v8,
-  de_int_json,
-  de_array_v8,
-  de_array_json,
-  de_str_v8,
-  de_str_json,
-  de_tuple_v8,
-  de_tuple_json,
-  de_tuple_v8_opt,
-  de_bstr_v8_12_b,
-  de_bstr_v8_1024_b,
-  de_sob_str_6b,
-  de_sob_str_1kb,
-  de_sob_buf_1b,
-  de_sob_buf_1kb,
-  de_sob_buf_16kb,
-  de_sob_buf_512kb,
-);
-
-benchmark_main!(benches);
diff --git a/serde_v8/benches/ser.rs b/serde_v8/benches/ser.rs
deleted file mode 100644
index 8423dc4..0000000
--- a/serde_v8/benches/ser.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use bencher::benchmark_group;
-use bencher::benchmark_main;
-use bencher::Bencher;
-
-use serde::Serialize;
-
-use serde_v8::ByteString;
-use serde_v8_utilities::v8_do;
-
-#[derive(Serialize)]
-struct MathOp {
-  arg1: u64,
-  arg2: u64,
-  operator: Option<String>,
-}
-
-fn serdo(f: impl FnOnce(&mut v8::HandleScope)) {
-  v8_do(|| {
-    let isolate = &mut v8::Isolate::new(v8::CreateParams::default());
-    let handle_scope = &mut v8::HandleScope::new(isolate);
-    let context = v8::Context::new(handle_scope);
-    let scope = &mut v8::ContextScope::new(handle_scope, context);
-
-    f(scope);
-  })
-}
-
-macro_rules! dualbench {
-  ($v8_fn:ident, $json_fn:ident, $src:expr) => {
-    fn $v8_fn(b: &mut Bencher) {
-      serdo(|scope| {
-        let v = $src;
-        b.iter(move || {
-          let _ = serde_v8::to_v8(scope, &v).unwrap();
-        });
-      });
-    }
-
-    fn $json_fn(b: &mut Bencher) {
-      let v = $src;
-      b.iter(move || {
-        let _ = serde_json::to_string(&v).unwrap();
-      });
-    }
-  };
-}
-
-dualbench!(
-  ser_struct_v8,
-  ser_struct_json,
-  MathOp {
-    arg1: 10,
-    arg2: 123,
-    operator: None
-  }
-);
-dualbench!(ser_bool_v8, ser_bool_json, true);
-dualbench!(ser_int_v8, ser_int_json, 12345);
-dualbench!(
-  ser_array_v8,
-  ser_array_json,
-  vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-);
-dualbench!(ser_str_v8, ser_str_json, "hello world");
-dualbench!(ser_tuple_v8, ser_tuple_json, (1, false));
-
-fn ser_struct_v8_manual(b: &mut Bencher) {
-  serdo(|scope| {
-    let v = MathOp {
-      arg1: 10,
-      arg2: 123,
-      operator: None,
-    };
-    b.iter(|| {
-      let obj = v8::Object::new(scope);
-      let k1 = v8::String::new(scope, "arg1").unwrap();
-      let k2 = v8::String::new(scope, "arg2").unwrap();
-      let k3 = v8::String::new(scope, "operator").unwrap();
-      //    let k1 = v8::String::new_from_utf8(scope, "arg1".as_ref(), v8::NewStringType::Internalized).unwrap();
-      //    let k2 = v8::String::new_from_utf8(scope, "arg2".as_ref(), v8::NewStringType::Internalized).unwrap();
-      //    let k3 = v8::String::new_from_utf8(scope, "operator".as_ref(), v8::NewStringType::Internalized).unwrap();
-      let v1 = v8::Number::new(scope, v.arg1 as f64);
-      let v2 = v8::Number::new(scope, v.arg2 as f64);
-      let v3 = v8::null(scope);
-      obj.set(scope, k1.into(), v1.into()).unwrap();
-      obj.set(scope, k2.into(), v2.into()).unwrap();
-      obj.set(scope, k3.into(), v3.into()).unwrap();
-    });
-  });
-}
-
-fn ser_bstr_12_b(b: &mut Bencher) {
-  serdo(|scope| {
-    let bstr = ByteString::from("hello world\n");
-    b.iter(|| {
-      let _ = serde_v8::to_v8(scope, &bstr).unwrap();
-    });
-  });
-}
-
-fn ser_bstr_1024_b(b: &mut Bencher) {
-  serdo(|scope| {
-    let mut s = "hello world\n".repeat(100);
-    s.truncate(1024);
-    let bstr = ByteString::from(s);
-    b.iter(|| {
-      let _ = serde_v8::to_v8(scope, &bstr).unwrap();
-    });
-  });
-}
-
-benchmark_group!(
-  benches,
-  ser_struct_v8,
-  ser_struct_json,
-  ser_bool_v8,
-  ser_bool_json,
-  ser_int_v8,
-  ser_int_json,
-  ser_array_v8,
-  ser_array_json,
-  ser_str_v8,
-  ser_str_json,
-  ser_tuple_v8,
-  ser_tuple_json,
-  ser_struct_v8_manual,
-  ser_bstr_12_b,
-  ser_bstr_1024_b,
-);
-benchmark_main!(benches);


Test patch:
diff --git a/.github/workflows/ci-test-valgrind/suppressions.txt b/.github/workflows/ci-test-valgrind/suppressions.txt
index 57f02a2..dcdffa9 100644
--- a/.github/workflows/ci-test-valgrind/suppressions.txt
+++ b/.github/workflows/ci-test-valgrind/suppressions.txt
@@ -14,128 +14,16 @@
 {
    <insert_a_suppression_name_here>
    Memcheck:Cond
-   fun:_ZN4heap4base12_GLOBAL__N_122IteratePointersInStackEPNS0_12StackVisitorEPKvS5_S5_
-   fun:_ZNK4heap4base5Stack26IteratePointersUntilMarkerEPNS0_12StackVisitorE
-   fun:_ZN5cppgc8internal10MarkerBase10VisitRootsENS_18EmbedderStackStateE
-   fun:_ZN5cppgc8internal10MarkerBase16EnterAtomicPauseENS_18EmbedderStackStateE
-   fun:_ZN2v88internal7CppHeap15EnterFinalPauseEN5cppgc18EmbedderStackStateE
-   fun:_ZN2v88internal20MarkCompactCollector15MarkLiveObjectsEv
-   fun:_ZN2v88internal20MarkCompactCollector14CollectGarbageEv
-   fun:_ZN2v88internal4Heap11MarkCompactEv
-   fun:_ZN2v88internal4Heap24PerformGarbageCollectionENS0_16GarbageCollectorENS0_23GarbageCollectionReasonEPKc
-   fun:_ZZN2v88internal4Heap14CollectGarbageENS0_15AllocationSpaceENS0_23GarbageCollectionReasonENS_15GCCallbackFlagsEENK3$_1clEv
-   fun:_ZN4heap4base5Stack24SetMarkerAndCallbackImplIZN2v88internal4Heap14CollectGarbageENS4_15AllocationSpaceENS4_23GarbageCollectionReasonENS3_15GCCallbackFlagsEE3$_1EEvPS1_PvPKv
+   fun:_ZN2v88internal7CppHeap15EnterFinalPauseEN5cppgc*
    obj:*
 }
 {
    <insert_a_suppression_name_here>
    Memcheck:Cond
-   fun:_ZZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKvENK3$_0clEPh
-   fun:_ZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN2v88internal12_GLOBAL__N_137UnifiedHeapConservativeMarkingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN4heap4base12_GLOBAL__N_122IteratePointersInStackEPNS0_12StackVisitorEPKvS5_S5_
-   fun:_ZNK4heap4base5Stack26IteratePointersUntilMarkerEPNS0_12StackVisitorE
-   fun:_ZN5cppgc8internal10MarkerBase10VisitRootsENS_18EmbedderStackStateE
-   fun:_ZN5cppgc8internal10MarkerBase16EnterAtomicPauseENS_18EmbedderStackStateE
-   fun:_ZN2v88internal7CppHeap15EnterFinalPauseEN5cppgc18EmbedderStackStateE
-   fun:_ZN2v88internal20MarkCompactCollector15MarkLiveObjectsEv
-   fun:_ZN2v88internal20MarkCompactCollector14CollectGarbageEv
-   fun:_ZN2v88internal4Heap11MarkCompactEv
-   fun:_ZN2v88internal4Heap24PerformGarbageCollectionENS0_16GarbageCollectorENS0_23GarbageCollectionReasonEPKc
+   fun:*ConservativeTracingVisitor27TraceConservativelyIfNeeded*
 }
 {
    <insert_a_suppression_name_here>
    Memcheck:Cond
-   fun:_ZZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKvENK3$_0clEPh
-   fun:_ZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN2v88internal12_GLOBAL__N_137UnifiedHeapConservativeMarkingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN4heap4base12_GLOBAL__N_122IteratePointersInStackEPNS0_12StackVisitorEPKvS5_S5_
-   fun:_ZNK4heap4base5Stack26IteratePointersUntilMarkerEPNS0_12StackVisitorE
-   fun:_ZN5cppgc8internal10MarkerBase10VisitRootsENS_18EmbedderStackStateE
-   fun:_ZN5cppgc8internal10MarkerBase16EnterAtomicPauseENS_18EmbedderStackStateE
-   fun:_ZN2v88internal7CppHeap15EnterFinalPauseEN5cppgc18EmbedderStackStateE
-   fun:_ZN2v88internal20MarkCompactCollector15MarkLiveObjectsEv
-   fun:_ZN2v88internal20MarkCompactCollector14CollectGarbageEv
-   fun:_ZN2v88internal4Heap11MarkCompactEv
-   fun:_ZN2v88internal4Heap24PerformGarbageCollectionENS0_16GarbageCollectorENS0_23GarbageCollectionReasonEPKc
+   fun:_ZN4heap4base12_GLOBAL__N_122IteratePointersInStack*
 }
-{
-   <insert_a_suppression_name_here>
-   Memcheck:Cond
-   fun:_ZZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKvENK3$_0clEPh
-   fun:_ZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN2v88internal12_GLOBAL__N_137UnifiedHeapConservativeMarkingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN4heap4base12_GLOBAL__N_122IteratePointersInStackEPNS0_12StackVisitorEPKvS5_S5_
-   fun:_ZNK4heap4base5Stack26IteratePointersUntilMarkerEPNS0_12StackVisitorE
-   fun:_ZN5cppgc8internal10MarkerBase10VisitRootsENS_18EmbedderStackStateE
-   fun:_ZN5cppgc8internal10MarkerBase16EnterAtomicPauseENS_18EmbedderStackStateE
-   fun:_ZN2v88internal7CppHeap15EnterFinalPauseEN5cppgc18EmbedderStackStateE
-   fun:_ZN2v88internal20MarkCompactCollector15MarkLiveObjectsEv
-   fun:_ZN2v88internal20MarkCompactCollector14CollectGarbageEv
-   fun:_ZN2v88internal4Heap11MarkCompactEv
-   fun:_ZN2v88internal4Heap24PerformGarbageCollectionENS0_16GarbageCollectorENS0_23GarbageCollectionReasonEPKc
-}
-{
-   <insert_a_suppression_name_here>
-   Memcheck:Cond
-   fun:_ZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN2v88internal12_GLOBAL__N_137UnifiedHeapConservativeMarkingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN4heap4base12_GLOBAL__N_122IteratePointersInStackEPNS0_12StackVisitorEPKvS5_S5_
-   fun:_ZNK4heap4base5Stack26IteratePointersUntilMarkerEPNS0_12StackVisitorE
-   fun:_ZN5cppgc8internal10MarkerBase10VisitRootsENS_18EmbedderStackStateE
-   fun:_ZN5cppgc8internal10MarkerBase16EnterAtomicPauseENS_18EmbedderStackStateE
-   fun:_ZN2v88internal7CppHeap15EnterFinalPauseEN5cppgc18EmbedderStackStateE
-   fun:_ZN2v88internal20MarkCompactCollector15MarkLiveObjectsEv
-   fun:_ZN2v88internal20MarkCompactCollector14CollectGarbageEv
-   fun:_ZN2v88internal4Heap11MarkCompactEv
-   fun:_ZN2v88internal4Heap24PerformGarbageCollectionENS0_16GarbageCollectorENS0_23GarbageCollectionReasonEPKc
-   fun:_ZZN2v88internal4Heap14CollectGarbageENS0_15AllocationSpaceENS0_23GarbageCollectionReasonENS_15GCCallbackFlagsEENK3$_1clEv
-}
-{
-   <insert_a_suppression_name_here>
-   Memcheck:Cond
-   fun:_ZZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKvENK3$_0clEPh
-   fun:_ZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN2v88internal12_GLOBAL__N_137UnifiedHeapConservativeMarkingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN4heap4base12_GLOBAL__N_122IteratePointersInStackEPNS0_12StackVisitorEPKvS5_S5_
-   fun:_ZNK4heap4base5Stack26IteratePointersUntilMarkerEPNS0_12StackVisitorE
-   fun:_ZN5cppgc8internal10MarkerBase10VisitRootsENS_18EmbedderStackStateE
-   fun:_ZN5cppgc8internal10MarkerBase16EnterAtomicPauseENS_18EmbedderStackStateE
-   fun:_ZN2v88internal7CppHeap15EnterFinalPauseEN5cppgc18EmbedderStackStateE
-   fun:_ZN2v88internal20MarkCompactCollector15MarkLiveObjectsEv
-   fun:_ZN2v88internal20MarkCompactCollector14CollectGarbageEv
-   fun:_ZN2v88internal4Heap11MarkCompactEv
-   fun:_ZN2v88internal4Heap24PerformGarbageCollectionENS0_16GarbageCollectorENS0_23GarbageCollectionReasonEPKc
-}
-{
-   <insert_a_suppression_name_here>
-   Memcheck:Cond
-   fun:_ZZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKvENK3$_0clEPh
-   fun:_ZN2v88internal12_GLOBAL__N_137UnifiedHeapConservativeMarkingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN4heap4base12_GLOBAL__N_122IteratePointersInStackEPNS0_12StackVisitorEPKvS5_S5_
-   fun:_ZNK4heap4base5Stack26IteratePointersUntilMarkerEPNS0_12StackVisitorE
-   fun:_ZN5cppgc8internal10MarkerBase10VisitRootsENS_18EmbedderStackStateE
-   fun:_ZN5cppgc8internal10MarkerBase16EnterAtomicPauseENS_18EmbedderStackStateE
-   fun:_ZN2v88internal7CppHeap15EnterFinalPauseEN5cppgc18EmbedderStackStateE
-   fun:_ZN2v88internal20MarkCompactCollector15MarkLiveObjectsEv
-   fun:_ZN2v88internal20MarkCompactCollector14CollectGarbageEv
-   fun:_ZN2v88internal4Heap11MarkCompactEv
-   fun:_ZN2v88internal4Heap24PerformGarbageCollectionENS0_16GarbageCollectorENS0_23GarbageCollectionReasonEPKc
-   fun:_ZZN2v88internal4Heap14CollectGarbageENS0_15AllocationSpaceENS0_23GarbageCollectionReasonENS_15GCCallbackFlagsEENK3$_1clEv
-}
-{
-   <insert_a_suppression_name_here>
-   Memcheck:Cond
-   fun:_ZZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKvENK3$_0clEPh
-   fun:_ZN5cppgc8internal26ConservativeTracingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN2v88internal12_GLOBAL__N_137UnifiedHeapConservativeMarkingVisitor27TraceConservativelyIfNeededEPKv
-   fun:_ZN4heap4base12_GLOBAL__N_122IteratePointersInStackEPNS0_12StackVisitorEPKvS5_S5_
-   fun:_ZNK4heap4base5Stack26IteratePointersUntilMarkerEPNS0_12StackVisitorE
-   fun:_ZN5cppgc8internal10MarkerBase10VisitRootsENS_18EmbedderStackStateE
-   fun:_ZN5cppgc8internal10MarkerBase16EnterAtomicPauseENS_18EmbedderStackStateE
-   fun:_ZN2v88internal7CppHeap15EnterFinalPauseEN5cppgc18EmbedderStackStateE
-   fun:_ZN2v88internal20MarkCompactCollector15MarkLiveObjectsEv
-   fun:_ZN2v88internal20MarkCompactCollector14CollectGarbageEv
-   fun:_ZN2v88internal4Heap11MarkCompactEv
-   fun:_ZN2v88internal4Heap24PerformGarbageCollectionENS0_16GarbageCollectorENS0_23GarbageCollectionReasonEPKc
-}
\ No newline at end of file
diff --git a/core/modules/tests.rs b/core/modules/tests.rs
index 71a319b..f02f4b0 100644
--- a/core/modules/tests.rs
+++ b/core/modules/tests.rs
@@ -504,35 +504,9 @@ fn test_json_module() {
 
   let module_map = runtime.module_map().clone();
 
-  let (mod_a, mod_b, mod_c) = {
+  let (mod_b, mod_c) = {
     let scope = &mut runtime.handle_scope();
-    let specifier_a = ascii_str!("file:///a.js");
     let specifier_b = ascii_str!("file:///b.js");
-    let mod_a = module_map
-      .new_es_module(
-        scope,
-        true,
-        specifier_a,
-        ascii_str!(
-          r#"
-          import jsonData from './c.json' assert {type: "json"};
-          assert(jsonData.a == "b");
-          assert(jsonData.c.d == 10);
-        "#
-        ),
-        false,
-        None,
-      )
-      .unwrap();
-
-    let imports = module_map.get_requested_modules(mod_a);
-    assert_eq!(
-      imports,
-      Some(vec![ModuleRequest {
-        specifier: ModuleSpecifier::parse("file:///c.json").unwrap(),
-        requested_module_type: RequestedModuleType::Json,
-      },])
-    );
 
     let mod_b = module_map
       .new_es_module(
@@ -569,18 +543,11 @@ fn test_json_module() {
       .unwrap();
     let imports = module_map.get_requested_modules(mod_c).unwrap();
     assert_eq!(imports.len(), 0);
-    (mod_a, mod_b, mod_c)
+    (mod_b, mod_c)
   };
 
   runtime.instantiate_module(mod_c).unwrap();
-  assert_eq!(loader.counts(), ModuleLoadEventCounts::new(2, 0, 0));
-
-  runtime.instantiate_module(mod_a).unwrap();
-
-  let receiver = runtime.mod_evaluate(mod_a);
-  futures::executor::block_on(runtime.run_event_loop(Default::default()))
-    .unwrap();
-  futures::executor::block_on(receiver).unwrap();
+  assert_eq!(loader.counts(), ModuleLoadEventCounts::new(1, 0, 0));
 
   runtime.instantiate_module(mod_b).unwrap();
 
diff --git a/testing/checkin/runtime/__bootstrap.js b/testing/checkin/runtime/__bootstrap.js
index 87ec6e1..0fb1253 100644
--- a/testing/checkin/runtime/__bootstrap.js
+++ b/testing/checkin/runtime/__bootstrap.js
@@ -1,5 +1,4 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
 
 // Allow 'using' to work in TypeScript
-Symbol.dispose = Symbol("Symbol.dispose");
 Symbol.asyncDispose = Symbol("Symbol.asyncDispose");
diff --git a/testing/integration/user_breaks_promise_constructor/user_breaks_promise_constructor.out b/testing/integration/user_breaks_promise_constructor/user_breaks_promise_constructor.out
new file mode 100644
index 0000000..9c2a539
--- /dev/null
+++ b/testing/integration/user_breaks_promise_constructor/user_breaks_promise_constructor.out
@@ -0,0 +1,3 @@
+1
+2
+[ERR] Uncaught (in promise) x
diff --git a/testing/integration/user_breaks_promise_constructor/user_breaks_promise_constructor.ts b/testing/integration/user_breaks_promise_constructor/user_breaks_promise_constructor.ts
new file mode 100644
index 0000000..0f84fbc
--- /dev/null
+++ b/testing/integration/user_breaks_promise_constructor/user_breaks_promise_constructor.ts
@@ -0,0 +1,9 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+// https://github.com/denoland/deno_core/issues/743
+console.log("1");
+Object.defineProperty(Promise.prototype, "constructor", {
+  get() {
+    throw "x";
+  },
+});
+console.log("2");
diff --git a/testing/integration/user_breaks_promise_species/user_breaks_promise_species.out b/testing/integration/user_breaks_promise_species/user_breaks_promise_species.out
new file mode 100644
index 0000000..8bc9536
--- /dev/null
+++ b/testing/integration/user_breaks_promise_species/user_breaks_promise_species.out
@@ -0,0 +1,4 @@
+1
+2
+[ERR] TypeError: object.constructor[Symbol.species] is not a constructor
+[ERR]     at Promise.then (<anonymous>)
diff --git a/testing/integration/user_breaks_promise_species/user_breaks_promise_species.ts b/testing/integration/user_breaks_promise_species/user_breaks_promise_species.ts
new file mode 100644
index 0000000..225ac46
--- /dev/null
+++ b/testing/integration/user_breaks_promise_species/user_breaks_promise_species.ts
@@ -0,0 +1,5 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+// https://github.com/denoland/deno_core/issues/742
+console.log("1");
+Object.defineProperty(Promise, Symbol.species, { value: 0 });
+console.log("2");
diff --git a/testing/lib.rs b/testing/lib.rs
index 159c76e..0655022 100644
--- a/testing/lib.rs
+++ b/testing/lib.rs
@@ -69,6 +69,8 @@ integration_test!(
   timer_ref_and_cancel,
   timer_many,
   ts_types,
+  user_breaks_promise_constructor,
+  user_breaks_promise_species,
   worker_spawn,
   worker_terminate,
   worker_terminate_op,
diff --git a/testing/unit/ops_buffer_test.ts b/testing/unit/ops_buffer_test.ts
index fc4bb12..0239957 100644
--- a/testing/unit/ops_buffer_test.ts
+++ b/testing/unit/ops_buffer_test.ts
@@ -21,5 +21,7 @@ test(function testBufferTransfer() {
   op_v8slice_store("buffer", data);
   buffer.transfer(100);
   const output = op_v8slice_clone("buffer");
-  assertArrayEquals(output, new Uint8Array(100));
+  // Note: after https://chromium-review.googlesource.com/c/v8/v8/+/5394731 landed, the underlying
+  // AB backingstore is no longer resized.
+  assertArrayEquals(output, new Uint8Array(1024 * 1024));
 });