Rolling to V8 12.9.202.1 (#1579)

* Rolling to V8 12.9.202.1

* initial changes for 12.9

* CallbackScope for fast fns

* disable broken thing by default

* use windows 2022 runner

---------

Co-authored-by: snek <the@snek.dev>
This commit is contained in:
denobot 2024-08-20 10:48:10 -04:00 committed by GitHub
parent 1f5ebd9c4e
commit 3d29396d72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 98 additions and 151 deletions

View File

@ -62,7 +62,7 @@ jobs:
variant: release
cargo: cargo
- os: ${{ github.repository == 'denoland/rusty_v8' && 'windows-2019-xxl' || 'windows-2019' }}
- os: ${{ github.repository == 'denoland/rusty_v8' && 'windows-2022-xxl' || 'windows-2022' }}
target: x86_64-pc-windows-msvc
variant: release # Note: we do not support windows debug builds.
cargo: cargo

3
.gitmodules vendored
View File

@ -19,9 +19,6 @@
[submodule "buildtools"]
path = buildtools
url = https://chromium.googlesource.com/chromium/src/buildtools.git
[submodule "third_party/zlib"]
path = third_party/zlib
url = https://chromium.googlesource.com/chromium/src/third_party/zlib.git
[submodule "third_party/icu"]
path = third_party/icu
url = https://github.com/denoland/icu.git

View File

@ -1,6 +1,6 @@
# Rusty V8 Binding
V8 Version: 12.8.374.16
V8 Version: 12.9.202.1
[![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)

2
build

@ -1 +1 @@
Subproject commit 8cc2df0e909d0365e20cc0869e565149a723d2ca
Subproject commit c6d44e625aa64fa89cbdc971dfd301353bee04f3

View File

@ -47,7 +47,7 @@ fn main() {
"PYTHON",
"DISABLE_CLANG",
"EXTRA_GN_ARGS",
"NO_PRINT_GN_ARGS",
"PRINT_GN_ARGS",
"CARGO_ENCODED_RUSTFLAGS",
];
for env in envs {
@ -313,7 +313,7 @@ fn build_v8(is_asan: bool) {
let gn_out = maybe_gen(gn_args);
assert!(gn_out.exists());
assert!(gn_out.join("args.gn").exists());
if env::var_os("NO_PRINT_GN_ARGS").is_none() {
if env_bool("PRINT_GN_ARGS") {
print_gn_args(&gn_out);
}
build("rusty_v8", None);

@ -1 +1 @@
Subproject commit 3ef44a2b92d5dd1faa5189a06f3a5febe6db2d58
Subproject commit 60a590902cf146c282f15242401bd8543256e2a2

View File

@ -33,6 +33,9 @@ EACH_TYPED_ARRAY(TYPED_ARRAY_MAX_LENGTH)
using v8__CFunction = v8::CFunction;
using v8__CFunctionInfo = v8::CFunctionInfo;
using v8__FastApiArrayBufferView = v8::FastApiArrayBufferView;
using v8__FastOneByteString = v8::FastOneByteString;
using v8__FastApiTypedArray = v8::FastApiTypedArray<void>;
using v8__Isolate__UseCounterFeature = v8::Isolate::UseCounterFeature;

View File

@ -1,9 +1,9 @@
use std::ffi::c_void;
use crate::binding::*;
use crate::Isolate;
use crate::Local;
use crate::Value;
use std::ffi::c_void;
use std::marker::PhantomData;
#[derive(Clone, Copy)]
#[repr(transparent)]
@ -140,42 +140,26 @@ bitflags::bitflags! {
#[repr(C)]
pub struct FastApiCallbackOptions<'a> {
pub isolate: *mut Isolate,
/// If the callback wants to signal an error condition or to perform an
/// allocation, it must set options.fallback to true and do an early return
/// from the fast method. Then V8 checks the value of options.fallback and if
/// it's true, falls back to executing the SlowCallback, which is capable of
/// reporting the error (either by throwing a JS exception or logging to the
/// console) or doing the allocation. It's the embedder's responsibility to
/// ensure that the fast callback is idempotent up to the point where error and
/// fallback conditions are checked, because otherwise executing the slow
/// callback might produce visible side-effects twice.
pub fallback: bool,
/// The `data` passed to the FunctionTemplate constructor, or `undefined`.
pub data: Local<'a, Value>,
/// When called from WebAssembly, a view of the calling module's memory.
pub wasm_memory: *const FastApiTypedArray<u8>,
}
// https://source.chromium.org/chromium/chromium/src/+/main:v8/include/v8-fast-api-calls.h;l=336
#[repr(C)]
pub struct FastApiTypedArray<T: Default> {
/// Returns the length in number of elements.
pub length: usize,
// This pointer should include the typed array offset applied.
// It's not guaranteed that it's aligned to sizeof(T), it's only
// guaranteed that it's 4-byte aligned, so for 8-byte types we need to
// provide a special implementation for reading from it, which hides
// the possibly unaligned read in the `get` method.
data: *mut T,
}
#[allow(unused)] // only constructed by V8
#[repr(transparent)]
pub struct FastApiTypedArray<T: Default>(v8__FastApiTypedArray, PhantomData<T>);
impl<T: Default> FastApiTypedArray<T> {
/// Returns the length in number of elements.
pub const fn length(&self) -> usize {
self.0._base.length_
}
/// Performs an unaligned-safe read of T from the underlying data.
#[inline(always)]
pub const fn get(&self, index: usize) -> T {
debug_assert!(index < self.length);
debug_assert!(index < self.length());
// SAFETY: src is valid for reads, and is a valid value for T
unsafe { std::ptr::read_unaligned(self.data.add(index)) }
unsafe { std::ptr::read_unaligned((self.0.data_ as *const T).add(index)) }
}
/// Returns a slice pointing to the underlying data if safe to do so.
@ -183,14 +167,16 @@ impl<T: Default> FastApiTypedArray<T> {
pub fn get_storage_if_aligned(&self) -> Option<&mut [T]> {
// V8 may provide an invalid or null pointer when length is zero, so we just
// ignore that value completely and create an empty slice in this case.
if self.length == 0 {
if self.length() == 0 {
return Some(&mut []);
}
let data = self.0.data_ as *mut T;
// Ensure that we never return an unaligned or null buffer
if self.data.is_null() || (self.data as usize) % align_of::<T>() != 0 {
return None;
if data.is_null() || !data.is_aligned() {
None
} else {
Some(unsafe { std::slice::from_raw_parts_mut(data, self.length()) })
}
Some(unsafe { std::slice::from_raw_parts_mut(self.data, self.length) })
}
}
@ -200,19 +186,9 @@ impl<T: Default> FastApiTypedArray<T> {
/// own instance type. It could be supported if we specify that
/// TypedArray<T> always has precedence over the generic ArrayBufferView,
/// but this complicates overload resolution.
#[repr(C)]
pub struct FastApiArrayBufferView {
pub data: *mut c_void,
pub byte_length: usize,
}
pub type FastApiArrayBufferView = v8__FastApiArrayBufferView;
// FastApiOneByteString is an alias for SeqOneByteString and the type is widely used in deno_core.
#[allow(unused)]
#[repr(C)]
pub struct FastApiOneByteString {
data: *const u8,
pub length: u32,
}
pub type FastApiOneByteString = v8__FastOneByteString;
impl FastApiOneByteString {
#[inline(always)]
@ -224,6 +200,6 @@ impl FastApiOneByteString {
}
// SAFETY: The data is guaranteed to be valid for the length of the string.
unsafe { std::slice::from_raw_parts(self.data, self.length as usize) }
unsafe { std::slice::from_raw_parts(self.data as _, self.length as usize) }
}
}

View File

@ -102,6 +102,7 @@ use std::ops::DerefMut;
use std::ptr;
use std::ptr::NonNull;
use crate::fast_api::FastApiCallbackOptions;
use crate::function::FunctionCallbackInfo;
use crate::function::PropertyCallbackInfo;
use crate::Context;
@ -628,6 +629,7 @@ where
/// - `&FunctionCallbackInfo`
/// - `&PropertyCallbackInfo`
/// - `&PromiseRejectMessage`
/// - `&FastApiCallbackOptions`
#[derive(Debug)]
pub struct CallbackScope<'s, C = Context> {
_data: NonNull<data::ScopeData>,
@ -637,10 +639,23 @@ pub struct CallbackScope<'s, C = Context> {
impl<'s> CallbackScope<'s> {
#[allow(clippy::new_ret_no_self)]
pub unsafe fn new<P: param::NewCallbackScope<'s>>(param: P) -> P::NewScope {
let (isolate, context) = param.get_isolate_mut_and_maybe_current_context();
data::ScopeData::get_current_mut(isolate)
.new_callback_scope_data(context)
.as_scope()
let context = param.get_context();
let scope_data = data::ScopeData::get_current_mut(param.get_isolate_mut());
// A HandleScope is not implicitly created for
// fast functions, so one must be opened here.
let scope_data = if P::NEEDS_SCOPE {
if let Some(context) = context {
scope_data.new_handle_scope_data_with_context(&context)
} else {
scope_data.new_handle_scope_data()
}
} else {
scope_data.new_callback_scope_data(context)
};
// This scope needs to exit when dropped, as it
// must not live beyond the callback activation.
scope_data.disable_zombie();
scope_data.as_scope()
}
}
@ -1160,11 +1175,10 @@ mod param {
pub trait NewCallbackScope<'s>: Sized + getter::GetIsolate<'s> {
type NewScope: Scope;
const NEEDS_SCOPE: bool = false;
unsafe fn get_isolate_mut_and_maybe_current_context(
self,
) -> (&'s mut Isolate, Option<Local<'s, Context>>) {
(self.get_isolate_mut(), None)
fn get_context(&self) -> Option<Local<'s, Context>> {
None
}
}
@ -1184,13 +1198,16 @@ mod param {
type NewScope = CallbackScope<'s>;
}
impl<'s> NewCallbackScope<'s> for &'s FastApiCallbackOptions<'s> {
type NewScope = CallbackScope<'s>;
const NEEDS_SCOPE: bool = true;
}
impl<'s> NewCallbackScope<'s> for Local<'s, Context> {
type NewScope = CallbackScope<'s>;
unsafe fn get_isolate_mut_and_maybe_current_context(
self,
) -> (&'s mut Isolate, Option<Local<'s, Context>>) {
(getter::GetIsolate::get_isolate_mut(self), Some(self))
fn get_context(&self) -> Option<Local<'s, Context>> {
Some(*self)
}
}
@ -1241,6 +1258,12 @@ mod getter {
}
}
impl<'s> GetIsolate<'s> for &'s FastApiCallbackOptions<'s> {
unsafe fn get_isolate_mut(self) -> &'s mut Isolate {
&mut *self.isolate
}
}
impl<'s> GetIsolate<'s> for Local<'s, Context> {
unsafe fn get_isolate_mut(self) -> &'s mut Isolate {
&mut *raw::v8__Context__GetIsolate(&*self)
@ -1405,6 +1428,7 @@ pub(crate) mod data {
let isolate = data.isolate;
data.scope_type_specific_data.init_with(|| {
ScopeTypeSpecificData::HandleScope {
allow_zombie: true,
raw_handle_scope: unsafe { raw::HandleScope::uninit() },
raw_context_scope: None,
}
@ -1413,6 +1437,7 @@ pub(crate) mod data {
ScopeTypeSpecificData::HandleScope {
raw_handle_scope,
raw_context_scope,
..
} => {
unsafe { raw_handle_scope.init(isolate) };
init_context_fn(isolate, &mut data.context, raw_context_scope);
@ -1422,6 +1447,15 @@ pub(crate) mod data {
})
}
#[inline(always)]
pub(super) fn disable_zombie(&mut self) {
if let ScopeTypeSpecificData::HandleScope { allow_zombie, .. } =
&mut self.scope_type_specific_data
{
*allow_zombie = false;
}
}
#[inline(always)]
pub(super) fn new_handle_scope_data(&mut self) -> &mut Self {
self.new_handle_scope_data_with(|_, _, raw_context_scope| {
@ -1732,7 +1766,9 @@ pub(crate) mod data {
#[inline(always)]
pub(super) fn notify_scope_dropped(&mut self) {
match &self.scope_type_specific_data {
ScopeTypeSpecificData::HandleScope { .. }
ScopeTypeSpecificData::HandleScope {
allow_zombie: true, ..
}
| ScopeTypeSpecificData::EscapableHandleScope { .. } => {
// Defer scope exit until the parent scope is touched.
self.status.set(match self.status.get() {
@ -1864,6 +1900,7 @@ pub(crate) mod data {
_raw_context_scope: raw::ContextScope,
},
HandleScope {
allow_zombie: bool,
raw_handle_scope: raw::HandleScope,
raw_context_scope: Option<raw::ContextScope>,
},

View File

@ -10451,7 +10451,13 @@ fn host_create_shadow_realm_context_callback() {
#[test]
fn test_fast_calls() {
static mut WHO: &str = "none";
fn fast_fn(_recv: v8::Local<v8::Object>, a: u32, b: u32) -> u32 {
fn fast_fn(
_recv: v8::Local<v8::Object>,
a: u32,
b: u32,
options: &v8::fast_api::FastApiCallbackOptions,
) -> u32 {
let _scope = unsafe { v8::CallbackScope::new(options) };
unsafe { WHO = "fast" };
a + b
}
@ -10464,6 +10470,7 @@ fn test_fast_calls() {
fast_api::Type::V8Value.scalar(),
fast_api::Type::Uint32.scalar(),
fast_api::Type::Uint32.scalar(),
fast_api::Type::CallbackOptions.scalar(),
],
fast_api::Int64Representation::Number,
),
@ -10887,7 +10894,7 @@ fn test_fast_calls_overload() {
) {
unsafe { WHO = "fast_buf" };
let buf = unsafe { &*data };
assert_eq!(buf.length, 2);
assert_eq!(buf.length(), 2);
assert_eq!(buf.get(0), 6);
assert_eq!(buf.get(1), 9);
}
@ -10977,77 +10984,6 @@ fn test_fast_calls_overload() {
assert_eq!("fast_array", unsafe { WHO });
}
#[test]
fn test_fast_calls_callback_options_fallback() {
static mut WHO: &str = "none";
fn fast_fn(
_recv: v8::Local<v8::Object>,
options: *mut fast_api::FastApiCallbackOptions,
) {
if unsafe { WHO == "fast" } {
let options = unsafe { &mut *options };
options.fallback = true; // Go back to slow path.
} else {
unsafe { WHO = "fast" };
}
}
const FAST_TEST: fast_api::CFunction = fast_api::CFunction::new(
fast_fn as _,
&fast_api::CFunctionInfo::new(
fast_api::Type::Void.scalar(),
&[
fast_api::Type::V8Value.scalar(),
fast_api::Type::CallbackOptions.scalar(),
],
fast_api::Int64Representation::Number,
),
);
fn slow_fn(
scope: &mut v8::HandleScope,
_: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue<v8::Value>,
) {
unsafe { WHO = "slow" };
rv.set(v8::Boolean::new(scope, false).into());
}
let _setup_guard = setup::parallel_test();
let isolate = &mut v8::Isolate::new(Default::default());
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope, Default::default());
let scope = &mut v8::ContextScope::new(scope, context);
let global = context.global(scope);
let template =
v8::FunctionTemplate::builder(slow_fn).build_fast(scope, &[FAST_TEST]);
let name = v8::String::new(scope, "func").unwrap();
let value = template.get_function(scope).unwrap();
global.set(scope, name.into(), value.into()).unwrap();
let source = r#"
function f() { return func(); }
%PrepareFunctionForOptimization(f);
f();
"#;
eval(scope, source).unwrap();
assert_eq!("slow", unsafe { WHO });
let source = r#"
%OptimizeFunctionOnNextCall(f);
f();
"#;
eval(scope, source).unwrap();
assert_eq!("fast", unsafe { WHO });
let source = r#"
f(); // Second call fallbacks back to slow path.
"#;
eval(scope, source).unwrap();
assert_eq!("slow", unsafe { WHO });
}
#[test]
fn test_fast_calls_callback_options_data() {
static mut DATA: bool = false;
@ -11057,7 +10993,6 @@ fn test_fast_calls_callback_options_data() {
) {
let options = &mut *options;
if !options.data.is_external() {
options.fallback = true;
return;
}
@ -11079,11 +11014,10 @@ fn test_fast_calls_callback_options_data() {
);
fn slow_fn(
scope: &mut v8::HandleScope,
_: &mut v8::HandleScope,
_: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue<v8::Value>,
_: v8::ReturnValue<v8::Value>,
) {
rv.set(v8::Boolean::new(scope, false).into());
}
let _setup_guard = setup::parallel_test();

@ -1 +1 @@
Subproject commit 9d1552f25c3d9e9114b7d7aed55790570a99bc4d
Subproject commit ed3733b91e472a1e7a641c1f0c1e6c0ea698e958

@ -1 +1 @@
Subproject commit 6bb75caa139ee1e686d2205910454cf6ea212e58
Subproject commit f801c947082a3e0a4b48780303526b73905f6ecd

@ -1 +1 @@
Subproject commit a3c7d3e2f3e1e724b4651891b1a71257cbd88acc
Subproject commit eb6567388e89d9730c76dee71d68ac82e4a1abf6

@ -1 +1 @@
Subproject commit d09db732ff68f40fd3581306c650b17ea1955b4e
Subproject commit 116c20dae60d84a77005697cf29f72783f81b0f9

@ -1 +1 @@
Subproject commit 4dc76da47b1145e53e508a23c1bf2204cf5ee7ee
Subproject commit 63b7be17f8981d716ea9a0d65bb04654d79548a8

View File

@ -7,7 +7,7 @@ with open('./v8/DEPS') as f:
import subprocess
def process(name, dep):
if name == 'build':
if name == 'build' or name == 'third_party/icu':
# We have our own fork of this
return

2
v8

@ -1 +1 @@
Subproject commit 8e78e913dfed43460b215473fe39db2fce46984e
Subproject commit bc49fb862240af6bfa37dbbae09db7eafd3719e8