diff --git a/Cargo.lock b/Cargo.lock index 3063273609c6c..21d9bacf7da07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9230,6 +9230,7 @@ dependencies = [ "tokio-util", "tracing", "triomphe 0.1.12", + "turbo-bincode", "turbo-dyn-eq-hash", "turbo-rcstr", "turbo-tasks-hash", diff --git a/turbopack/crates/turbo-tasks-macros/src/primitive_input.rs b/turbopack/crates/turbo-tasks-macros/src/primitive_input.rs index d76babd905947..92edd5b8cba08 100644 --- a/turbopack/crates/turbo-tasks-macros/src/primitive_input.rs +++ b/turbopack/crates/turbo-tasks-macros/src/primitive_input.rs @@ -1,14 +1,11 @@ -use proc_macro2::Span; use syn::{ - Meta, Result, Token, Type, + MacroDelimiter, Meta, MetaList, Result, Token, Type, parse::{Parse, ParseStream}, - spanned::Spanned, }; -#[derive(Debug)] pub struct PrimitiveInput { pub ty: Type, - pub manual_shrink_to_fit: Option, + pub bincode_wrappers: Option, } impl Parse for PrimitiveInput { @@ -16,7 +13,7 @@ impl Parse for PrimitiveInput { let ty: Type = input.parse()?; let mut parsed_input = PrimitiveInput { ty, - manual_shrink_to_fit: None, + bincode_wrappers: None, }; if input.parse::>()?.is_some() { let punctuated = input.parse_terminated(Meta::parse, Token![,])?; @@ -27,15 +24,26 @@ impl Parse for PrimitiveInput { .map(ToString::to_string) .as_deref() .unwrap_or_default(), - &meta, + meta, ) { - ("manual_shrink_to_fit", Meta::Path(_)) => { - parsed_input.manual_shrink_to_fit = Some(meta.span()) + ("bincode_wrappers", meta) => { + let Meta::List(MetaList { + tokens: wrapper_tokens, + delimiter: MacroDelimiter::Paren(..), + .. + }) = meta + else { + return Err(syn::Error::new_spanned( + meta, + "expected parenthesized (EncodeTy, DecodeTy) list", + )); + }; + parsed_input.bincode_wrappers = Some(syn::parse2(wrapper_tokens)?); } (_, meta) => { return Err(syn::Error::new_spanned( meta, - "unexpected token, expected: \"manual_shrink_to_fit\"", + "unexpected token, expected: \"bincode_wrappers\"", )); } } @@ -44,3 +52,28 @@ impl Parse for PrimitiveInput { Ok(parsed_input) } } + +// TODO: wire this up in https://github.com/vercel/next.js/pull/86338 +#[allow(dead_code)] +pub struct BincodeWrappers { + pub encode_ty: Type, + pub decode_ty: Type, +} + +impl Parse for BincodeWrappers { + fn parse(input: ParseStream) -> Result { + let punctuated = input.parse_terminated(Type::parse, Token![,])?; + let items: [Type; 2] = punctuated + .into_iter() + .collect::>() + .try_into() + .map_err(|_| { + syn::Error::new(input.span(), "expected exactly two comma-separated types") + })?; + let (encode_ty, decode_ty) = items.into(); + Ok(BincodeWrappers { + encode_ty, + decode_ty, + }) + } +} diff --git a/turbopack/crates/turbo-tasks-macros/src/primitive_macro.rs b/turbopack/crates/turbo-tasks-macros/src/primitive_macro.rs index dbd0011e695cb..130babc8eb610 100644 --- a/turbopack/crates/turbo-tasks-macros/src/primitive_macro.rs +++ b/turbopack/crates/turbo-tasks-macros/src/primitive_macro.rs @@ -8,9 +8,11 @@ use crate::{ }; pub fn primitive(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as PrimitiveInput); + let PrimitiveInput { + ty, + bincode_wrappers: _, + } = parse_macro_input!(input as PrimitiveInput); - let ty = input.ty; let Some(ident) = get_type_ident(&ty) else { return quote! { // An error occurred while parsing the ident. @@ -34,7 +36,13 @@ pub fn primitive(input: TokenStream) -> TokenStream { } } }; - let name = global_name(quote! {stringify!(#ty) }); + + let name = global_name(quote!(stringify!(#ty))); + // TODO: https://github.com/vercel/next.js/pull/86338 -- switch to bincode, use bincode wrapper + let new_value_type = quote! { + turbo_tasks::ValueType::new_with_any_serialization::<#ty>(#name); + }; + let value_type_and_register = value_type_and_register( &ident, quote! { #ty }, @@ -45,10 +53,8 @@ pub fn primitive(input: TokenStream) -> TokenStream { quote! { turbo_tasks::VcCellCompareMode<#ty> }, - quote! { - turbo_tasks::ValueType::new_with_any_serialization::<#ty>(#name) - }, - quote! { true }, + new_value_type, + /* has_serialization */ quote! { true }, ); let value_default_impl = quote! { diff --git a/turbopack/crates/turbo-tasks/Cargo.toml b/turbopack/crates/turbo-tasks/Cargo.toml index 71aeb02d79bd7..4c5ff7e480ac6 100644 --- a/turbopack/crates/turbo-tasks/Cargo.toml +++ b/turbopack/crates/turbo-tasks/Cargo.toml @@ -48,6 +48,7 @@ tokio = { workspace = true, features = ["full"] } tokio-util = { workspace = true } tracing = { workspace = true } triomphe = { workspace = true, features = ["unsize", "unstable"] } +turbo-bincode = { workspace = true } turbo-dyn-eq-hash = { workspace = true } turbo-rcstr = { workspace = true } turbo-tasks-hash = { workspace = true } diff --git a/turbopack/crates/turbo-tasks/src/primitives.rs b/turbopack/crates/turbo-tasks/src/primitives.rs index a9f2313f4d1f5..51bf5f8afc0dc 100644 --- a/turbopack/crates/turbo-tasks/src/primitives.rs +++ b/turbopack/crates/turbo-tasks/src/primitives.rs @@ -1,18 +1,25 @@ use std::time::Duration; +use bincode::{ + Decode, Encode, + de::Decoder, + enc::Encoder, + error::{DecodeError, EncodeError}, +}; use turbo_rcstr::RcStr; use turbo_tasks_macros::primitive as __turbo_tasks_internal_primitive; use crate::{ - Vc, {self as turbo_tasks}, + self as turbo_tasks, Vc, + value_type::{ManualDecodeWrapper, ManualEncodeWrapper}, }; __turbo_tasks_internal_primitive!(()); -__turbo_tasks_internal_primitive!(String, manual_shrink_to_fit); +__turbo_tasks_internal_primitive!(String); __turbo_tasks_internal_primitive!(RcStr); __turbo_tasks_internal_primitive!(Option); __turbo_tasks_internal_primitive!(Option); -__turbo_tasks_internal_primitive!(Vec, manual_shrink_to_fit); +__turbo_tasks_internal_primitive!(Vec); __turbo_tasks_internal_primitive!(Option); __turbo_tasks_internal_primitive!(Option); __turbo_tasks_internal_primitive!(bool); @@ -29,7 +36,46 @@ __turbo_tasks_internal_primitive!(i64); __turbo_tasks_internal_primitive!(i128); __turbo_tasks_internal_primitive!(usize); __turbo_tasks_internal_primitive!(isize); -__turbo_tasks_internal_primitive!(serde_json::Value); +__turbo_tasks_internal_primitive!( + serde_json::Value, + bincode_wrappers(JsonValueEncodeWrapper, JsonValueDecodeWrapper), +); __turbo_tasks_internal_primitive!(Duration); -__turbo_tasks_internal_primitive!(Vec, manual_shrink_to_fit); -__turbo_tasks_internal_primitive!(Vec, manual_shrink_to_fit); +__turbo_tasks_internal_primitive!(Vec); +__turbo_tasks_internal_primitive!(Vec); + +// TODO: use this in https://github.com/vercel/next.js/pull/86338 +#[allow(dead_code)] +struct JsonValueEncodeWrapper<'a>(&'a serde_json::Value); + +impl ManualEncodeWrapper for JsonValueEncodeWrapper<'_> { + type Value = serde_json::Value; + + fn new<'a>(value: &'a Self::Value) -> impl Encode + 'a { + JsonValueEncodeWrapper(value) + } +} + +impl Encode for JsonValueEncodeWrapper<'_> { + fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { + turbo_bincode::serde_json::encode(self.0, encoder) + } +} + +// TODO: use this in https://github.com/vercel/next.js/pull/86338 +#[allow(dead_code)] +struct JsonValueDecodeWrapper(serde_json::Value); + +impl ManualDecodeWrapper for JsonValueDecodeWrapper { + type Value = serde_json::Value; + + fn inner(self) -> Self::Value { + self.0 + } +} + +impl Decode for JsonValueDecodeWrapper { + fn decode>(decoder: &mut D) -> Result { + Ok(Self(turbo_bincode::serde_json::decode(decoder)?)) + } +} diff --git a/turbopack/crates/turbo-tasks/src/value_type.rs b/turbopack/crates/turbo-tasks/src/value_type.rs index c97188ebb1c03..217ac346b954b 100644 --- a/turbopack/crates/turbo-tasks/src/value_type.rs +++ b/turbopack/crates/turbo-tasks/src/value_type.rs @@ -5,6 +5,7 @@ use std::{ }; use auto_hash_map::{AutoMap, AutoSet}; +use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; use tracing::Span; @@ -98,6 +99,21 @@ pub fn any_as_serialize( ); } +// TODO: use this in https://github.com/vercel/next.js/pull/86338 +#[allow(dead_code)] +pub trait ManualEncodeWrapper: Encode { + type Value; + // this uses RPIT to avoid some lifetime problems + fn new<'a>(value: &'a Self::Value) -> impl Encode + 'a; +} + +// TODO: use this in https://github.com/vercel/next.js/pull/86338 +#[allow(dead_code)] +pub trait ManualDecodeWrapper: Decode<()> { + type Value; + fn inner(self) -> Self::Value; +} + impl ValueType { /// This is internally used by `#[turbo_tasks::value]` pub fn new(global_name: &'static str) -> Self {