Skip to content

Commit c4bc59c

Browse files
committed
Import from Firecracker
Signed-off-by: Adrian Catangiu <[email protected]>
1 parent 93f86c3 commit c4bc59c

File tree

12 files changed

+1082
-0
lines changed

12 files changed

+1082
-0
lines changed

Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "versionize_derive"
3+
version = "0.1.0"
4+
authors = ["Amazon Firecracker team <[email protected]>"]
5+
6+
[dependencies]
7+
proc-macro2 = ">=1.0"
8+
quote = ">=1.0"
9+
syn = { version = ">=1.0.13", features=["full", "extra-traits"]}
10+
11+
[lib]
12+
proc-macro = true
13+
14+
[dev-dependencies]
15+
versionize = { git = "https://github.com/firecracker-microvm/versionize" }
16+

src/common.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/// An interface for generating serialzer and deserializers based on
5+
/// field descriptions.
6+
pub trait Descriptor {
7+
/// Returns the serializer code block as a token stream.
8+
fn generate_serializer(&self) -> proc_macro2::TokenStream;
9+
/// Returns the deserializer code block as a token stream.
10+
fn generate_deserializer(&self) -> proc_macro2::TokenStream;
11+
/// Returns the curent version.
12+
fn version(&self) -> u16;
13+
/// Returns the type name as string.
14+
fn ty(&self) -> String;
15+
}
16+
17+
/// Describes a structure and it's fields.
18+
pub(crate) struct GenericDescriptor<T> {
19+
// The structure type identifier.
20+
pub ty: syn::Ident,
21+
pub version: u16,
22+
pub fields: Vec<T>,
23+
}
24+
25+
// A trait that defines an interface to check if a certain field
26+
// exists at a specified version.
27+
pub(crate) trait Exists {
28+
fn exists_at(&self, version: u16) -> bool {
29+
// All fields have a start version.
30+
// Some field do not have an end version specified.
31+
version >= self.start_version()
32+
&& (0 == self.end_version() || (self.end_version() > 0 && version < self.end_version()))
33+
}
34+
35+
fn start_version(&self) -> u16;
36+
fn end_version(&self) -> u16;
37+
}
38+
39+
// A trait that defines an interface for exposing a field type.
40+
pub(crate) trait FieldType {
41+
fn ty(&self) -> syn::Type;
42+
}
43+
44+
#[cfg(test)]
45+
mod tests {
46+
use super::Exists;
47+
48+
#[test]
49+
fn test_exists_at() {
50+
impl Exists for u32 {
51+
fn start_version(&self) -> u16 {
52+
3
53+
}
54+
55+
fn end_version(&self) -> u16 {
56+
5
57+
}
58+
}
59+
60+
let test = 1234;
61+
assert!(!test.exists_at(2));
62+
assert!(test.exists_at(3));
63+
assert!(test.exists_at(4));
64+
assert!(!test.exists_at(5));
65+
assert!(!test.exists_at(6));
66+
}
67+
}

src/descriptors/enum_desc.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use common::{Descriptor, GenericDescriptor};
5+
use fields::enum_variant::*;
6+
use helpers::compute_version;
7+
use quote::quote;
8+
9+
pub(crate) type EnumDescriptor = GenericDescriptor<EnumVariant>;
10+
11+
impl Descriptor for EnumDescriptor {
12+
fn generate_serializer(&self) -> proc_macro2::TokenStream {
13+
let mut versioned_serializers = proc_macro2::TokenStream::new();
14+
15+
for i in 1..=self.version {
16+
let mut versioned_serializer = proc_macro2::TokenStream::new();
17+
18+
for field in &self.fields {
19+
versioned_serializer.extend(field.generate_serializer(i));
20+
}
21+
22+
// Generate the match arm for version `i` serializer.
23+
versioned_serializers.extend(quote! {
24+
#i => {
25+
match self {
26+
#versioned_serializer
27+
}
28+
}
29+
});
30+
}
31+
32+
versioned_serializers
33+
}
34+
35+
// Versioned/semantic deserialization is not implemented for enums.
36+
fn generate_deserializer(&self) -> proc_macro2::TokenStream {
37+
let mut versioned_deserializers = proc_macro2::TokenStream::new();
38+
39+
for field in &self.fields {
40+
versioned_deserializers.extend(field.generate_deserializer());
41+
}
42+
43+
quote! {
44+
let variant_index = <u32 as Versionize>::deserialize(&mut reader, version_map, app_version)?;
45+
match variant_index {
46+
#versioned_deserializers
47+
x => return Err(VersionizeError::Deserialize(format!("Unknown variant_index {}", x)))
48+
}
49+
}
50+
}
51+
52+
fn version(&self) -> u16 {
53+
self.version
54+
}
55+
56+
fn ty(&self) -> String {
57+
self.ty.to_string()
58+
}
59+
}
60+
61+
impl EnumDescriptor {
62+
pub fn new(input: &syn::DataEnum, ident: syn::Ident) -> Self {
63+
let mut descriptor = EnumDescriptor {
64+
ty: ident,
65+
version: 1,
66+
fields: vec![],
67+
};
68+
69+
descriptor.parse_enum_variants(&input.variants);
70+
descriptor.version = compute_version(&descriptor.fields);
71+
descriptor
72+
}
73+
74+
fn parse_enum_variants(
75+
&mut self,
76+
variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
77+
) {
78+
for (index, variant) in variants.iter().enumerate() {
79+
self.fields
80+
.push(EnumVariant::new(self.version, variant, index as u32));
81+
}
82+
}
83+
}

