Skip to content

Commit a2fc66e

Browse files
committed
Turbopack: Add bincode encode/decode wrappers for serde_json::Value when stored in a cell
1 parent 2931550 commit a2fc66e

File tree

6 files changed

+126
-23
lines changed

6 files changed

+126
-23
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
1-
use proc_macro2::Span;
21
use syn::{
3-
Meta, Result, Token, Type,
2+
MacroDelimiter, Meta, MetaList, Result, Token, Type,
43
parse::{Parse, ParseStream},
5-
spanned::Spanned,
64
};
75

8-
#[derive(Debug)]
96
pub struct PrimitiveInput {
107
pub ty: Type,
11-
pub manual_shrink_to_fit: Option<Span>,
8+
pub bincode_wrappers: Option<BincodeWrappers>,
129
}
1310

1411
impl Parse for PrimitiveInput {
1512
fn parse(input: ParseStream) -> Result<Self> {
1613
let ty: Type = input.parse()?;
1714
let mut parsed_input = PrimitiveInput {
1815
ty,
19-
manual_shrink_to_fit: None,
16+
bincode_wrappers: None,
2017
};
2118
if input.parse::<Option<Token![,]>>()?.is_some() {
2219
let punctuated = input.parse_terminated(Meta::parse, Token![,])?;
@@ -27,15 +24,26 @@ impl Parse for PrimitiveInput {
2724
.map(ToString::to_string)
2825
.as_deref()
2926
.unwrap_or_default(),
30-
&meta,
27+
meta,
3128
) {
32-
("manual_shrink_to_fit", Meta::Path(_)) => {
33-
parsed_input.manual_shrink_to_fit = Some(meta.span())
29+
("bincode_wrappers", meta) => {
30+
let Meta::List(MetaList {
31+
tokens: wrapper_tokens,
32+
delimiter: MacroDelimiter::Paren(..),
33+
..
34+
}) = meta
35+
else {
36+
return Err(syn::Error::new_spanned(
37+
meta,
38+
"expected parenthesized (EncodeTy, DecodeTy) list",
39+
));
40+
};
41+
parsed_input.bincode_wrappers = Some(syn::parse2(wrapper_tokens)?);
3442
}
3543
(_, meta) => {
3644
return Err(syn::Error::new_spanned(
3745
meta,
38-
"unexpected token, expected: \"manual_shrink_to_fit\"",
46+
"unexpected token, expected: \"bincode_wrappers\"",
3947
));
4048
}
4149
}
@@ -44,3 +52,28 @@ impl Parse for PrimitiveInput {
4452
Ok(parsed_input)
4553
}
4654
}
55+
56+
// TODO: wire this up in https://github.com/vercel/next.js/pull/86338
57+
#[allow(dead_code)]
58+
pub struct BincodeWrappers {
59+
pub encode_ty: Type,
60+
pub decode_ty: Type,
61+
}
62+
63+
impl Parse for BincodeWrappers {
64+
fn parse(input: ParseStream) -> Result<Self> {
65+
let punctuated = input.parse_terminated(Type::parse, Token![,])?;
66+
let items: [Type; 2] = punctuated
67+
.into_iter()
68+
.collect::<Vec<_>>()
69+
.try_into()
70+
.map_err(|_| {
71+
syn::Error::new(input.span(), "expected exactly two comma-separated types")
72+
})?;
73+
let (encode_ty, decode_ty) = items.into();
74+
Ok(BincodeWrappers {
75+
encode_ty,
76+
decode_ty,
77+
})
78+
}
79+
}

turbopack/crates/turbo-tasks-macros/src/primitive_macro.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ use crate::{
88
};
99

1010
pub fn primitive(input: TokenStream) -> TokenStream {
11-
let input = parse_macro_input!(input as PrimitiveInput);
11+
let PrimitiveInput {
12+
ty,
13+
bincode_wrappers: _,
14+
} = parse_macro_input!(input as PrimitiveInput);
1215

13-
let ty = input.ty;
1416
let Some(ident) = get_type_ident(&ty) else {
1517
return quote! {
1618
// An error occurred while parsing the ident.
@@ -34,7 +36,13 @@ pub fn primitive(input: TokenStream) -> TokenStream {
3436
}
3537
}
3638
};
37-
let name = global_name(quote! {stringify!(#ty) });
39+
40+
let name = global_name(quote!(stringify!(#ty)));
41+
// TODO: https://github.com/vercel/next.js/pull/86338 -- switch to bincode, use bincode wrapper
42+
let new_value_type = quote! {
43+
turbo_tasks::ValueType::new_with_any_serialization::<#ty>(#name);
44+
};
45+
3846
let value_type_and_register = value_type_and_register(
3947
&ident,
4048
quote! { #ty },
@@ -45,10 +53,8 @@ pub fn primitive(input: TokenStream) -> TokenStream {
4553
quote! {
4654
turbo_tasks::VcCellCompareMode<#ty>
4755
},
48-
quote! {
49-
turbo_tasks::ValueType::new_with_any_serialization::<#ty>(#name)
50-
},
51-
quote! { true },
56+
new_value_type,
57+
/* has_serialization */ quote! { true },
5258
);
5359

5460
let value_default_impl = quote! {

turbopack/crates/turbo-tasks/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ tokio = { workspace = true, features = ["full"] }
4848
tokio-util = { workspace = true }
4949
tracing = { workspace = true }
5050
triomphe = { workspace = true, features = ["unsize", "unstable"] }
51+
turbo-bincode = { workspace = true }
5152
turbo-dyn-eq-hash = { workspace = true }
5253
turbo-rcstr = { workspace = true }
5354
turbo-tasks-hash = { workspace = true }

turbopack/crates/turbo-tasks/src/primitives.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
use std::time::Duration;
22

3+
use bincode::{
4+
Decode, Encode,
5+
de::Decoder,
6+
enc::Encoder,
7+
error::{DecodeError, EncodeError},
8+
};
39
use turbo_rcstr::RcStr;
410
use turbo_tasks_macros::primitive as __turbo_tasks_internal_primitive;
511

612
use crate::{
7-
Vc, {self as turbo_tasks},
13+
self as turbo_tasks, Vc,
14+
value_type::{ManualDecodeWrapper, ManualEncodeWrapper},
815
};
916

1017
__turbo_tasks_internal_primitive!(());
11-
__turbo_tasks_internal_primitive!(String, manual_shrink_to_fit);
18+
__turbo_tasks_internal_primitive!(String);
1219
__turbo_tasks_internal_primitive!(RcStr);
1320
__turbo_tasks_internal_primitive!(Option<String>);
1421
__turbo_tasks_internal_primitive!(Option<RcStr>);
15-
__turbo_tasks_internal_primitive!(Vec<RcStr>, manual_shrink_to_fit);
22+
__turbo_tasks_internal_primitive!(Vec<RcStr>);
1623
__turbo_tasks_internal_primitive!(Option<u16>);
1724
__turbo_tasks_internal_primitive!(Option<u64>);
1825
__turbo_tasks_internal_primitive!(bool);
@@ -29,7 +36,46 @@ __turbo_tasks_internal_primitive!(i64);
2936
__turbo_tasks_internal_primitive!(i128);
3037
__turbo_tasks_internal_primitive!(usize);
3138
__turbo_tasks_internal_primitive!(isize);
32-
__turbo_tasks_internal_primitive!(serde_json::Value);
39+
__turbo_tasks_internal_primitive!(
40+
serde_json::Value,
41+
bincode_wrappers(JsonValueEncodeWrapper, JsonValueDecodeWrapper),
42+
);
3343
__turbo_tasks_internal_primitive!(Duration);
34-
__turbo_tasks_internal_primitive!(Vec<u8>, manual_shrink_to_fit);
35-
__turbo_tasks_internal_primitive!(Vec<bool>, manual_shrink_to_fit);
44+
__turbo_tasks_internal_primitive!(Vec<u8>);
45+
__turbo_tasks_internal_primitive!(Vec<bool>);
46+
47+
// TODO: use this in https://github.com/vercel/next.js/pull/86338
48+
#[allow(dead_code)]
49+
struct JsonValueEncodeWrapper<'a>(&'a serde_json::Value);
50+
51+
impl ManualEncodeWrapper for JsonValueEncodeWrapper<'_> {
52+
type Value = serde_json::Value;
53+
54+
fn new<'a>(value: &'a Self::Value) -> impl Encode + 'a {
55+
JsonValueEncodeWrapper(value)
56+
}
57+
}
58+
59+
impl Encode for JsonValueEncodeWrapper<'_> {
60+
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
61+
turbo_bincode::serde_json::encode(self.0, encoder)
62+
}
63+
}
64+
65+
// TODO: use this in https://github.com/vercel/next.js/pull/86338
66+
#[allow(dead_code)]
67+
struct JsonValueDecodeWrapper(serde_json::Value);
68+
69+
impl ManualDecodeWrapper for JsonValueDecodeWrapper {
70+
type Value = serde_json::Value;
71+
72+
fn inner(self) -> Self::Value {
73+
self.0
74+
}
75+
}
76+
77+
impl<Context> Decode<Context> for JsonValueDecodeWrapper {
78+
fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
79+
Ok(Self(turbo_bincode::serde_json::decode(decoder)?))
80+
}
81+
}

turbopack/crates/turbo-tasks/src/value_type.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::{
55
};
66

77
use auto_hash_map::{AutoMap, AutoSet};
8+
use bincode::{Decode, Encode};
89
use serde::{Deserialize, Serialize};
910
use tracing::Span;
1011

@@ -98,6 +99,21 @@ pub fn any_as_serialize<T: Any + Serialize + Send + Sync + 'static>(
9899
);
99100
}
100101

102+
// TODO: use this in https://github.com/vercel/next.js/pull/86338
103+
#[allow(dead_code)]
104+
pub trait ManualEncodeWrapper: Encode {
105+
type Value;
106+
// this uses RPIT to avoid some lifetime problems
107+
fn new<'a>(value: &'a Self::Value) -> impl Encode + 'a;
108+
}
109+
110+
// TODO: use this in https://github.com/vercel/next.js/pull/86338
111+
#[allow(dead_code)]
112+
pub trait ManualDecodeWrapper: Decode<()> {
113+
type Value;
114+
fn inner(self) -> Self::Value;
115+
}
116+
101117
impl ValueType {
102118
/// This is internally used by `#[turbo_tasks::value]`
103119
pub fn new<T: VcValueType>(global_name: &'static str) -> Self {

0 commit comments

Comments
 (0)