Repository: denoland/deno_core
Pull request #635 (2024-03-05 13:46:07)

chore: upgrade rusty_v8 to 0.85.0


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 6d93cb6..7805275 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -105,7 +105,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "swc_macros_common",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -297,12 +297,6 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
 
-[[package]]
-name = "convert_case"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
-
 [[package]]
 name = "cooked-waker"
 version = "5.0.0"
@@ -486,7 +480,7 @@ dependencies = [
 
 [[package]]
 name = "deno_core"
-version = "0.267.0"
+version = "0.269.0"
 dependencies = [
  "anyhow",
  "bencher",
@@ -554,7 +548,7 @@ dependencies = [
 
 [[package]]
 name = "deno_ops"
-version = "0.143.0"
+version = "0.145.0"
 dependencies = [
  "pretty_assertions",
  "prettyplease",
@@ -563,7 +557,7 @@ dependencies = [
  "quote",
  "strum",
  "strum_macros",
- "syn 2.0.48",
+ "syn",
  "testing_macros",
  "thiserror",
 ]
@@ -579,7 +573,7 @@ dependencies = [
  "prettyplease",
  "rustversion",
  "serde",
- "syn 2.0.48",
+ "syn",
  "testing_macros",
  "trybuild",
 ]
@@ -593,19 +587,6 @@ dependencies = [
  "tokio",
 ]
 
-[[package]]
-name = "derive_more"
-version = "0.99.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
-dependencies = [
- "convert_case",
- "proc-macro2",
- "quote",
- "rustc_version 0.4.0",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "diff"
 version = "0.1.13"
@@ -708,7 +689,7 @@ checksum = "3a0b11eeb173ce52f84ebd943d42e58813a2ebb78a6a3ff0a243b71c5199cd7b"
 dependencies = [
  "proc-macro2",
  "swc_macros_common",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -777,7 +758,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -1037,7 +1018,7 @@ dependencies = [
  "Inflector",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -1135,9 +1116,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.8.10"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
 dependencies = [
  "libc",
  "wasi",
@@ -1278,7 +1259,7 @@ dependencies = [
  "phf_shared",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -1307,7 +1288,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -1358,7 +1339,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -1384,7 +1365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
 dependencies = [
  "proc-macro2",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -1395,7 +1376,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f"
 dependencies = [
  "proc-macro-rules-macros",
  "proc-macro2",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -1407,7 +1388,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -1556,7 +1537,7 @@ dependencies = [
  "regex",
  "relative-path",
  "rustc_version 0.4.0",
- "syn 2.0.48",
+ "syn",
  "unicode-ident",
 ]
 
@@ -1689,7 +1670,7 @@ checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -1706,11 +1687,10 @@ dependencies = [
 
 [[package]]
 name = "serde_v8"
-version = "0.176.0"
+version = "0.178.0"
 dependencies = [
  "bencher",
  "bytes",
- "derive_more",
  "num-bigint",
  "serde",
  "serde_bytes",
@@ -1867,7 +1847,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "swc_macros_common",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -1895,7 +1875,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustversion",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -1957,7 +1937,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "swc_macros_common",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -2006,7 +1986,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "swc_macros_common",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -2090,7 +2070,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "swc_macros_common",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -2194,7 +2174,7 @@ checksum = "695a1d8b461033d32429b5befbf0ad4d7a2c4d6ba9cd5ba4e0645c615839e8e4"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -2205,7 +2185,7 @@ checksum = "50176cfc1cbc8bb22f41c6fe9d1ec53fbe057001219b5954961b8ad0f336fce9"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -2229,18 +2209,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "swc_macros_common",
- "syn 2.0.48",
-]
-
-[[package]]
-name = "syn"
-version = "1.0.109"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
+ "syn",
 ]
 
 [[package]]
@@ -2276,7 +2245,7 @@ dependencies = [
  "quote",
  "regex",
  "relative-path",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -2305,7 +2274,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -2360,7 +2329,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -2396,7 +2365,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
 ]
 
 [[package]]
@@ -2528,9 +2497,9 @@ dependencies = [
 
 [[package]]
 name = "v8"
-version = "0.83.2"
+version = "0.85.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f6c8a960dd2eb74b22eda64f7e9f3d1688f82b80202828dc0425ebdeda826ef"
+checksum = "ec8e09551fa5c3500b47f08912b4a39e07ae20a3874051941408fbd52e3e5190"
 dependencies = [
  "bitflags 2.4.2",
  "fslock",
@@ -2590,7 +2559,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
  "wasm-bindgen-shared",
 ]
 
@@ -2612,7 +2581,7 @@ checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 27bb95a..95b8048 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,11 +19,11 @@ repository = "https://github.com/denoland/deno_core"
 
 [workspace.dependencies]
 # Local dependencies
-deno_core = { version = "0.267.0", path = "./core" }
-deno_ops = { version = "0.143.0", path = "./ops" }
-serde_v8 = { version = "0.176.0", path = "./serde_v8" }
+deno_core = { version = "0.269.0", path = "./core" }
+deno_ops = { version = "0.145.0", path = "./ops" }
+serde_v8 = { version = "0.178.0", path = "./serde_v8" }
 
-v8 = { version = "0.83.2", default-features = false }
+v8 = { version = "0.85.0", default-features = false }
 deno_ast = { version = "=0.32.0", features = ["transpiling"] }
 deno_unsync = "0.3.2"
 deno_core_icudata = "0.0.73"
@@ -36,7 +36,6 @@ bit-set = "0"
 bit-vec = "0"
 bytes = "1.4.0"
 cooked-waker = "5"
-derive_more = "0.99.17"
 fastrand = "2"
 futures = "0.3.21"
 libc = "0.2.126"
diff --git a/core/01_core.js b/core/01_core.js
index b039e2b..fd23a6d 100644
--- a/core/01_core.js
+++ b/core/01_core.js
@@ -427,6 +427,7 @@
     op_read_sync: readSync,
     op_write_sync: writeSync,
     op_shutdown: shutdown,
+    op_is_terminal: isTerminal,
   } = ensureFastOps(true);
 
   const callSiteRetBuf = new Uint32Array(2);
@@ -640,6 +641,7 @@
     readSync,
     writeSync,
     shutdown,
+    isTerminal,
     print: (msg, isErr) => op_print(msg, isErr),
     setLeakTracingEnabled: (enabled) => {
       __setLeakTracingEnabled(enabled);
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 49c061a..432505c 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -2,7 +2,7 @@
 
 [package]
 name = "deno_core"
-version = "0.267.0"
+version = "0.269.0"
 authors.workspace = true
 edition.workspace = true
 license.workspace = true
@@ -20,6 +20,7 @@ v8_use_custom_libcxx = ["v8/use_custom_libcxx"]
 include_js_files_for_snapshotting = []
 unsafe_runtime_options = []
 unsafe_use_unprotected_platform = []
+lazy_eval_snapshot = []
 
 [dependencies]
 anyhow.workspace = true
diff --git a/core/fast_string.rs b/core/fast_string.rs
index f2ea372..907f1ac 100644
--- a/core/fast_string.rs
+++ b/core/fast_string.rs
@@ -55,7 +55,7 @@ impl FastStaticString {
     }
     // SAFETY: Workaround compiler error in create_external_onebyte_const for long ascii strings
     unsafe {
-      assert!(s.is_ascii());
+      debug_assert!(s.is_ascii());
       let c = v8::String::create_external_onebyte_const(&[]);
       let mut ptrs: OneByteConst = std::mem::transmute(c);
       ptrs.cached_data = std::mem::transmute(s.as_ptr());
diff --git a/core/lib.deno_core.d.ts b/core/lib.deno_core.d.ts
index 4d18de3..7a76c7b 100644
--- a/core/lib.deno_core.d.ts
+++ b/core/lib.deno_core.d.ts
@@ -96,6 +96,11 @@ declare namespace Deno {
      */
     function print(message: string, is_err?: boolean): void;
 
+    /**
+     * Returns whether the given (file-like) resource is a TTY.
+     */
+    function isTerminal(rid: number): boolean;
+
     /**
      * Shutdown a resource
      */
diff --git a/core/modules/map.rs b/core/modules/map.rs
index 3dab5aa..57ca421 100644
--- a/core/modules/map.rs
+++ b/core/modules/map.rs
@@ -542,7 +542,7 @@ impl ModuleMap {
         .get_specifier()
         .to_rust_string_lossy(tc_scope);
 
-      let import_attributes = module_request.get_import_assertions();
+      let import_attributes = module_request.get_import_attributes();
 
       let attributes = parse_import_attributes(
         tc_scope,
diff --git a/core/ops.rs b/core/ops.rs
index c9cbd0a..130c04e 100644
--- a/core/ops.rs
+++ b/core/ops.rs
@@ -138,15 +138,64 @@ impl OpCtx {
   }
 
   #[inline(always)]
-  pub fn decl(&self) -> &OpDecl {
+  pub const fn decl(&self) -> &OpDecl {
     &self.decl
   }
 
   #[inline(always)]
-  pub fn metrics_enabled(&self) -> bool {
+  pub const fn metrics_enabled(&self) -> bool {
     self.metrics_fn.is_some()
   }
 
+  /// Generates four external references for each op. If an op does not have a fastcall, it generates
+  /// "null" slots to avoid changing the size of the external references array.
+  pub const fn external_references(&self) -> [v8::ExternalReference; 4] {
+    extern "C" fn placeholder() {}
+
+    let ctx_ptr = v8::ExternalReference {
+      pointer: self as *const OpCtx as _,
+    };
+    let null = v8::ExternalReference {
+      pointer: placeholder as _,
+    };
+
+    if self.metrics_enabled() {
+      let slow_fn = v8::ExternalReference {
+        function: self.decl.slow_fn_with_metrics,
+      };
+      if let (Some(fast_fn), Some(fast_fn_c_info)) =
+        (&self.decl.fast_fn_with_metrics, &self.fast_fn_c_info)
+      {
+        let fast_fn = v8::ExternalReference {
+          pointer: fast_fn.function as _,
+        };
+        let fast_info = v8::ExternalReference {
+          pointer: fast_fn_c_info.as_ptr() as _,
+        };
+        [ctx_ptr, slow_fn, fast_fn, fast_info]
+      } else {
+        [ctx_ptr, slow_fn, null, null]
+      }
+    } else {
+      let slow_fn = v8::ExternalReference {
+        function: self.decl.slow_fn,
+      };
+      if let (Some(fast_fn), Some(fast_fn_c_info)) =
+        (&self.decl.fast_fn, &self.fast_fn_c_info)
+      {
+        let fast_fn = v8::ExternalReference {
+          pointer: fast_fn.function as _,
+        };
+        let fast_info = v8::ExternalReference {
+          pointer: fast_fn_c_info.as_ptr() as _,
+        };
+        [ctx_ptr, slow_fn, fast_fn, fast_info]
+      } else {
+        [ctx_ptr, slow_fn, null, null]
+      }
+    }
+  }
+
   /// This takes the last error from an [`OpCtx`], assuming that no other code anywhere
   /// can hold a `&mut` to the last_fast_error field.
   ///
diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs
index f496e97..9c22baf 100644
--- a/core/ops_builtin.rs
+++ b/core/ops_builtin.rs
@@ -58,6 +58,7 @@ builtin_ops! {
   op_panic,
   op_cancel_handle,
   op_encode_binary_string,
+  op_is_terminal,
   ops_builtin_types::op_is_any_array_buffer,
   ops_builtin_types::op_is_arguments_object,
   ops_builtin_types::op_is_array_buffer,
@@ -395,3 +396,12 @@ pub fn op_cancel_handle(state: &mut OpState) -> u32 {
 fn op_encode_binary_string(#[buffer] s: &[u8]) -> ByteString {
   ByteString::from(s)
 }
+
+#[op2(fast)]
+fn op_is_terminal(
+  state: &mut OpState,
+  #[smi] rid: ResourceId,
+) -> Result<bool, Error> {
+  let handle = state.resource_table.get_handle(rid)?;
+  Ok(handle.is_terminal())
+}
diff --git a/core/runtime/bindings.rs b/core/runtime/bindings.rs
index dacdc55..5f5065a 100644
--- a/core/runtime/bindings.rs
+++ b/core/runtime/bindings.rs
@@ -1,6 +1,7 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
 use anyhow::Context;
 use log::debug;
+use std::mem::MaybeUninit;
 use std::os::raw::c_void;
 use std::path::PathBuf;
 use url::Url;
@@ -14,6 +15,7 @@ use crate::error::is_instance_of_error;
 use crate::error::throw_type_error;
 use crate::error::AnyError;
 use crate::error::JsStackFrame;
+use crate::extension_set::LoadedSources;
 use crate::modules::get_requested_module_type_from_attributes;
 use crate::modules::parse_import_attributes;
 use crate::modules::synthetic_module_evaluation_steps;
@@ -23,19 +25,24 @@ use crate::ops::OpCtx;
 use crate::runtime::InitMode;
 use crate::runtime::JsRealm;
 use crate::FastStaticString;
+use crate::FastString;
 use crate::JsRuntime;
 use crate::ModuleType;
 
 pub(crate) fn create_external_references(
   ops: &[OpCtx],
   additional_references: &[v8::ExternalReference],
-) -> &'static v8::ExternalReferences {
+  sources: &[v8::OneByteConst],
+  ops_in_snapshot: usize,
+  sources_in_snapshot: usize,
+) -> v8::ExternalReferences {
   // Overallocate a bit, it's better than having to resize the vector.
   let mut references = Vec::with_capacity(
     6 + CONTEXT_SETUP_SOURCES.len()
       + BUILTIN_SOURCES.len()
       + (ops.len() * 4)
-      + additional_references.len(),
+      + additional_references.len()
+      + sources.len(),
   );
 
   references.push(v8::ExternalReference {
@@ -73,43 +80,86 @@ pub(crate) fn create_external_references(
     });
   }
 
-  for ctx in ops {
-    if ctx.metrics_enabled() {
-      let ctx_ptr = ctx as *const OpCtx as _;
-      references.push(v8::ExternalReference { pointer: ctx_ptr });
-      references.push(v8::ExternalReference {
-        function: ctx.decl.slow_fn_with_metrics,
-      });
-      if let Some(fast_fn) = &ctx.decl.fast_fn_with_metrics {
-        references.push(v8::ExternalReference {
-          pointer: fast_fn.function as _,
-        });
-        references.push(v8::ExternalReference {
-          pointer: ctx.fast_fn_c_info.unwrap().as_ptr() as _,
-        });
-      }
-    } else {
-      let ctx_ptr = ctx as *const OpCtx as _;
-      references.push(v8::ExternalReference { pointer: ctx_ptr });
-      references.push(v8::ExternalReference {
-        function: ctx.decl.slow_fn,
-      });
-      if let Some(fast_fn) = &ctx.decl.fast_fn {
-        references.push(v8::ExternalReference {
-          pointer: fast_fn.function as _,
-        });
-        references.push(v8::ExternalReference {
-          pointer: ctx.fast_fn_c_info.unwrap().as_ptr() as _,
-        });
-      }
-    }
+  references.extend_from_slice(additional_references);
+
+  for ctx in &ops[..ops_in_snapshot] {
+    references.extend_from_slice(&ctx.external_references());
   }
 
-  references.extend_from_slice(additional_references);
+  for source in &sources[..sources_in_snapshot] {
+    references.push(v8::ExternalReference {
+      pointer: source as *const _ as _,
+    })
+  }
+
+  for ctx in &ops[ops_in_snapshot..] {
+    references.extend_from_slice(&ctx.external_references());
+  }
 
-  let refs = v8::ExternalReferences::new(&references);
-  let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs));
-  refs
+  for source in &sources[sources_in_snapshot..] {
+    references.push(v8::ExternalReference {
+      pointer: source as *const _ as _,
+    })
+  }
+
+  v8::ExternalReferences::new(&references)
+}
+
+/// Combine the snapshotted sources (which may be empty) with the loaded sources, and ensure that
+/// each of the loaded source files passed to this function has a correct `v8::OneByteConst` backing
+/// that can be used for compilation.
+pub(crate) fn externalize_sources(
+  sources: &mut LoadedSources,
+  snapshot_sources: Vec<&'static [u8]>,
+) -> (Box<[v8::OneByteConst]>, Box<[FastString]>) {
+  // This is a complex method partly because we're still waiting on the `Copy` trait on v8::OneByteConst
+  // to land.
+
+  // Create an uninitialized Box<[v8::OneByteConst]. This will be simplified when we get Copy on OneByteConst.
+  // Because we don't have that trait, we work around it with [usize; 3].
+  const INIT_VALUE: MaybeUninit<[usize; 3]> =
+    MaybeUninit::<[usize; 3]>::uninit();
+  let externals =
+    vec![INIT_VALUE; sources.len() + snapshot_sources.len()].into_boxed_slice();
+
+  // Keep the original sources around, since we're borrowing from them.
+  let mut original_sources = Vec::with_capacity(sources.len());
+
+  // SAFETY: We are creating `v8::OneByteConst`s here for each of the input sources. Because
+  // we keep the original source alive, we can safely make a `v8::OneByteConst` from _any_
+  // source type. We'll make this lifetime static elsewhere in the code so we can safely
+  // use it with v8 strings.
+  unsafe {
+    let mut externals: Box<[v8::OneByteConst]> = std::mem::transmute(externals);
+
+    // First, add all the snapshot sources. These must be done first because we need
+    // to ensure that snapshotted sources are added to the externalrefs before non-snapshotted
+    // sources so that they line up correct.
+    let offset = 0;
+    for (index, source) in snapshot_sources.iter().enumerate() {
+      externals[index + offset] =
+        FastStaticString::create_external_onebyte_const(source);
+    }
+
+    // Next, add the non-snapshot sources. For each source file, we swap its `code`
+    // member to use this new external string. Note that this is only safe because
+    // we keep the original source alive.
+    let offset = snapshot_sources.len();
+    for (index, source) in sources.into_iter().enumerate() {
+      externals[index + offset] =
+        FastStaticString::create_external_onebyte_const(std::mem::transmute(
+          source.code.as_bytes(),
+        ));
+      let ptr = &externals[index + offset] as *const v8::OneByteConst;
+      let original_source = std::mem::replace(
+        &mut source.code,
+        FastStaticString::from(&*ptr).into(),
+      );
+      original_sources.push(original_source)
+    }
+
+    (externals, original_sources.into_boxed_slice())
+  }
 }
 
 // TODO(nayeemrmn): Move to runtime and/or make `pub(crate)`.
diff --git a/core/runtime/jsruntime.rs b/core/runtime/jsruntime.rs
index 2406db2..eefc13d 100644
--- a/core/runtime/jsruntime.rs
+++ b/core/runtime/jsruntime.rs
@@ -95,6 +95,9 @@ pub type ExtensionTranspiler =
 /// Objects that need to live as long as the isolate
 #[derive(Default)]
 pub(crate) struct IsolateAllocations {
+  pub(crate) external_refs: Option<Box<v8::ExternalReferences>>,
+  pub(crate) externalized_sources: Box<[v8::OneByteConst]>,
+  pub(crate) original_sources: Box<[FastString]>,
   pub(crate) near_heap_limit_callback_data:
     Option<(Box<RefCell<dyn Any>>, v8::NearHeapLimitCallback)>,
 }
@@ -137,6 +140,10 @@ impl<T> DerefMut for ManuallyDropRc<T> {
 /// control dropping more closely here using ManuallyDrop.
 pub(crate) struct InnerIsolateState {
   will_snapshot: bool,
+  extension_count: usize,
+  op_count: usize,
+  source_count: usize,
+  addl_refs_count: usize,
   main_realm: ManuallyDrop<JsRealm>,
   pub(crate) state: ManuallyDropRc<JsRuntimeState>,
   v8_isolate: ManuallyDrop<v8::OwnedIsolate>,
@@ -625,7 +632,7 @@ impl JsRuntime {
   ) -> Result<JsRuntime, Error> {
     let init_mode = InitMode::from_options(&options);
     let mut extensions = std::mem::take(&mut options.extensions);
-    let isolate_allocations = IsolateAllocations::default();
+    let mut isolate_allocations = IsolateAllocations::default();
 
     // First let's create an `OpState` and contribute to it from extensions...
     let mut op_state = OpState::new(options.feature_checker.take());
@@ -635,7 +642,7 @@ impl JsRuntime {
     let mut files_loaded = Vec::with_capacity(128);
     let mut source_mapper: SourceMapper<Rc<dyn SourceMapGetter>> =
       SourceMapper::new(options.source_map_getter);
-    let sources = extension_set::into_sources(
+    let mut sources = extension_set::into_sources(
       options.extension_transpiler.as_deref(),
       &extensions,
       &mut source_mapper,
@@ -688,14 +695,49 @@ impl JsRuntime {
       additional_references,
     ) = extension_set::get_middlewares_and_external_refs(&mut extensions);
 
-    let (maybe_startup_snapshot, sidecar_data) = options
+    // Capture the extension, op and source counts
+    let extension_count = extensions.len();
+    let op_count = op_ctxs.len();
+    let source_count = sources.len();
+    let addl_refs_count = additional_references.len();
+
+    let (maybe_startup_snapshot, mut sidecar_data) = options
       .startup_snapshot
       .take()
       .map(snapshot::deconstruct)
       .unzip();
 
-    let external_refs_static =
-      bindings::create_external_references(&op_ctxs, &additional_references);
+    let ops_in_snapshot = sidecar_data
+      .as_ref()
+      .map(|d| d.snapshot_data.op_count)
+      .unwrap_or_default();
+    let sources_in_snapshot = sidecar_data
+      .as_ref()
+      .map(|d| d.snapshot_data.source_count)
+      .unwrap_or_default();
+
+    let snapshot_sources: Vec<&[u8]> = sidecar_data
+      .as_mut()
+      .map(|s| std::mem::take(&mut s.snapshot_data.external_strings))
+      .unwrap_or_default();
+    (
+      isolate_allocations.externalized_sources,
+      isolate_allocations.original_sources,
+    ) = bindings::externalize_sources(&mut sources, snapshot_sources);
+
+    isolate_allocations.external_refs =
+      Some(Box::new(bindings::create_external_references(
+        &op_ctxs,
+        &additional_references,
+        &isolate_allocations.externalized_sources,
+        ops_in_snapshot,
+        sources_in_snapshot,
+      )));
+
+    let external_refs: &v8::ExternalReferences =
+      isolate_allocations.external_refs.as_ref().unwrap().as_ref();
+    // SAFETY: We attach external_refs to IsolateAllocations which will live as long as the isolate
+    let external_refs_static = unsafe { std::mem::transmute(external_refs) };
 
     let mut isolate = setup::create_isolate(
       will_snapshot,
@@ -811,18 +853,19 @@ impl JsRuntime {
 
     if let Some((snapshotted_data, mut data_store)) = snapshotted_data {
       *exception_state.js_handled_promise_rejection_cb.borrow_mut() =
-        snapshotted_data.js_handled_promise_rejection_cb;
+        snapshotted_data
+          .js_handled_promise_rejection_cb
+          .map(|cb| data_store.get(scope, cb));
       module_map.update_with_snapshotted_data(
         scope,
         &mut data_store,
         snapshotted_data.module_map_data,
       );
 
-      state_rc
-        .source_mapper
-        .borrow_mut()
-        .ext_source_maps
-        .extend(snapshotted_data.ext_source_maps);
+      let mut mapper = state_rc.source_mapper.borrow_mut();
+      for (key, map) in snapshotted_data.ext_source_maps {
+        mapper.ext_source_maps.insert(key, map.into());
+      }
     }
 
     context.set_slot(scope, module_map.clone());
@@ -848,6 +891,10 @@ impl JsRuntime {
     let mut js_runtime = JsRuntime {
       inner: InnerIsolateState {
         will_snapshot,
+        extension_count,
+        op_count,
+        source_count,
+        addl_refs_count,
         main_realm: ManuallyDrop::new(main_realm),
         state: ManuallyDropRc(ManuallyDrop::new(state_rc)),
         v8_isolate: ManuallyDrop::new(isolate),
@@ -1708,7 +1755,7 @@ impl JsRuntime {
     }
 
     if pending_state.has_pending_module_evaluation {
-      if pending_state.has_pending_refed_ops
+      if pending_state.has_pending_ops
         || pending_state.has_pending_dyn_imports
         || pending_state.has_pending_dyn_module_evaluation
         || pending_state.has_pending_background_tasks
@@ -1723,7 +1770,7 @@ impl JsRuntime {
     }
 
     if pending_state.has_pending_dyn_module_evaluation {
-      if pending_state.has_pending_refed_ops
+      if pending_state.has_pending_ops
         || pending_state.has_pending_dyn_imports
         || pending_state.has_pending_background_tasks
         || pending_state.has_tick_scheduled
@@ -1816,6 +1863,14 @@ impl JsRuntimeForSnapshot {
   pub fn snapshot(mut self) -> Box<[u8]> {
     // Ensure there are no live inspectors to prevent crashes.
     self.inner.prepare_for_cleanup();
+    let externals_count =
+      self.0.allocations.external_refs.as_ref().unwrap().len() as _;
+    let original_sources =
+      std::mem::take(&mut self.0.allocations.original_sources);
+    let external_strings = original_sources
+      .iter()
+      .map(|s| s.as_str().as_bytes())
+      .collect();
     let realm = JsRealm::clone(&self.inner.main_realm);
 
     // Set the context to be snapshot's default context
@@ -1825,6 +1880,15 @@ impl JsRuntimeForSnapshot {
       scope.set_default_context(local_context);
     }
 
+    // Borrow the source maps during the snapshot to avoid copies
+    let source_maps = std::mem::take(
+      &mut self.inner.state.source_mapper.borrow_mut().ext_source_maps,
+    );
+    let mut ext_source_maps = HashMap::with_capacity(source_maps.len());
+    for (k, v) in &source_maps {
+      ext_source_maps.insert(k.clone(), v.as_ref());
+    }
+
     // Serialize the module map and store its data in the snapshot.
     // TODO(mmastrac): This should deconstruct the realm into sidecar data rather than
     // extracting it from the realm and then tearing the realm down. IE, this should
@@ -1843,19 +1907,19 @@ impl JsRuntimeForSnapshot {
           .js_handled_promise_rejection_cb
           .borrow()
           .clone()
-      };
-      let ext_source_maps = self
-        .inner
-        .state
-        .source_mapper
-        .borrow()
-        .ext_source_maps
-        .clone();
+      }
+      .map(|cb| data_store.register(cb));
 
       let snapshotted_data = SnapshottedData {
         module_map_data,
+        externals_count,
+        op_count: self.inner.op_count,
+        addl_refs_count: self.inner.addl_refs_count,
+        source_count: self.inner.source_count,
+        extension_count: self.inner.extension_count,
         js_handled_promise_rejection_cb: maybe_js_handled_promise_rejection_cb,
         ext_source_maps,
+        external_strings,
       };
 
       let mut scope = realm.handle_scope(self.v8_isolate());
@@ -1881,6 +1945,7 @@ impl JsRuntimeForSnapshot {
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub(crate) struct EventLoopPendingState {
+  has_pending_ops: bool,
   has_pending_refed_ops: bool,
   has_pending_dyn_imports: bool,
   has_pending_dyn_module_evaluation: bool,
@@ -1900,7 +1965,8 @@ impl EventLoopPendingState {
     let num_unrefed_ops = state.unrefed_ops.borrow().len();
     let num_pending_ops = state.pending_ops.len();
     let has_pending_tasks = state.task_spawner_factory.has_pending_tasks();
-    let has_pending_timers = state.timers.has_pending_timers();
+    let has_pending_timers = !state.timers.is_empty();
+    let has_pending_refed_timers = state.timers.has_pending_timers();
     let has_pending_dyn_imports = modules.has_pending_dynamic_imports();
     let has_pending_dyn_module_evaluation =
       modules.has_pending_dyn_module_evaluation();
@@ -1915,10 +1981,14 @@ impl EventLoopPendingState {
         .pending_handled_promise_rejections
         .borrow()
         .is_empty();
+    let has_pending_refed_ops = has_pending_tasks
+      || has_pending_refed_timers
+      || num_pending_ops > num_unrefed_ops;
     EventLoopPendingState {
-      has_pending_refed_ops: has_pending_tasks
+      has_pending_ops: has_pending_refed_ops
         || has_pending_timers
-        || num_pending_ops > num_unrefed_ops,
+        || (num_pending_ops > 0),
+      has_pending_refed_ops,
       has_pending_dyn_imports,
       has_pending_dyn_module_evaluation,
       has_pending_module_evaluation,
diff --git a/core/runtime/setup.rs b/core/runtime/setup.rs
index 94f2411..2c97b0c 100644
--- a/core/runtime/setup.rs
+++ b/core/runtime/setup.rs
@@ -13,7 +13,7 @@ use std::sync::Once;
 
 fn v8_init(
   v8_platform: Option<v8::SharedRef<v8::Platform>>,
-  predictable: bool,
+  snapshot: bool,
   expose_natives: bool,
 ) {
   #[cfg(feature = "include_icu_data")]
@@ -29,17 +29,24 @@ fn v8_init(
     " --turbo_fast_api_calls",
     " --harmony-array-from_async",
     " --harmony-iterator-helpers",
+    " --harmony-temporal",
   );
-  let predictable_flags = "--predictable --random-seed=42";
+  let snapshot_flags = "--predictable --random-seed=42";
   let expose_natives_flags = "--expose_gc --allow_natives_syntax";
-
+  let lazy_flags = if cfg!(feature = "lazy_eval_snapshot") {
+    "--lazy=false --lazy-eval=false --lazy-streaming=false"
+  } else {
+    ""
+  };
   #[allow(clippy::useless_format)]
-  let flags = match (predictable, expose_natives) {
+  let flags = match (snapshot, expose_natives) {
     (false, false) => format!("{base_flags}"),
-    (true, false) => format!("{base_flags} {predictable_flags}"),
+    (true, false) => format!("{base_flags} {snapshot_flags} {lazy_flags}"),
     (false, true) => format!("{base_flags} {expose_natives_flags}"),
     (true, true) => {
-      format!("{base_flags} {predictable_flags} {expose_natives_flags}")
+      format!(
+        "{base_flags} {snapshot_flags} {lazy_flags} {expose_natives_flags}"
+      )
     }
   };
   v8::V8::set_flags_from_string(&flags);
@@ -61,22 +68,21 @@ fn v8_init(
 
 pub fn init_v8(
   v8_platform: Option<v8::SharedRef<v8::Platform>>,
-  predictable: bool,
+  snapshot: bool,
   expose_natives: bool,
 ) {
   static DENO_INIT: Once = Once::new();
-  static DENO_PREDICTABLE: AtomicBool = AtomicBool::new(false);
-  static DENO_PREDICTABLE_SET: AtomicBool = AtomicBool::new(false);
-
-  if DENO_PREDICTABLE_SET.load(Ordering::SeqCst) {
-    let current = DENO_PREDICTABLE.load(Ordering::SeqCst);
-    assert_eq!(current, predictable, "V8 may only be initialized once in either snapshotting or non-snapshotting mode. Either snapshotting or non-snapshotting mode may be used in a single process, not both.");
-    DENO_PREDICTABLE_SET.store(true, Ordering::SeqCst);
-    DENO_PREDICTABLE.store(predictable, Ordering::SeqCst);
+  static DENO_SNAPSHOT: AtomicBool = AtomicBool::new(false);
+  static DENO_SNAPSHOT_SET: AtomicBool = AtomicBool::new(false);
+
+  if DENO_SNAPSHOT_SET.load(Ordering::SeqCst) {
+    let current = DENO_SNAPSHOT.load(Ordering::SeqCst);
+    assert_eq!(current, snapshot, "V8 may only be initialized once in either snapshotting or non-snapshotting mode. Either snapshotting or non-snapshotting mode may be used in a single process, not both.");
+    DENO_SNAPSHOT_SET.store(true, Ordering::SeqCst);
+    DENO_SNAPSHOT.store(snapshot, Ordering::SeqCst);
   }
 
-  DENO_INIT
-    .call_once(move || v8_init(v8_platform, predictable, expose_natives));
+  DENO_INIT.call_once(move || v8_init(v8_platform, snapshot, expose_natives));
 }
 
 pub fn init_cppgc(isolate: &mut v8::Isolate) -> v8::UniqueRef<v8::cppgc::Heap> {
diff --git a/core/runtime/snapshot.rs b/core/runtime/snapshot.rs
index 14f6662..8136739 100644
--- a/core/runtime/snapshot.rs
+++ b/core/runtime/snapshot.rs
@@ -3,7 +3,6 @@
 use anyhow::Error;
 use serde::Deserialize;
 use serde::Serialize;
-use std::borrow::Cow;
 use std::collections::HashMap;
 use std::path::Path;
 use std::path::PathBuf;
@@ -230,19 +229,31 @@ pub fn get_js_files(
   js_files
 }
 
+/// The data we intend to snapshot, separated from any V8 objects that
+/// are stored in the [`SnapshotLoadDataStore`]/[`SnapshotStoreDataStore`].
+#[derive(Serialize, Deserialize)]
 pub(crate) struct SnapshottedData<'snapshot> {
+  pub js_handled_promise_rejection_cb: Option<u32>,
   pub module_map_data: ModuleMapSnapshotData,
-  pub js_handled_promise_rejection_cb: Option<v8::Global<v8::Function>>,
-  pub ext_source_maps: HashMap<String, Cow<'snapshot, [u8]>>,
+  pub externals_count: u32,
+  pub extension_count: usize,
+  pub op_count: usize,
+  pub source_count: usize,
+  pub addl_refs_count: usize,
+  #[serde(borrow)]
+  pub ext_source_maps: HashMap<String, &'snapshot [u8]>,
+  #[serde(borrow)]
+  pub external_strings: Vec<&'snapshot [u8]>,
 }
 
-/// Snapshot sidecar data that is safe to serialize via serde.
+/// Snapshot sidecar data, containing a [`SnapshottedData`] and the length of the
+/// associated data array. This is the final form of the [`SnapshottedData`] before
+/// we hand it off to serde.
 #[derive(Serialize, Deserialize)]
 pub(crate) struct SerializableSnapshotSidecarData<'snapshot> {
   data_count: u32,
-  module_map_data: ModuleMapSnapshotData,
-  js_handled_promise_rejection_cb: Option<SnapshotDataId>,
-  ext_source_maps: HashMap<String, Cow<'snapshot, [u8]>>,
+  #[serde(borrow)]
+  pub snapshot_data: SnapshottedData<'snapshot>,
 }
 
 impl<'snapshot> SerializableSnapshotSidecarData<'snapshot> {
@@ -272,16 +283,7 @@ pub(crate) fn load_snapshotted_data_from_snapshot<'snapshot>(
     data.data.push(Some(item));
   }
 
-  (
-    SnapshottedData {
-      module_map_data: raw_data.module_map_data,
-      js_handled_promise_rejection_cb: raw_data
-        .js_handled_promise_rejection_cb
-        .map(|x| data.get(scope, x)),
-      ext_source_maps: raw_data.ext_source_maps,
-    },
-    data,
-  )
+  (raw_data.snapshot_data, data)
 }
 
 /// Given a `SnapshottedData` and `SnapshotStoreDataStore`, attaches the data to the
@@ -290,17 +292,12 @@ pub(crate) fn store_snapshotted_data_for_snapshot<'snapshot>(
   scope: &mut v8::HandleScope,
   context: v8::Global<v8::Context>,
   snapshotted_data: SnapshottedData<'snapshot>,
-  mut data_store: SnapshotStoreDataStore,
+  data_store: SnapshotStoreDataStore,
 ) -> SerializableSnapshotSidecarData<'snapshot> {
   let context = v8::Local::new(scope, context);
-  let js_handled_promise_rejection_cb = snapshotted_data
-    .js_handled_promise_rejection_cb
-    .map(|v| data_store.register(v));
   let raw_snapshot_data = SerializableSnapshotSidecarData {
     data_count: data_store.data.len() as _,
-    module_map_data: snapshotted_data.module_map_data,
-    js_handled_promise_rejection_cb,
-    ext_source_maps: snapshotted_data.ext_source_maps,
+    snapshot_data: snapshotted_data,
   };
 
   for data in data_store.data {
diff --git a/core/web_timeout.rs b/core/web_timeout.rs
index 036f727..641f78e 100644
--- a/core/web_timeout.rs
+++ b/core/web_timeout.rs
@@ -476,7 +476,6 @@ impl<T: Clone> WebTimers<T> {
   }
 
   /// Is this set of timers empty?
-  #[cfg(test)]
   pub fn is_empty(&self) -> bool {
     self.data_map.borrow().is_empty()
   }
diff --git a/ops/Cargo.toml b/ops/Cargo.toml
index 3c1b6d7..28d7ddd 100644
--- a/ops/Cargo.toml
+++ b/ops/Cargo.toml
@@ -2,7 +2,7 @@
 
 [package]
 name = "deno_ops"
-version = "0.143.0"
+version = "0.145.0"
 authors.workspace = true
 edition.workspace = true
 license.workspace = true
diff --git a/serde_v8/Cargo.toml b/serde_v8/Cargo.toml
index 030c356..f5dcfdb 100644
--- a/serde_v8/Cargo.toml
+++ b/serde_v8/Cargo.toml
@@ -2,7 +2,7 @@
 
 [package]
 name = "serde_v8"
-version = "0.176.0"
+version = "0.178.0"
 authors.workspace = true
 edition.workspace = true
 license.workspace = true
@@ -15,7 +15,6 @@ path = "lib.rs"
 
 [dependencies]
 bytes.workspace = true
-derive_more.workspace = true
 num-bigint.workspace = true
 serde.workspace = true
 smallvec = { workspace = true, features = ["union"] }
diff --git a/serde_v8/magic/bigint.rs b/serde_v8/magic/bigint.rs
index 3391b1a..6405f56 100644
--- a/serde_v8/magic/bigint.rs
+++ b/serde_v8/magic/bigint.rs
@@ -1,5 +1,8 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
 
+use std::ops::Deref;
+use std::ops::DerefMut;
+
 use smallvec::smallvec;
 use smallvec::SmallVec;
 
@@ -8,22 +11,24 @@ use super::transl8::ToV8;
 use crate::magic::transl8::impl_magic;
 use crate::Error;
 
-#[derive(
-  PartialEq,
-  Eq,
-  Clone,
-  Debug,
-  Default,
-  derive_more::Deref,
-  derive_more::DerefMut,
-  derive_more::AsRef,
-  derive_more::AsMut,
-)]
-#[as_mut(forward)]
-#[as_ref(forward)]
+#[derive(PartialEq, Eq, Clone, Debug, Default)]
 pub struct BigInt(num_bigint::BigInt);
 impl_magic!(BigInt);
 
+impl Deref for BigInt {
+  type Target = num_bigint::BigInt;
+
+  fn deref(&self) -> &Self::Target {
+    &self.0
+  }
+}
+
+impl DerefMut for BigInt {
+  fn deref_mut(&mut self) -> &mut Self::Target {
+    &mut self.0
+  }
+}
+
 impl ToV8 for BigInt {
   fn to_v8<'a>(
     &mut self,
diff --git a/serde_v8/magic/transl8.rs b/serde_v8/magic/transl8.rs
index 5f3f791..7472726 100644
--- a/serde_v8/magic/transl8.rs
+++ b/serde_v8/magic/transl8.rs
@@ -141,25 +141,3 @@ macro_rules! impl_magic {
   };
 }
 pub(crate) use impl_magic;
-
-macro_rules! impl_wrapper {
-  ($i:item) => {
-    #[derive(
-      PartialEq,
-      Eq,
-      Clone,
-      Debug,
-      Default,
-      derive_more::Deref,
-      derive_more::DerefMut,
-      derive_more::AsRef,
-      derive_more::AsMut,
-      derive_more::From,
-    )]
-    #[as_mut(forward)]
-    #[as_ref(forward)]
-    #[from(forward)]
-    $i
-  };
-}
-pub(crate) use impl_wrapper;
diff --git a/serde_v8/magic/u16string.rs b/serde_v8/magic/u16string.rs
index 040207d..b9d74ef 100644
--- a/serde_v8/magic/u16string.rs
+++ b/serde_v8/magic/u16string.rs
@@ -1,17 +1,68 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
 
+use std::ops::Deref;
+use std::ops::DerefMut;
+
 use crate::Error;
 
 use super::transl8::impl_magic;
-use super::transl8::impl_wrapper;
 use super::transl8::FromV8;
 use super::transl8::ToV8;
 
-impl_wrapper!(
-  pub struct U16String(Vec<u16>);
-);
+#[derive(PartialEq, Eq, Clone, Debug, Default)]
+pub struct U16String(Vec<u16>);
 impl_magic!(U16String);
 
+impl Deref for U16String {
+  type Target = Vec<u16>;
+
+  fn deref(&self) -> &Self::Target {
+    &self.0
+  }
+}
+
+impl DerefMut for U16String {
+  fn deref_mut(&mut self) -> &mut Self::Target {
+    &mut self.0
+  }
+}
+
+impl AsRef<[u16]> for U16String {
+  fn as_ref(&self) -> &[u16] {
+    &self.0
+  }
+}
+
+impl AsMut<[u16]> for U16String {
+  fn as_mut(&mut self) -> &mut [u16] {
+    &mut self.0
+  }
+}
+
+impl<const N: usize> From<[u16; N]> for U16String {
+  fn from(value: [u16; N]) -> Self {
+    Self(value.into())
+  }
+}
+
+impl<const N: usize> From<&[u16; N]> for U16String {
+  fn from(value: &[u16; N]) -> Self {
+    Self(value.into())
+  }
+}
+
+impl From<&[u16]> for U16String {
+  fn from(value: &[u16]) -> Self {
+    Self(value.into())
+  }
+}
+
+impl From<Vec<u16>> for U16String {
+  fn from(value: Vec<u16>) -> Self {
+    Self(value)
+  }
+}
+
 impl ToV8 for U16String {
   fn to_v8<'a>(
     &mut self,


Test patch:
diff --git a/core/runtime/tests/misc.rs b/core/runtime/tests/misc.rs
index 41665a8..cb96ec4 100644
--- a/core/runtime/tests/misc.rs
+++ b/core/runtime/tests/misc.rs
@@ -11,6 +11,7 @@ use cooked_waker::Wake;
 use cooked_waker::WakeRef;
 use futures::future::poll_fn;
 use rstest::rstest;
+use std::collections::HashSet;
 use std::rc::Rc;
 use std::sync::atomic::AtomicBool;
 use std::sync::atomic::AtomicI8;
@@ -1228,3 +1229,81 @@ async fn terminate_execution_run_event_loop_js() {
 
   terminator_thread.join().unwrap();
 }
+
+#[tokio::test]
+async fn global_template_middleware() {
+  use v8::MapFnTo;
+
+  static mut CALLS: Vec<String> = Vec::new();
+
+  pub fn descriptor<'s>(
+    _scope: &mut v8::HandleScope<'s>,
+    _key: v8::Local<'s, v8::Name>,
+    _args: v8::PropertyCallbackArguments<'s>,
+    _rv: v8::ReturnValue,
+  ) {
+    unsafe { CALLS.push("descriptor".to_string()) }
+  }
+
+  pub fn setter<'s>(
+    _scope: &mut v8::HandleScope<'s>,
+    _key: v8::Local<'s, v8::Name>,
+    _value: v8::Local<'s, v8::Value>,
+    _args: v8::PropertyCallbackArguments<'s>,
+    _rv: v8::ReturnValue,
+  ) {
+    unsafe {
+      CALLS.push("setter".to_string());
+    }
+  }
+
+  fn definer<'s>(
+    _scope: &mut v8::HandleScope<'s>,
+    _key: v8::Local<'s, v8::Name>,
+    _descriptor: &v8::PropertyDescriptor,
+    _args: v8::PropertyCallbackArguments<'s>,
+    _rv: v8::ReturnValue,
+  ) {
+    unsafe {
+      CALLS.push("definer".to_string());
+    }
+  }
+
+  pub fn gt_middleware<'s>(
+    _scope: &mut v8::HandleScope<'s, ()>,
+    template: v8::Local<'s, v8::ObjectTemplate>,
+  ) -> v8::Local<'s, v8::ObjectTemplate> {
+    let mut config = v8::NamedPropertyHandlerConfiguration::new().flags(
+      v8::PropertyHandlerFlags::NON_MASKING
+        | v8::PropertyHandlerFlags::HAS_NO_SIDE_EFFECT,
+    );
+
+    config = config.descriptor_raw(descriptor.map_fn_to());
+    config = config.setter_raw(setter.map_fn_to());
+    config = config.definer_raw(definer.map_fn_to());
+
+    template.set_named_property_handler(config);
+
+    template
+  }
+
+  deno_core::extension!(test_ext, global_template_middleware = gt_middleware);
+  let mut runtime = JsRuntime::new(RuntimeOptions {
+    extensions: vec![test_ext::init_ops()],
+    ..Default::default()
+  });
+
+  // Create sleep function that waits for 2 seconds.
+  runtime
+    .execute_script(
+      "check_global_template_middleware.js",
+      r#"Object.defineProperty(globalThis, 'key', { value: 9, enumerable: true, configurable: true, writable: true })"#,
+    )
+    .unwrap();
+
+  let calls_set =
+    unsafe { CALLS.clone().into_iter().collect::<HashSet<String>>() };
+  assert!(calls_set.contains("definer"));
+  assert!(calls_set.contains("setter"));
+  assert!(calls_set.contains("descriptor"));
+}
diff --git a/core/runtime/tests/snapshot.rs b/core/runtime/tests/snapshot.rs
index cfc618a..64959c3 100644
--- a/core/runtime/tests/snapshot.rs
+++ b/core/runtime/tests/snapshot.rs
@@ -392,3 +392,66 @@ pub(crate) fn es_snapshot_without_runtime_module_loader() {
     r#"Uncaught (in promise) TypeError: Importing ext: modules is only allowed from ext: and node: modules. Tried to import ext:module_snapshot/test2.js from (no referrer)"#
   );
 }
+
+#[test]
+pub fn snapshot_with_additional_extensions() {
+  #[op2]
+  #[string]
+  fn op_before() -> String {
+    "before".to_owned()
+  }
+
+  #[op2]
+  #[string]
+  fn op_after() -> String {
+    "after".to_owned()
+  }
+
+  deno_core::extension!(
+    before_snapshot,
+    ops = [op_before],
+    esm_entry_point = "ext:module_snapshot/before.js",
+    esm = ["ext:module_snapshot/before.js" =
+      // If this throws, we accidentally tried to evaluate this module twice
+      { source = "if (globalThis.before) { throw 'twice?' } globalThis.before = () => { globalThis.BEFORE = Deno.core.ops.op_before(); };" },]
+  );
+  deno_core::extension!(
+    after_snapshot,
+    ops = [op_after],
+    esm_entry_point = "ext:module_snapshot/after.js",
+    esm = ["ext:module_snapshot/after.js" = {
+      source =
+        "globalThis.before(); globalThis.AFTER = Deno.core.ops.op_after();"
+    },]
+  );
+
+  let snapshot = {
+    let runtime = JsRuntimeForSnapshot::new(RuntimeOptions {
+      extensions: vec![before_snapshot::init_ops_and_esm()],
+      ..Default::default()
+    });
+
+    Box::leak(runtime.snapshot())
+  };
+
+  let mut runtime = JsRuntime::new(RuntimeOptions {
+    startup_snapshot: Some(snapshot),
+    extensions: vec![
+      before_snapshot::init_ops(),
+      after_snapshot::init_ops_and_esm(),
+    ],
+    ..Default::default()
+  });
+
+  // Make sure the module was evaluated.
+  {
+    let scope = &mut runtime.main_realm().handle_scope(runtime.v8_isolate());
+    let global_test: v8::Local<v8::String> =
+      JsRuntime::eval(scope, "globalThis.BEFORE + '/' + globalThis.AFTER")
+        .unwrap();
+    assert_eq!(
+      serde_v8::to_utf8(global_test.to_string(scope).unwrap(), scope),
+      String::from("before/after"),
+    );
+  }
+}
diff --git a/serde_v8/magic/bytestring.rs b/serde_v8/magic/bytestring.rs
index 5fa776f..671634f 100644
--- a/serde_v8/magic/bytestring.rs
+++ b/serde_v8/magic/bytestring.rs
@@ -1,29 +1,48 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use std::mem::size_of;
+use std::ops::Deref;
+use std::ops::DerefMut;
+
+use smallvec::SmallVec;
+
 use super::transl8::FromV8;
 use super::transl8::ToV8;
 use crate::magic::transl8::impl_magic;
 use crate::Error;
-use smallvec::SmallVec;
-use std::mem::size_of;
 
 const USIZE2X: usize = size_of::<usize>() * 2;
 
-#[derive(
-  PartialEq,
-  Eq,
-  Clone,
-  Debug,
-  Default,
-  derive_more::Deref,
-  derive_more::DerefMut,
-  derive_more::AsRef,
-  derive_more::AsMut,
-)]
-#[as_mut(forward)]
-#[as_ref(forward)]
+#[derive(PartialEq, Eq, Clone, Debug, Default)]
 pub struct ByteString(SmallVec<[u8; USIZE2X]>);
 impl_magic!(ByteString);
 
+impl Deref for ByteString {
+  type Target = SmallVec<[u8; USIZE2X]>;
+
+  fn deref(&self) -> &Self::Target {
+    &self.0
+  }
+}
+
+impl DerefMut for ByteString {
+  fn deref_mut(&mut self) -> &mut Self::Target {
+    &mut self.0
+  }
+}
+
+impl AsRef<[u8]> for ByteString {
+  fn as_ref(&self) -> &[u8] {
+    &self.0
+  }
+}
+
+impl AsMut<[u8]> for ByteString {
+  fn as_mut(&mut self) -> &mut [u8] {
+    &mut self.0
+  }
+}
+
 // const-assert that Vec<u8> and SmallVec<[u8; size_of::<usize>() * 2]> have a same size.
 // Note from https://docs.rs/smallvec/latest/smallvec/#union -
 //   smallvec can still be larger than Vec if the inline buffer is
diff --git a/testing/checkin/runner/mod.rs b/testing/checkin/runner/mod.rs
index 580957a..b7374a4 100644
--- a/testing/checkin/runner/mod.rs
+++ b/testing/checkin/runner/mod.rs
@@ -47,6 +47,7 @@ deno_core::extension!(
     ops::op_stats_dump,
     ops::op_stats_delete,
     ops_io::op_pipe_create,
+    ops_io::op_file_open,
     ops_async::op_async_yield,
     ops_async::op_async_barrier_create,
     ops_async::op_async_barrier_await,
@@ -176,8 +177,17 @@ async fn run_integration_test_task(
     for line in e.to_string().split('\n') {
       output.line(format!("[ERR] {line}"));
     }
+  } else {
+    // Only await the module if we didn't fail
+    if let Err(e) = f.await {
+      let state = runtime.op_state().clone();
+      let state = state.borrow();
+      let output: &Output = state.borrow();
+      for line in e.to_string().split('\n') {
+        output.line(format!("[ERR] {line}"));
+      }
+    }
   }
-  f.await?;
   let mut lines = runtime.op_state().borrow_mut().take::<Output>().take();
   lines.push(String::new());
   let mut expected_output = String::new();
diff --git a/testing/checkin/runner/ops_io.rs b/testing/checkin/runner/ops_io.rs
index f02c05e..6bd53a1 100644
--- a/testing/checkin/runner/ops_io.rs
+++ b/testing/checkin/runner/ops_io.rs
@@ -1,13 +1,17 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use anyhow::Error;
 use deno_core::op2;
 use deno_core::AsyncRefCell;
 use deno_core::BufView;
 use deno_core::OpState;
 use deno_core::RcRef;
 use deno_core::Resource;
+use deno_core::ResourceHandle;
 use deno_core::ResourceId;
 use deno_core::WriteOutcome;
 use futures::FutureExt;
+use std::cell::RefCell;
+use std::rc::Rc;
 use tokio::io::AsyncReadExt;
 use tokio::io::AsyncWriteExt;
 use tokio::io::DuplexStream;
@@ -65,3 +69,39 @@ pub fn op_pipe_create(op_state: &mut OpState) -> (ResourceId, ResourceId) {
   });
   (rid1, rid2)
 }
+
+struct FileResource {
+  handle: deno_core::ResourceHandle,
+}
+
+impl FileResource {
+  fn new(file: tokio::fs::File) -> Self {
+    let handle = ResourceHandle::from_fd_like(&file);
+    Self { handle }
+  }
+}
+
+impl Resource for FileResource {
+  fn backing_handle(self: Rc<Self>) -> Option<ResourceHandle> {
+    Some(self.handle)
+  }
+}
+
+#[op2(async)]
+#[serde]
+pub async fn op_file_open(
+  #[string] path: String,
+  op_state: Rc<RefCell<OpState>>,
+) -> Result<ResourceId, Error> {
+  let tokio_file = tokio::fs::OpenOptions::new()
+    .read(true)
+    .write(false)
+    .create(false)
+    .open(&path)
+    .await?;
+  let rid = op_state
+    .borrow_mut()
+    .resource_table
+    .add(FileResource::new(tokio_file));
+  Ok(rid)
+}
diff --git a/testing/integration/pending_unref_op_tla/pending_unref_op_tla.out b/testing/integration/pending_unref_op_tla/pending_unref_op_tla.out
new file mode 100644
index 0000000..7cf44be
--- /dev/null
+++ b/testing/integration/pending_unref_op_tla/pending_unref_op_tla.out
@@ -0,0 +1,2 @@
+should not panic
+didn't panic!
diff --git a/testing/integration/pending_unref_op_tla/pending_unref_op_tla.ts b/testing/integration/pending_unref_op_tla/pending_unref_op_tla.ts
new file mode 100644
index 0000000..a30a8aa
--- /dev/null
+++ b/testing/integration/pending_unref_op_tla/pending_unref_op_tla.ts
@@ -0,0 +1,7 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+console.log("should not panic");
+await new Promise((r) => {
+  const id = setTimeout(r, 1000);
+  Deno.unrefTimer(id);
+});
+console.log("didn't panic!");
diff --git a/testing/lib.rs b/testing/lib.rs
index e3ec71e..5933a29 100644
--- a/testing/lib.rs
+++ b/testing/lib.rs
@@ -58,6 +58,7 @@ integration_test!(
   error_ext_stack,
   error_with_stack,
   error_without_stack,
+  pending_unref_op_tla,
   smoke_test,
   timer_ref,
   timer_ref_and_cancel,
diff --git a/testing/unit/resource_test.ts b/testing/unit/resource_test.ts
index 2050555..39872cf 100644
--- a/testing/unit/resource_test.ts
+++ b/testing/unit/resource_test.ts
@@ -1,7 +1,7 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
 import { assert, assertArrayEquals, assertEquals, test } from "checkin:testing";
 
-const { op_pipe_create } = Deno.core.ensureFastOps();
+const { op_pipe_create, op_file_open } = Deno.core.ensureFastOps();
 
 test(async function testPipe() {
   const [p1, p2] = op_pipe_create();
@@ -47,3 +47,8 @@ test(function opsSyncBadResource() {
     assert(e instanceof Deno.core.BadResource);
   }
 });
+
+test(async function testFileIsNotTerminal() {
+  const file = await op_file_open("./README.md");
+  assert(!Deno.core.isTerminal(file));
+});
diff --git a/testing/unit/tc39_test.ts b/testing/unit/tc39_test.ts
index d4a8785..841c81d 100644
--- a/testing/unit/tc39_test.ts
+++ b/testing/unit/tc39_test.ts
@@ -1,5 +1,5 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-import { fail, test } from "checkin:testing";
+import { assert, fail, test } from "checkin:testing";
 
 // Verify that "array by copy" proposal is enabled (https://github.com/tc39/proposal-change-array-by-copy)
 test(function testArrayByCopy() {
@@ -38,3 +38,9 @@ test(function testIteratorHelpers() {
     fail("failed");
   }
 });
+
+// Verify that the "Temporal" proposal is enabled
+test(function testTemporalEnabled() {
+  // @ts-expect-error: Not available in TypeScript yet
+  assert(typeof Temporal !== "undefined");
+});