diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 385ce6d3..1ce3884a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.gitmodules b/.gitmodules index 0eef6803..dd6743c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/README.md b/README.md index dd14e0bb..8b9df299 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/build b/build index 8cc2df0e..c6d44e62 160000 --- a/build +++ b/build @@ -1 +1 @@ -Subproject commit 8cc2df0e909d0365e20cc0869e565149a723d2ca +Subproject commit c6d44e625aa64fa89cbdc971dfd301353bee04f3 diff --git a/build.rs b/build.rs index ffd8fa33..1bc2c773 100644 --- a/build.rs +++ b/build.rs @@ -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); diff --git a/buildtools b/buildtools index 3ef44a2b..60a59090 160000 --- a/buildtools +++ b/buildtools @@ -1 +1 @@ -Subproject commit 3ef44a2b92d5dd1faa5189a06f3a5febe6db2d58 +Subproject commit 60a590902cf146c282f15242401bd8543256e2a2 diff --git a/src/binding.hpp b/src/binding.hpp index 78b52024..8f16ccc3 100644 --- a/src/binding.hpp +++ b/src/binding.hpp @@ -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; using v8__Isolate__UseCounterFeature = v8::Isolate::UseCounterFeature; diff --git a/src/fast_api.rs b/src/fast_api.rs index 7c69b8e6..d51dc089 100644 --- a/src/fast_api.rs +++ b/src/fast_api.rs @@ -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, } -// https://source.chromium.org/chromium/chromium/src/+/main:v8/include/v8-fast-api-calls.h;l=336 -#[repr(C)] -pub struct FastApiTypedArray { - /// 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(v8__FastApiTypedArray, PhantomData); impl FastApiTypedArray { + /// 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 FastApiTypedArray { 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::() != 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 FastApiTypedArray { /// own instance type. It could be supported if we specify that /// TypedArray 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) } } } diff --git a/src/scope.rs b/src/scope.rs index c11b2088..9561336f 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -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, @@ -637,10 +639,23 @@ pub struct CallbackScope<'s, C = Context> { impl<'s> CallbackScope<'s> { #[allow(clippy::new_ret_no_self)] pub unsafe fn new>(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>) { - (self.get_isolate_mut(), None) + fn get_context(&self) -> Option> { + 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>) { - (getter::GetIsolate::get_isolate_mut(self), Some(self)) + fn get_context(&self) -> Option> { + 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, }, diff --git a/tests/test_api.rs b/tests/test_api.rs index ad433675..1d1f986f 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -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, a: u32, b: u32) -> u32 { + fn fast_fn( + _recv: v8::Local, + 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, - 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, - ) { - 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::ReturnValue, ) { - rv.set(v8::Boolean::new(scope, false).into()); } let _setup_guard = setup::parallel_test(); diff --git a/third_party/abseil-cpp b/third_party/abseil-cpp index 9d1552f2..ed3733b9 160000 --- a/third_party/abseil-cpp +++ b/third_party/abseil-cpp @@ -1 +1 @@ -Subproject commit 9d1552f25c3d9e9114b7d7aed55790570a99bc4d +Subproject commit ed3733b91e472a1e7a641c1f0c1e6c0ea698e958 diff --git a/third_party/libc++/src b/third_party/libc++/src index 6bb75caa..f801c947 160000 --- a/third_party/libc++/src +++ b/third_party/libc++/src @@ -1 +1 @@ -Subproject commit 6bb75caa139ee1e686d2205910454cf6ea212e58 +Subproject commit f801c947082a3e0a4b48780303526b73905f6ecd diff --git a/third_party/libc++abi/src b/third_party/libc++abi/src index a3c7d3e2..eb656738 160000 --- a/third_party/libc++abi/src +++ b/third_party/libc++abi/src @@ -1 +1 @@ -Subproject commit a3c7d3e2f3e1e724b4651891b1a71257cbd88acc +Subproject commit eb6567388e89d9730c76dee71d68ac82e4a1abf6 diff --git a/third_party/libunwind/src b/third_party/libunwind/src index d09db732..116c20da 160000 --- a/third_party/libunwind/src +++ b/third_party/libunwind/src @@ -1 +1 @@ -Subproject commit d09db732ff68f40fd3581306c650b17ea1955b4e +Subproject commit 116c20dae60d84a77005697cf29f72783f81b0f9 diff --git a/tools/clang b/tools/clang index 4dc76da4..63b7be17 160000 --- a/tools/clang +++ b/tools/clang @@ -1 +1 @@ -Subproject commit 4dc76da47b1145e53e508a23c1bf2204cf5ee7ee +Subproject commit 63b7be17f8981d716ea9a0d65bb04654d79548a8 diff --git a/tools/update_deps.py b/tools/update_deps.py index d06adf16..999f50b6 100644 --- a/tools/update_deps.py +++ b/tools/update_deps.py @@ -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 diff --git a/v8 b/v8 index 8e78e913..bc49fb86 160000 --- a/v8 +++ b/v8 @@ -1 +1 @@ -Subproject commit 8e78e913dfed43460b215473fe39db2fce46984e +Subproject commit bc49fb862240af6bfa37dbbae09db7eafd3719e8