Make deserialization faster (#324)

swc_ecma_ast:
 - make deserialization faster
This commit is contained in:
강동윤 2019-03-08 11:36:23 +09:00 committed by GitHub
parent dd5f17463e
commit 248a53ce7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 315 additions and 306 deletions

View File

@ -1,4 +1,4 @@
required_version = "1.0.1"
required_version = "1.0.3"
use_field_init_shorthand = true
merge_imports = true
wrap_comments = true

View File

@ -15,7 +15,6 @@ use swc_common::Fold;
use swc_common::{ast_node, Span};
#[ast_node]
#[derive(Deserialize)]
pub struct Class {
#[serde(default)]
pub span: Span,

View File

@ -4,12 +4,10 @@ use crate::{
stmt::BlockStmt,
typescript::{TsParamProp, TsTypeAnn, TsTypeParamDecl},
};
use serde::Deserialize;
use swc_common::{ast_node, Span};
/// Common parts of function and method.
#[ast_node]
#[derive(Deserialize)]
pub struct Function {
pub params: Vec<Pat>,

View File

@ -117,7 +117,7 @@ fn error_tests(tests: &mut Vec<TestDescAndFn>) -> Result<(), io::Error> {
const TYPES: &[&str] = &[
"fail", /* TODO
* "early" */
* "early" */
];
for err_type in TYPES {

View File

@ -273,8 +273,7 @@ test!(
{a6, a7} = {}) {}"#,
"function fn(a1, param, param1, a5, param2) {
var a2 = param === void 0 ? 4 : param, ref = param1 ? param1 : _throw(new TypeError(\"Cannot \
destructure 'undefined' or 'null'\")), a3 = ref.a3, a4 = ref.a4, ref1 = param2 === void 0 ? \
{
destructure 'undefined' or 'null'\")), a3 = ref.a3, a4 = ref.a4, ref1 = param2 === void 0 ? {
} : param2, ref2 = ref1 ? ref1 : _throw(new TypeError(\"Cannot destructure 'undefined' or \
'null'\")), a6 = ref2.a6, a7 = ref2.a7;
}

View File

@ -17,184 +17,10 @@ impl Parse for Args {
}
}
struct VariantAttr {
_paren_token: token::Paren,
tags: Punctuated<Lit, Token![,]>,
}
impl Parse for VariantAttr {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
Ok(VariantAttr {
_paren_token: parenthesized!(content in input),
tags: content.parse_terminated(Lit::parse)?,
})
}
}
pub fn expand_enum(
DeriveInput {
generics,
ident,
data,
..
}: DeriveInput,
) -> Vec<ItemImpl> {
let data = match data {
Data::Enum(data) => data,
_ => unreachable!("expand_enum is called with none-enum item"),
};
let deserialize = {
let mut all_tags: Punctuated<_, token::Comma> = Default::default();
let mut match_type = data
.variants
.iter()
.map(|variant| {
let field_type = match variant.fields {
Fields::Unnamed(ref fields) => {
assert!(
fields.unnamed.len() == 1,
"#[ast_node] enum cannot contain variant with multiple fields"
);
fields.unnamed.last().unwrap().into_value().ty.clone()
}
_ => {
unreachable!("#[ast_node] enum cannot contain named fields or unit variant")
}
};
let tags = variant
.attrs
.iter()
.filter_map(|attr| -> Option<VariantAttr> {
if !is_attr_name(attr, "tag") {
return None;
}
let tags =
parse2(attr.tts.clone()).expect("failed to parse #[tag] attribute");
Some(tags)
})
.flat_map(|v| v.tags)
.collect::<Punctuated<_, token::Comma>>();
assert!(
tags.len() >= 1,
"All #[ast_node] enum variants have one or more tag"
);
if tags.len() == 1
&& match tags.first().map(Pair::into_value) {
Some(Lit::Str(s)) => &*s.value() == "*",
_ => false,
}
{
Quote::new_call_site()
.quote_with(smart_quote!(
Vars {
Enum: &ident,
Variant: &variant.ident,
VariantFieldType: &field_type,
},
{
if let Ok(v) = std::result::Result::map(
<VariantFieldType as serde::Deserialize>::deserialize(
serde::private::de::ContentRefDeserializer::<D::Error>::new(
&content,
),
),
Enum::Variant,
) {
return Ok(v);
}
}
))
.parse()
} else {
for tag in tags.iter() {
all_tags.push(tag.clone());
}
Quote::new_call_site()
.quote_with(smart_quote!(
Vars {
Enum: &ident,
Variant: &variant.ident,
VariantFieldType: &field_type,
tags,
},
{
{
const TAGS: &[&str] = &[tags];
if TAGS.contains(&&*ty.ty) {
if let Ok(v) = std::result::Result::map(
<VariantFieldType as serde::Deserialize>::deserialize(
serde::private::de::ContentRefDeserializer::<
D::Error,
>::new(
&content
),
),
Enum::Variant,
) {
return Ok(v);
}
}
}
}
))
.parse()
}
})
.collect::<Vec<Expr>>();
let mut match_type_expr = Quote::new_call_site();
for expr in match_type {
match_type_expr = match_type_expr.quote_with(smart_quote!(Vars { expr }, { expr }));
}
match_type_expr = match_type_expr.quote_with(smart_quote!(Vars { all_tags }, {
return Err(serde::de::Error::unknown_variant(&ty.ty, &[all_tags]));
}));
Quote::new_call_site()
.quote_with(smart_quote!(
Vars {
match_type_expr,
Enum: &ident
},
{
impl<'de> serde::Deserialize<'de> for Enum {
fn deserialize<D>(Deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let content =
<serde::private::de::Content as serde::Deserialize>::deserialize(
Deserializer,
)?;
let ty = swc_common::serializer::Type::deserialize(
serde::private::de::ContentRefDeserializer::<D::Error>::new(
&content,
),
)?;
match_type_expr
}
}
}
))
.parse::<ItemImpl>()
.with_generics(generics.clone())
};
vec![deserialize]
}
pub fn expand_struct(args: Args, i: DeriveInput) -> Vec<ItemImpl> {
let mut items = vec![];
let generics = i.generics.clone();
let item_ident = Ident::new("Item", i.ident.span());
// let item_ident = Ident::new("Item", i.ident.span());
items.push(
Quote::new_call_site()
@ -213,132 +39,133 @@ pub fn expand_struct(args: Args, i: DeriveInput) -> Vec<ItemImpl> {
.with_generics(generics.clone()),
);
let ident = i.ident.clone();
let cloned = i.clone();
// let ident = i.ident.clone();
// let cloned = i.clone();
items.push({
let (fields, item_data) = match i.data {
Data::Struct(DataStruct {
struct_token,
semi_token,
fields: Fields::Named(FieldsNamed { brace_token, named }),
}) => {
let fields: Punctuated<_, token::Comma> = named
.clone()
.into_iter()
.map(|field| FieldValue {
member: Member::Named(field.ident.clone().unwrap()),
expr: Quote::new_call_site()
.quote_with(smart_quote!(
Vars {
field: &field.ident
},
{ node.node.field }
))
.parse(),
// items.push({
// let (fields, item_data) = match i.data {
// Data::Struct(DataStruct {
// struct_token,
// semi_token,
// fields: Fields::Named(FieldsNamed { brace_token, named }),
// }) => {
// let fields: Punctuated<_, token::Comma> = named
// .clone()
// .into_iter()
// .map(|field| FieldValue {
// member: Member::Named(field.ident.clone().unwrap()),
// expr: Quote::new_call_site()
// .quote_with(smart_quote!(
// Vars {
// field: &field.ident
// },
// { node.node.field }
// ))
// .parse(),
attrs: field
.attrs
.into_iter()
.filter(|attr| is_attr_name(attr, "cfg"))
.collect(),
colon_token: Some(call_site()),
})
.collect();
// attrs: field
// .attrs
// .into_iter()
// .filter(|attr| is_attr_name(attr, "cfg"))
// .collect(),
// colon_token: Some(call_site()),
// })
// .collect();
let item_data = Data::Struct(DataStruct {
struct_token,
semi_token,
fields: Fields::Named(FieldsNamed {
brace_token,
named: named
.into_pairs()
.map(|pair| {
let handle = |v: Field| Field {
vis: Visibility::Inherited,
attrs: v
.attrs
.into_iter()
.filter(|attr| {
is_attr_name(attr, "serde") || is_attr_name(attr, "cfg")
})
.collect(),
..v
};
// let item_data = Data::Struct(DataStruct {
// struct_token,
// semi_token,
// fields: Fields::Named(FieldsNamed {
// brace_token,
// named: named
// .into_pairs()
// .map(|pair| {
// let handle = |v: Field| Field {
// vis: Visibility::Inherited,
// attrs: v
// .attrs
// .into_iter()
// .filter(|attr| {
// is_attr_name(attr, "serde") ||
// is_attr_name(attr, "cfg") })
// .collect(),
// ..v
// };
match pair {
Pair::End(v) => Pair::End(handle(v)),
Pair::Punctuated(v, p) => Pair::Punctuated(handle(v), p),
}
})
.collect(),
}),
});
// match pair {
// Pair::End(v) => Pair::End(handle(v)),
// Pair::Punctuated(v, p) =>
// Pair::Punctuated(handle(v), p), }
// })
// .collect(),
// }),
// });
(fields, item_data)
}
_ => unreachable!("enum / tuple struct / union with #[ast_node(\"Foo\")]"),
};
// (fields, item_data)
// }
// _ => unreachable!("enum / tuple struct / union with
// #[ast_node(\"Foo\")]"), };
let convert_item_to_self = Quote::new_call_site().quote_with(smart_quote!(
Vars {
fields,
Type: &ident
},
{ Type { fields } }
));
// let convert_item_to_self =
// Quote::new_call_site().quote_with(smart_quote!( Vars {
// fields,
// Type: &ident
// },
// { Type { fields } }
// ));
let body = Quote::new_call_site().quote_with(smart_quote!(
Vars {
convert_item_to_self
},
{
let node = ::swc_common::serializer::Node::<Item>::deserialize(deserializer)?;
// let body = Quote::new_call_site().quote_with(smart_quote!(
// Vars {
// convert_item_to_self
// },
// {
// let node =
// ::swc_common::serializer::Node::<Item>::deserialize(deserializer)?;
if node.ty != <Self as ::swc_common::AstNode>::TYPE {
return Err(D::Error::unknown_variant(
&node.ty,
&[<Self as ::swc_common::AstNode>::TYPE],
));
}
// if node.ty != <Self as ::swc_common::AstNode>::TYPE {
// return Err(D::Error::unknown_variant(
// &node.ty,
// &[<Self as ::swc_common::AstNode>::TYPE],
// ));
// }
Ok(convert_item_to_self)
}
));
// Ok(convert_item_to_self)
// }
// ));
let item = DeriveInput {
vis: Visibility::Inherited,
ident: item_ident,
attrs: vec![],
data: item_data,
..cloned
};
Quote::new_call_site()
.quote_with(smart_quote!(
Vars {
// A new item which implements Deserialize
item,
Type: ident,
body
},
{
impl<'de> ::serde::Deserialize<'de> for Type {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
use ::serde::de::Error;
#[derive(::serde::Deserialize)]
#[serde(rename_all = "camelCase")]
item
body
}
}
}
))
.parse::<ItemImpl>()
.with_generics(generics)
});
// let item = DeriveInput {
// vis: Visibility::Inherited,
// ident: item_ident,
// attrs: vec![],
// data: item_data,
// ..cloned
// };
// Quote::new_call_site()
// .quote_with(smart_quote!(
// Vars {
// // A new item which implements Deserialize
// item,
// Type: ident,
// body
// },
// {
// impl<'de> ::serde::Deserialize<'de> for Type {
// fn deserialize<D>(deserializer: D) -> Result<Self,
// D::Error> where
// D: ::serde::Deserializer<'de>,
// {
// use ::serde::de::Error;
// #[derive(::serde::Deserialize)]
// #[serde(rename_all = "camelCase")]
// ite
// body
// }
// }
// }
// ))
// .parse::<ItemImpl>()
// .with_generics(generics)
// });
items
}

View File

@ -0,0 +1,180 @@
use pmutil::Quote;
use swc_macros_common::prelude::*;
use syn::{
self,
parse::{Parse, ParseStream},
*,
};
struct VariantAttr {
_paren_token: token::Paren,
tags: Punctuated<Lit, Token![,]>,
}
impl Parse for VariantAttr {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
Ok(VariantAttr {
_paren_token: parenthesized!(content in input),
tags: content.parse_terminated(Lit::parse)?,
})
}
}
pub fn expand(
DeriveInput {
generics,
ident,
data,
..
}: DeriveInput,
) -> Vec<ItemImpl> {
let data = match data {
Data::Enum(data) => data,
_ => unreachable!("expand_enum is called with none-enum item"),
};
let deserialize = {
let mut all_tags: Punctuated<_, token::Comma> = Default::default();
let mut match_type = data
.variants
.iter()
.map(|variant| {
let field_type = match variant.fields {
Fields::Unnamed(ref fields) => {
assert!(
fields.unnamed.len() == 1,
"#[ast_node] enum cannot contain variant with multiple fields"
);
fields.unnamed.last().unwrap().into_value().ty.clone()
}
_ => {
unreachable!("#[ast_node] enum cannot contain named fields or unit variant")
}
};
let tags = variant
.attrs
.iter()
.filter_map(|attr| -> Option<VariantAttr> {
if !is_attr_name(attr, "tag") {
return None;
}
let tags =
parse2(attr.tts.clone()).expect("failed to parse #[tag] attribute");
Some(tags)
})
.flat_map(|v| v.tags)
.collect::<Punctuated<_, token::Comma>>();
assert!(
tags.len() >= 1,
"All #[ast_node] enum variants have one or more tag"
);
if tags.len() == 1
&& match tags.first().map(Pair::into_value) {
Some(Lit::Str(s)) => &*s.value() == "*",
_ => false,
}
{
Quote::new_call_site()
.quote_with(smart_quote!(
Vars {
Enum: &ident,
Variant: &variant.ident,
VariantFieldType: &field_type,
},
{
if let Ok(v) = std::result::Result::map(
<VariantFieldType as serde::Deserialize>::deserialize(
serde::private::de::ContentRefDeserializer::<D::Error>::new(
&content,
),
),
Enum::Variant,
) {
return Ok(v);
}
}
))
.parse()
} else {
for tag in tags.iter() {
all_tags.push(tag.clone());
}
Quote::new_call_site()
.quote_with(smart_quote!(
Vars {
Enum: &ident,
Variant: &variant.ident,
VariantFieldType: &field_type,
tags,
},
{
{
const TAGS: &[&str] = &[tags];
if TAGS.contains(&&*ty.ty) {
if let Ok(v) = std::result::Result::map(
<VariantFieldType as serde::Deserialize>::deserialize(
serde::private::de::ContentRefDeserializer::<
D::Error,
>::new(
&content
),
),
Enum::Variant,
) {
return Ok(v);
}
}
}
}
))
.parse()
}
})
.collect::<Vec<Expr>>();
let mut match_type_expr = Quote::new_call_site();
for expr in match_type {
match_type_expr = match_type_expr.quote_with(smart_quote!(Vars { expr }, { expr }));
}
match_type_expr = match_type_expr.quote_with(smart_quote!(Vars { all_tags }, {
return Err(serde::de::Error::unknown_variant(&ty.ty, &[all_tags]));
}));
Quote::new_call_site()
.quote_with(smart_quote!(
Vars {
match_type_expr,
Enum: &ident
},
{
impl<'de> serde::Deserialize<'de> for Enum {
fn deserialize<D>(Deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let content =
<serde::private::de::Content as serde::Deserialize>::deserialize(
Deserializer,
)?;
let ty = swc_common::serializer::Type::deserialize(
serde::private::de::ContentRefDeserializer::<D::Error>::new(
&content,
),
)?;
match_type_expr
}
}
}
))
.parse::<ItemImpl>()
.with_generics(generics.clone())
};
vec![deserialize]
}

View File

@ -14,6 +14,7 @@ use swc_macros_common::prelude::*;
use syn::*;
mod ast_node_macro;
mod enum_deserialize;
mod fold;
mod from_variant;
mod spanned;
@ -87,7 +88,7 @@ pub fn derive_deserialize_enum(input: proc_macro::TokenStream) -> proc_macro::To
let input = parse::<DeriveInput>(input).expect("failed to parse input as DeriveInput");
let item =
ast_node_macro::expand_enum(input)
enum_deserialize::expand(input)
.into_iter()
.fold(TokenStream::new(), |mut t, item| {
item.to_tokens(&mut t);
@ -117,8 +118,15 @@ pub fn ast_node(
}
item.quote_with(smart_quote!(Vars { input }, {
#[derive(::swc_common::FromVariant, ::swc_common::Spanned, Clone, Debug, PartialEq)]
#[derive(::serde::Serialize, ::swc_common::DeserializeEnum)]
#[derive(
::swc_common::FromVariant,
::swc_common::Spanned,
Clone,
Debug,
PartialEq,
::serde::Serialize,
::swc_common::DeserializeEnum,
)]
#[serde(untagged)]
#[cfg_attr(feature = "fold", derive(::swc_common::Fold))]
input
@ -161,7 +169,7 @@ pub fn ast_node(
let mut quote =
item.quote_with(smart_quote!(Vars { input, serde_tag, serde_rename }, {
#[derive(::swc_common::Spanned, Clone, Debug, PartialEq)]
#[derive(::serde::Serialize)]
#[derive(::serde::Serialize, ::serde::Deserialize)]
serde_tag
#[serde(rename_all = "camelCase")]
serde_rename

View File

@ -4,8 +4,7 @@ extern crate swc_common;
use serde::{Deserialize, Serialize};
use swc_common::{ast_node, Fold, Span, Spanned};
#[ast_node]
#[derive(Deserialize)]
#[ast_node("Class")]
// See https://github.com/rust-lang/rust/issues/44925
pub struct Class {
#[span]
@ -14,8 +13,7 @@ pub struct Class {
pub s: String,
}
#[ast_node]
#[derive(Deserialize)]
#[ast_node("Tuple")]
pub struct Tuple(#[span] HasSpan, #[fold(ignore)] usize, usize);
#[derive(Debug, Clone, PartialEq, Fold, Spanned, Serialize, Deserialize)]