Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Rusty V8 Binding

V8 Version: 14.9.207.2
V8 Version: 15.0.245.2

[![ci](https://github.com/denoland/rusty_v8/workflows/ci/badge.svg?branch=main)](https://github.com/denoland/rusty_v8/actions)
[![crates](https://img.shields.io/crates/v/v8.svg)](https://crates.io/crates/v8)
Expand Down
2 changes: 1 addition & 1 deletion build
Submodule build updated 130 files
2 changes: 1 addition & 1 deletion buildtools
Submodule buildtools updated from c9110b to 17495e
38 changes: 36 additions & 2 deletions src/fast_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,35 @@ use std::ptr::NonNull;
pub struct CFunction(v8__CFunction);

impl CFunction {
pub const fn new(address: *const c_void, type_info: &CFunctionInfo) -> Self {
/// Construct a `CFunction` from a function address and its type info.
///
/// `type_info` is borrowed for `'static` because the resulting `CFunction`
/// stores the raw `v8::CFunctionInfo*` pointer and, once handed to V8 via
/// [`FunctionBuilder::build_fast`], that pointer is retained for the
/// lifetime of the `FunctionTemplate` (and copied into each fast-call
/// dispatch site). Anything other than `'static` storage for the type info
/// would let the pointer dangle.
///
/// In practice this is achieved by constructing the `CFunctionInfo` inside
/// a `const` initializer (where reference-to-temporary rvalues are promoted
/// to `'static`), exactly as the existing test/bench patterns do:
///
/// ```ignore
/// const FAST_CALL: v8::fast_api::CFunction = v8::fast_api::CFunction::new(
/// my_fast_fn as _,
/// &v8::fast_api::CFunctionInfo::new(
/// v8::fast_api::Type::Void.as_info(),
/// &[v8::fast_api::Type::V8Value.as_info()],
/// v8::fast_api::Int64Representation::Number,
/// ),
/// );
/// ```
///
/// [`FunctionBuilder::build_fast`]: crate::FunctionBuilder::build_fast
pub const fn new(
address: *const c_void,
type_info: &'static CFunctionInfo,
) -> Self {
Self(v8__CFunction {
address_: address,
type_info_: &type_info.0,
Expand All @@ -38,9 +66,15 @@ impl CFunctionInfo {
/// |arg_info| is an array of |arg_count| CTypeInfos describing the
/// arguments. Only the last argument may be of the special type
/// CTypeInfo::kCallbackOptionsType.
///
/// `arg_info` is borrowed for `'static` because the resulting
/// `CFunctionInfo` stores the raw `arg_info.as_ptr()` pointer and V8 reads
/// it on every fast-call invocation through the `CFunction` chain. As with
/// [`CFunction::new`], `const` initializers make this trivial: rvalues
/// such as `&[Type::V8Value.as_info()]` are promoted to `'static` storage.
pub const fn new(
return_info: CTypeInfo,
arg_info: &[CTypeInfo],
arg_info: &'static [CTypeInfo],
repr: Int64Representation,
) -> Self {
Self(v8__CFunctionInfo {
Expand Down
23 changes: 22 additions & 1 deletion src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,10 +702,31 @@ impl<'s> FunctionBuilder<'s, FunctionTemplate> {
/// useful to pass them explicitly - eg. when you are snapshotting you'd provide
/// the overloads and `CFunctionInfo` that would be placed in the external
/// references array.
///
/// # Lifetime invariant
///
/// `overloads` is `&'static [CFunction]` because, since
/// [crrev.com/c/7828135], V8 stores the raw `v8::CFunction` pointers it
/// receives via `NewWithCFunctionOverloads` directly inside
/// `FunctionTemplateInfo` (rather than copying them into a managed heap
/// object). The pointed-to storage must therefore outlive every
/// `FunctionTemplate` that references it. A `FunctionTemplate` may live
/// until isolate disposal, so in practice this requires the slice (and its
/// elements, including the `CFunctionInfo`s they reference) to have
/// `'static` storage. Use a `const` slice such as
///
/// ```ignore
/// const OVERLOADS: &[v8::fast_api::CFunction] = &[FAST_TEST];
/// ```
///
/// or a `static` item; do **not** synthesize the slice from stack-local
/// data.
///
/// [crrev.com/c/7828135]: https://chromium-review.googlesource.com/c/v8/v8/+/7828135
pub fn build_fast<'i>(
self,
scope: &PinScope<'s, 'i>,
overloads: &[CFunction],
overloads: &'static [CFunction],
) -> Local<'s, FunctionTemplate> {
unsafe {
scope.cast_local(|sd| {
Expand Down
31 changes: 31 additions & 0 deletions tests/compile_fail/build_fast_local_overloads.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2019-2026 the Deno authors. All rights reserved. MIT license.
//
// Regression test for denoland/rusty_v8#1989 (Orchid #160).
//
// Since crrev.com/c/7828135, V8 stores the raw `v8::CFunction` pointers
// passed to `NewWithCFunctionOverloads` directly inside `FunctionTemplateInfo`
// instead of copying them into a managed heap object. The pointed-to storage
// must therefore outlive the resulting `FunctionTemplate`, which in practice
// means it must be `'static`. `FunctionBuilder::build_fast` enforces this at
// the type level by requiring `&'static [v8::fast_api::CFunction]`, so a
// stack-local `Vec` of overloads must be rejected.

fn slow_fn(
_: &mut v8::PinScope,
_: v8::FunctionCallbackArguments,
_: v8::ReturnValue<v8::Value>,
) {
}

fn rejects_local_overloads(scope: &mut v8::PinScope<'_, '_>) {
let overloads: Vec<v8::fast_api::CFunction> = Vec::new();
let _ = v8::FunctionTemplate::builder(slow_fn).build_fast(scope, &overloads);
}

pub fn main() {
rejects_local_overloads(mock());
}

fn mock<T>() -> T {
unimplemented!()
}
12 changes: 12 additions & 0 deletions tests/compile_fail/build_fast_local_overloads.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0597]: `overloads` does not live long enough
--> tests/compile_fail/build_fast_local_overloads.rs:22:68
|
21 | let overloads: Vec<v8::fast_api::CFunction> = Vec::new();
| --------- binding `overloads` declared here
22 | let _ = v8::FunctionTemplate::builder(slow_fn).build_fast(scope, &overloads);
| ---------------------------------------------------------^^^^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `overloads` is borrowed for `'static`
23 | }
| - `overloads` dropped here while still borrowed
11 changes: 11 additions & 0 deletions tests/test_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4703,6 +4703,17 @@ fn security_token() {
let global = v8::Local::new(scope, global);
templ.set_named_property_handler(
v8::NamedPropertyHandlerConfiguration::new()
// NON_MASKING so the interceptor only fires for properties that are
// absent on the global (here just `variable`). Without it the handler
// would also intercept lookups of built-in globals, including those
// V8 performs while bootstrapping the context: since V8 15 shipped
// `Atomics.pause`, `Genesis::InitializeGlobal_js_atomics_pause` does a
// `GetProperty(global, "Atomics")` during context creation. A masking
// handler answers that with the *parent's* `Atomics` (which already
// has `pause`), so V8 reinstalls `pause` and aborts with a duplicate
// descriptor CHECK. NON_MASKING skips already-present properties and
// avoids the collision while preserving what this test exercises.
.flags(v8::PropertyHandlerFlags::NON_MASKING)
.getter(
|scope: &mut v8::PinScope,
key: v8::Local<v8::Name>,
Expand Down
2 changes: 1 addition & 1 deletion third_party/abseil-cpp
Submodule abseil-cpp updated from 5e42a3 to d16e32
2 changes: 1 addition & 1 deletion third_party/libc++/src
Submodule src updated from 99457f to 5abc7f
2 changes: 1 addition & 1 deletion third_party/libunwind/src
Submodule src updated from a2530b to d6c7a2
2 changes: 1 addition & 1 deletion third_party/llvm-libc/src
Submodule src updated from cb9527 to 9309c1
2 changes: 1 addition & 1 deletion third_party/partition_alloc
Submodule partition_alloc updated from fafdd4 to ff3b8b
2 changes: 1 addition & 1 deletion third_party/rust
Submodule rust updated from 2b055f to 26e8ff
2 changes: 1 addition & 1 deletion tools/auto_update_v8.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const V8_TRACKING_BRANCH = "14.9-lkgr-denoland";
const V8_TRACKING_BRANCH = "15.0-lkgr-denoland";
const AUTOROLL_BRANCH = "autoroll";

function extractVersion() {
Expand Down
2 changes: 1 addition & 1 deletion tools/clang
Submodule clang updated from 61150a to 45f4b9
2 changes: 1 addition & 1 deletion tools/win
Submodule win updated from d16e6b to faefd1
2 changes: 1 addition & 1 deletion v8
Submodule v8 updated 1665 files
Loading