src/descriptors/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
pub mod enum_desc;
5+
pub mod struct_desc;
6+
pub mod union_desc;

src/descriptors/struct_desc.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use common::{Descriptor, GenericDescriptor};
5+
use fields::struct_field::*;
6+
use helpers::compute_version;
7+
use quote::{format_ident, quote};
8+
9+
pub(crate) type StructDescriptor = GenericDescriptor<StructField>;
10+
11+
impl Descriptor for StructDescriptor {
12+
fn generate_serializer(&self) -> proc_macro2::TokenStream {
13+
let mut versioned_serializers = proc_macro2::TokenStream::new();
14+
15+
for i in 1..=self.version {
16+
let mut versioned_serializer = proc_macro2::TokenStream::new();
17+
let mut semantic_serializer = proc_macro2::TokenStream::new();
18+
19+
// Generate field and semantic serializers for all fields.
20+
// Not all fields have semantic serializers defined and some fields
21+
// might be missing in version `i`. In these cases the generate_serializer() and
22+
// generate_semantic_serializer() will return an empty token stream.
23+
for field in &self.fields {
24+
versioned_serializer.extend(field.generate_serializer(i));
25+
semantic_serializer.extend(field.generate_semantic_serializer(i));
26+
}
27+
28+
// Generate the match arm for version `i`.
29+
versioned_serializers.extend(quote! {
30+
#i => {
31+
#semantic_serializer
32+
#versioned_serializer
33+
}
34+
});
35+
}
36+
37+
versioned_serializers
38+
}
39+
40+
fn generate_deserializer(&self) -> proc_macro2::TokenStream {
41+
let mut versioned_deserializers = proc_macro2::TokenStream::new();
42+
let struct_ident = format_ident!("{}", self.ty);
43+
44+
for i in 1..=self.version {
45+
let mut versioned_deserializer = proc_macro2::TokenStream::new();
46+
let mut semantic_deserializer = proc_macro2::TokenStream::new();
47+
48+
// Generate field and semantic deserializers for all fields.
49+
// Not all fields have semantic deserializers defined and some fields
50+
// might be missing in version `i`. In these cases the generate_deserializer() and
51+
// generate_semantic_deserializer() will return an empty token stream.
52+
for field in &self.fields {
53+
versioned_deserializer.extend(field.generate_deserializer(i));
54+
semantic_deserializer.extend(field.generate_semantic_deserializer(i));
55+
}
56+
57+
// Generate the match arm for version `i`.
58+
//
59+
// The semantic deserialization functions will be called after the object is constructed
60+
// using the previously generated field deserializers.
61+
versioned_deserializers.extend(quote! {
62+
#i => {
63+
let mut object = #struct_ident {
64+
#versioned_deserializer
65+
};
66+
#semantic_deserializer
67+
Ok(object)
68+
}
69+
});
70+
}
71+
72+
// Generate code to map the app version to struct version and wrap the
73+
// deserializers with the `version` match.
74+
quote! {
75+
let version = version_map.get_type_version(app_version, <Self as Versionize>::type_id());
76+
match version {
77+
#versioned_deserializers
78+
_ => panic!("Unknown {:?} version {}.", <Self as Versionize>::type_id(), version)
79+
}
80+
}
81+
}
82+
83+
fn version(&self) -> u16 {
84+
self.version
85+
}
86+
87+
fn ty(&self) -> String {
88+
self.ty.to_string()
89+
}
90+
}
91+
92+
impl StructDescriptor {
93+
pub fn new(input: &syn::DataStruct, ident: syn::Ident) -> Self {
94+
let mut descriptor = StructDescriptor {
95+
ty: ident,
96+
version: 1, // struct start at version 1.
97+
fields: vec![],
98+
};
99+
100+
// Fills self.fields.
101+
descriptor.parse_struct_fields(&input.fields);
102+
descriptor.version = compute_version(&descriptor.fields);
103+
descriptor
104+
}
105+
106+
fn parse_struct_fields(&mut self, fields: &syn::Fields) {
107+
match fields {
108+
syn::Fields::Named(ref named_fields) => {
109+
let pairs = named_fields.named.pairs();
110+
for field in pairs.into_iter() {
111+
self.fields.push(StructField::new(self.version, field));
112+
}
113+
}
114+
_ => panic!("Only named fields are supported."),
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)