46f029e65c
* ipc as a deault feature * first part of purge * second part of ns purge
807 lines
24 KiB
Rust
807 lines
24 KiB
Rust
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
|
// This file is part of Parity.
|
|
|
|
// Parity is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Parity is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
use aster;
|
|
|
|
use syntax::ast::{
|
|
MetaItem,
|
|
Item,
|
|
Ident,
|
|
};
|
|
|
|
use syntax::ast;
|
|
use syntax::codemap::Span;
|
|
use syntax::ext::base::{Annotatable, ExtCtxt};
|
|
use syntax::ptr::P;
|
|
|
|
pub struct Error;
|
|
|
|
use super::codegen;
|
|
|
|
pub fn expand_serialization_implementation(
|
|
cx: &mut ExtCtxt,
|
|
span: Span,
|
|
meta_item: &MetaItem,
|
|
annotatable: &Annotatable,
|
|
push: &mut FnMut(Annotatable)
|
|
) {
|
|
let item = match *annotatable {
|
|
Annotatable::Item(ref item) => item,
|
|
_ => {
|
|
cx.span_err(meta_item.span, "`#[derive(Binary)]` may only be applied to structs and enums");
|
|
return;
|
|
},
|
|
};
|
|
|
|
let builder = aster::AstBuilder::new().span(span);
|
|
|
|
let impl_item = match serialize_item(cx, &builder, &item) {
|
|
Ok(item) => item,
|
|
Err(Error) => {
|
|
// An error occurred, but it should have been reported already.
|
|
return;
|
|
},
|
|
};
|
|
|
|
push(Annotatable::Item(impl_item))
|
|
}
|
|
|
|
fn serialize_item(
|
|
cx: &ExtCtxt,
|
|
builder: &aster::AstBuilder,
|
|
item: &Item,
|
|
) -> Result<P<ast::Item>, Error> {
|
|
let generics = match item.node {
|
|
ast::ItemKind::Struct(_, ref generics) => generics,
|
|
ast::ItemKind::Enum(_, ref generics) => generics,
|
|
_ => {
|
|
cx.span_err(
|
|
item.span,
|
|
"`#[derive(Binary)]` may only be applied to structs and enums");
|
|
return Err(Error);
|
|
},
|
|
};
|
|
|
|
let ty = builder.ty().path()
|
|
.segment(item.ident).with_generics(generics.clone()).build()
|
|
.build();
|
|
|
|
let where_clause = &generics.where_clause;
|
|
|
|
let binary_expressions = try!(binary_expr(cx,
|
|
&builder,
|
|
&item,
|
|
&generics,
|
|
ty.clone()));
|
|
|
|
let (size_expr, read_expr, write_expr) =
|
|
(binary_expressions.size, binary_expressions.read, binary_expressions.write);
|
|
|
|
match quote_item!(cx,
|
|
impl $generics ::ipc::BinaryConvertable for $ty $where_clause {
|
|
fn size(&self) -> usize {
|
|
$size_expr
|
|
}
|
|
|
|
fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut ::std::collections::VecDeque<usize>) -> Result<(), ::ipc::BinaryConvertError> {
|
|
$write_expr
|
|
}
|
|
|
|
fn from_bytes(buffer: &[u8], length_stack: &mut ::std::collections::VecDeque<usize>) -> Result<Self, ::ipc::BinaryConvertError> {
|
|
$read_expr
|
|
}
|
|
|
|
fn len_params() -> usize {
|
|
1
|
|
}
|
|
})
|
|
{
|
|
Some(item) => Ok(item),
|
|
None => {
|
|
cx.span_err(
|
|
item.span,
|
|
"syntax error expanding serialization implementation");
|
|
Err(Error)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(unreachable_code)]
|
|
fn binary_expr(
|
|
cx: &ExtCtxt,
|
|
builder: &aster::AstBuilder,
|
|
item: &Item,
|
|
impl_generics: &ast::Generics,
|
|
ty: P<ast::Ty>,
|
|
) -> Result<BinaryExpressions, Error> {
|
|
match item.node {
|
|
ast::ItemKind::Struct(ref variant_data, _) => {
|
|
binary_expr_item_struct(
|
|
cx,
|
|
builder,
|
|
impl_generics,
|
|
ty,
|
|
item.span,
|
|
variant_data,
|
|
)
|
|
},
|
|
ast::ItemKind::Enum(ref enum_def, _) => {
|
|
binary_expr_enum(
|
|
cx,
|
|
builder,
|
|
item.ident,
|
|
impl_generics,
|
|
ty,
|
|
item.span,
|
|
enum_def,
|
|
)
|
|
},
|
|
_ => {
|
|
cx.span_bug(item.span,
|
|
"expected ItemStruct or ItemEnum in #[derive(Binary)]");
|
|
Err(Error)
|
|
},
|
|
}
|
|
}
|
|
|
|
struct BinaryExpressions {
|
|
pub size: P<ast::Expr>,
|
|
pub write: P<ast::Expr>,
|
|
pub read: P<ast::Expr>,
|
|
}
|
|
|
|
fn replace_qualified(s: &str) -> String {
|
|
if let Some(pos) = s.find("<") {
|
|
let mut source = s.to_owned();
|
|
source.insert(pos, ':');
|
|
source.insert(pos, ':');
|
|
source
|
|
}
|
|
else { s.to_owned() }
|
|
}
|
|
|
|
fn binary_expr_struct(
|
|
cx: &ExtCtxt,
|
|
builder: &aster::AstBuilder,
|
|
ty: P<ast::Ty>,
|
|
fields: &[ast::StructField],
|
|
value_ident: Option<ast::Ident>,
|
|
instance_ident: Option<ast::Ident>,
|
|
) -> Result<BinaryExpressions, Error> {
|
|
|
|
let size_exprs: Vec<P<ast::Expr>> = fields.iter().enumerate().map(|(index, field)| {
|
|
let raw_ident = ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty));
|
|
let index_ident = builder.id(format!("__field{}", index));
|
|
match raw_ident.as_ref() {
|
|
"u8" => {
|
|
quote_expr!(cx, 1)
|
|
},
|
|
"[u8]" => {
|
|
value_ident.and_then(|x| {
|
|
let field_id = builder.id(field.ident.unwrap());
|
|
Some(quote_expr!(cx, $x. $field_id .len()))
|
|
})
|
|
.unwrap_or_else(|| {
|
|
quote_expr!(cx, $index_ident .len())
|
|
}
|
|
)
|
|
}
|
|
_ => {
|
|
let field_type_ident = builder.id(
|
|
&::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)));
|
|
|
|
let field_type_ident_qualified = builder.id(
|
|
replace_qualified(&::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty))));
|
|
|
|
value_ident.and_then(|x|
|
|
{
|
|
let field_id = builder.id(field.ident.unwrap());
|
|
Some(quote_expr!(cx,
|
|
match $field_type_ident_qualified::len_params() {
|
|
0 => ::std::mem::size_of::<$field_type_ident>(),
|
|
_ => $x. $field_id .size(),
|
|
}))
|
|
})
|
|
.unwrap_or_else(|| {
|
|
quote_expr!(cx, match $field_type_ident_qualified::len_params() {
|
|
0 => ::std::mem::size_of::<$field_type_ident>(),
|
|
_ => $index_ident .size(),
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}).collect();
|
|
|
|
let first_size_expr = size_exprs[0].clone();
|
|
let mut total_size_expr = quote_expr!(cx, 0usize + $first_size_expr);
|
|
for index in 1..size_exprs.len() {
|
|
let next_expr = size_exprs[index].clone();
|
|
total_size_expr = quote_expr!(cx, $total_size_expr + $next_expr);
|
|
}
|
|
|
|
let mut write_stmts = Vec::<ast::Stmt>::new();
|
|
write_stmts.push(quote_stmt!(cx, let mut offset = 0usize;).unwrap());
|
|
|
|
let mut map_stmts = Vec::<ast::Stmt>::new();
|
|
let field_amount = builder.id(&format!("{}",fields.len()));
|
|
map_stmts.push(quote_stmt!(cx, let mut map = vec![0usize; $field_amount];).unwrap());
|
|
map_stmts.push(quote_stmt!(cx, let mut total = 0usize;).unwrap());
|
|
|
|
let mut post_write_stmts = Vec::<ast::Stmt>::new();
|
|
|
|
for (index, field) in fields.iter().enumerate() {
|
|
let field_type_ident = builder.id(
|
|
&::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)));
|
|
|
|
let field_type_ident_qualified = builder.id(
|
|
replace_qualified(&::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty))));
|
|
|
|
let member_expr = match value_ident {
|
|
Some(x) => {
|
|
let field_id = builder.id(field.ident.unwrap());
|
|
quote_expr!(cx, $x . $field_id)
|
|
},
|
|
None => {
|
|
let index_ident = builder.id(format!("__field{}", index));
|
|
quote_expr!(cx, $index_ident)
|
|
},
|
|
};
|
|
|
|
let raw_ident = ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty));
|
|
let range_ident = builder.id(format!("r{}", index));
|
|
|
|
let error_message = "Error serializing member: ".to_owned() + &::syntax::print::pprust::expr_to_string(&member_expr);
|
|
let error_message_literal = builder.expr().lit().str::<&str>(&error_message);
|
|
|
|
match raw_ident.as_ref() {
|
|
"u8" => {
|
|
write_stmts.push(quote_stmt!(cx, let next_line = offset + 1;).unwrap());
|
|
write_stmts.push(quote_stmt!(cx, buffer[offset] = $member_expr; ).unwrap());
|
|
},
|
|
"[u8]" => {
|
|
write_stmts.push(quote_stmt!(cx, let size = $member_expr .len();).unwrap());
|
|
write_stmts.push(quote_stmt!(cx, let next_line = offset + size;).unwrap());
|
|
write_stmts.push(quote_stmt!(cx, length_stack.push_back(size);).unwrap());
|
|
write_stmts.push(quote_stmt!(cx, let $range_ident = offset..next_line; ).unwrap());
|
|
post_write_stmts.push(quote_stmt!(cx, buffer[$range_ident].clone_from_slice($member_expr); ).unwrap());
|
|
}
|
|
_ => {
|
|
write_stmts.push(quote_stmt!(cx, let next_line = offset + match $field_type_ident_qualified::len_params() {
|
|
0 => ::std::mem::size_of::<$field_type_ident>(),
|
|
_ => { let size = $member_expr .size(); length_stack.push_back(size); size },
|
|
}).unwrap());
|
|
write_stmts.push(quote_stmt!(cx, let $range_ident = offset..next_line; ).unwrap());
|
|
post_write_stmts.push(quote_stmt!(cx,
|
|
if $range_ident.end - $range_ident.start > 0 {
|
|
if let Err(e) = $member_expr .to_bytes(&mut buffer[$range_ident], length_stack) {
|
|
warn!(target: "ipc", $error_message_literal);
|
|
return Err(e)
|
|
};
|
|
}
|
|
).unwrap());
|
|
}
|
|
}
|
|
|
|
write_stmts.push(quote_stmt!(cx, offset = next_line; ).unwrap());
|
|
|
|
let field_index = builder.id(&format!("{}", index));
|
|
map_stmts.push(quote_stmt!(cx, map[$field_index] = total;).unwrap());
|
|
|
|
match raw_ident.as_ref() {
|
|
"u8" => {
|
|
map_stmts.push(quote_stmt!(cx, total += 1;).unwrap());
|
|
},
|
|
"[u8]" => {
|
|
map_stmts.push(quote_stmt!(cx, let size = length_stack.pop_front().unwrap();).unwrap());
|
|
map_stmts.push(quote_stmt!(cx, total += size;).unwrap());
|
|
},
|
|
_ => {
|
|
map_stmts.push(quote_stmt!(cx, let size = match $field_type_ident_qualified::len_params() {
|
|
0 => ::std::mem::size_of::<$field_type_ident>(),
|
|
_ => length_stack.pop_front().unwrap(),
|
|
}).unwrap());
|
|
map_stmts.push(quote_stmt!(cx, total += size;).unwrap());
|
|
}
|
|
}
|
|
};
|
|
|
|
let read_expr = match fields.iter().any(|f| codegen::has_ptr(&f.ty)) {
|
|
true => {
|
|
// cannot create structs with pointers
|
|
quote_expr!(cx, Err(::ipc::binary::BinaryConvertError::not_supported()))
|
|
},
|
|
false => {
|
|
if value_ident.is_some() {
|
|
let instance_create = named_fields_sequence(cx, &ty, fields);
|
|
quote_expr!(cx, { $map_stmts; $instance_create; Ok(result) })
|
|
}
|
|
else {
|
|
let map_variant = P(fields_sequence(cx, &ty, fields, &instance_ident.unwrap_or(builder.id("Self"))));
|
|
quote_expr!(cx, { $map_stmts; Ok($map_variant) })
|
|
}
|
|
},
|
|
};
|
|
|
|
Ok(BinaryExpressions {
|
|
size: total_size_expr,
|
|
write: quote_expr!(cx, { $write_stmts; $post_write_stmts; Ok(()) } ),
|
|
read: read_expr,
|
|
})
|
|
}
|
|
|
|
#[allow(unreachable_code)]
|
|
fn binary_expr_item_struct(
|
|
cx: &ExtCtxt,
|
|
builder: &aster::AstBuilder,
|
|
_impl_generics: &ast::Generics,
|
|
ty: P<ast::Ty>,
|
|
span: Span,
|
|
variant_data: &ast::VariantData,
|
|
) -> Result<BinaryExpressions, Error> {
|
|
match *variant_data {
|
|
ast::VariantData::Tuple(ref fields, _) => {
|
|
binary_expr_struct(
|
|
cx,
|
|
&builder,
|
|
ty,
|
|
fields,
|
|
Some(builder.id("self")),
|
|
None,
|
|
)
|
|
},
|
|
ast::VariantData::Struct(ref fields, _) => {
|
|
binary_expr_struct(
|
|
cx,
|
|
&builder,
|
|
ty,
|
|
fields,
|
|
Some(builder.id("self")),
|
|
None,
|
|
)
|
|
},
|
|
_ => {
|
|
cx.span_bug(span,
|
|
&format!("#[derive(Binary)] Unsupported struct content, expected tuple/struct, found: {:?}",
|
|
variant_data));
|
|
Err(Error)
|
|
},
|
|
}
|
|
}
|
|
|
|
fn binary_expr_enum(
|
|
cx: &ExtCtxt,
|
|
builder: &aster::AstBuilder,
|
|
type_ident: Ident,
|
|
impl_generics: &ast::Generics,
|
|
ty: P<ast::Ty>,
|
|
span: Span,
|
|
enum_def: &ast::EnumDef,
|
|
) -> Result<BinaryExpressions, Error> {
|
|
let arms: Vec<_> = try!(
|
|
enum_def.variants.iter()
|
|
.enumerate()
|
|
.map(|(variant_index, variant)| {
|
|
binary_expr_variant(
|
|
cx,
|
|
builder,
|
|
type_ident,
|
|
impl_generics,
|
|
ty.clone(),
|
|
span,
|
|
variant,
|
|
variant_index,
|
|
)
|
|
})
|
|
.collect()
|
|
);
|
|
|
|
let (size_arms, write_arms, mut read_arms) = (
|
|
arms.iter().map(|x| x.size.clone()).collect::<Vec<ast::Arm>>(),
|
|
arms.iter().map(|x| x.write.clone()).collect::<Vec<ast::Arm>>(),
|
|
arms.iter().map(|x| x.read.clone()).collect::<Vec<ast::Arm>>());
|
|
|
|
read_arms.push(quote_arm!(cx, _ => { Err(::ipc::BinaryConvertError::variant(buffer[0])) } ));
|
|
|
|
Ok(BinaryExpressions {
|
|
size: quote_expr!(cx, 1usize + match *self { $size_arms }),
|
|
write: quote_expr!(cx, match *self { $write_arms }; ),
|
|
read: quote_expr!(cx, match buffer[0] { $read_arms }),
|
|
})
|
|
}
|
|
|
|
struct BinaryArm {
|
|
size: ast::Arm,
|
|
write: ast::Arm,
|
|
read: ast::Arm,
|
|
}
|
|
|
|
fn fields_sequence(
|
|
ext_cx: &ExtCtxt,
|
|
_ty: &P<ast::Ty>,
|
|
fields: &[ast::StructField],
|
|
variant_ident: &ast::Ident,
|
|
) -> ast::Expr {
|
|
use syntax::parse::token;
|
|
use syntax::ast::TokenTree::Token;
|
|
|
|
let named_members = fields.iter().any(|f| f.ident.is_some());
|
|
|
|
::quasi::parse_expr_panic(&mut ::syntax::parse::new_parser_from_tts(
|
|
ext_cx.parse_sess(),
|
|
ext_cx.cfg(),
|
|
{
|
|
let _sp = ext_cx.call_site();
|
|
let mut tt = ::std::vec::Vec::new();
|
|
tt.push(Token(_sp, token::Ident(variant_ident.clone())));
|
|
if named_members {
|
|
tt.push(Token(_sp, token::OpenDelim(token::Brace)));
|
|
}
|
|
else {
|
|
tt.push(Token(_sp, token::OpenDelim(token::Paren)));
|
|
}
|
|
|
|
for (idx, field) in fields.iter().enumerate() {
|
|
if field.ident.is_some() {
|
|
tt.push(Token(_sp, token::Ident(field.ident.clone().unwrap())));
|
|
tt.push(Token(_sp, token::Colon));
|
|
}
|
|
|
|
// special case for u8, it just takes byte form sequence
|
|
if ::syntax::print::pprust::ty_to_string(&field.ty) == "u8" {
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer"))));
|
|
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx)))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
|
|
tt.push(Token(_sp, token::Comma));
|
|
continue;
|
|
}
|
|
|
|
// special case for [u8], it just takes a byte sequence
|
|
if ::syntax::print::pprust::ty_to_string(&field.ty) == "[u8]" {
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer"))));
|
|
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx)))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::DotDot));
|
|
|
|
if idx+1 != fields.len() {
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx+1)))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
}
|
|
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
|
|
tt.push(Token(_sp, token::Comma));
|
|
continue;
|
|
}
|
|
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("try!"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Paren)));
|
|
tt.push(
|
|
Token(
|
|
_sp,
|
|
token::Ident(ext_cx.ident_of(&replace_qualified(&::syntax::print::pprust::ty_to_string(&field.ty))))
|
|
));
|
|
tt.push(Token(_sp, token::ModSep));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("from_bytes"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Paren)));
|
|
|
|
tt.push(Token(_sp, token::BinOp(token::And)));
|
|
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer"))));
|
|
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx)))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::DotDot));
|
|
|
|
if idx+1 != fields.len() {
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx+1)))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
}
|
|
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
|
|
tt.push(Token(_sp, token::Comma));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("length_stack"))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Paren)));
|
|
|
|
// name member if it has resulted in the error
|
|
tt.push(Token(_sp, token::Dot));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map_err"))));
|
|
|
|
tt.push(Token(_sp, token::OpenDelim(token::Paren)));
|
|
tt.push(Token(_sp, token::BinOp(token::Or)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("e"))));
|
|
tt.push(Token(_sp, token::BinOp(token::Or)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("e"))));
|
|
tt.push(Token(_sp, token::Dot));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("named"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Paren)));
|
|
tt.push(Token(_sp, token::Literal(token::Lit::Str_(
|
|
field.ident.unwrap_or(ext_cx.ident_of(&format!("f{}", idx))).name),
|
|
None))
|
|
);
|
|
tt.push(Token(_sp, token::CloseDelim(token::Paren)));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Paren)));
|
|
|
|
tt.push(Token(_sp, token::CloseDelim(token::Paren)));
|
|
|
|
tt.push(Token(_sp, token::Comma));
|
|
}
|
|
if named_members {
|
|
tt.push(Token(_sp, token::CloseDelim(token::Brace)));
|
|
}
|
|
else {
|
|
tt.push(Token(_sp, token::CloseDelim(token::Paren)));
|
|
}
|
|
tt
|
|
})
|
|
).unwrap()
|
|
}
|
|
|
|
fn named_fields_sequence(
|
|
ext_cx: &ExtCtxt,
|
|
ty: &P<ast::Ty>,
|
|
fields: &[ast::StructField],
|
|
) -> ast::Stmt {
|
|
use syntax::parse::token;
|
|
use syntax::ast::TokenTree::Token;
|
|
|
|
::quasi::parse_stmt_panic(&mut ::syntax::parse::new_parser_from_tts(
|
|
ext_cx.parse_sess(),
|
|
ext_cx.cfg(),
|
|
{
|
|
let _sp = ext_cx.call_site();
|
|
let mut tt = ::std::vec::Vec::new();
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("let"))));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("result"))));
|
|
tt.push(Token(_sp, token::Eq));
|
|
|
|
tt.push(Token(
|
|
_sp,
|
|
token::Ident(
|
|
ext_cx.ident_of(&::syntax::print::pprust::ty_to_string(ty))
|
|
)));
|
|
|
|
tt.push(Token(_sp, token::OpenDelim(token::Brace)));
|
|
|
|
for (idx, field) in fields.iter().enumerate() {
|
|
tt.push(Token(_sp, token::Ident(field.ident.clone().expect("function is called for named fields"))));
|
|
tt.push(Token(_sp, token::Colon));
|
|
|
|
// special case for u8, it just takes byte form sequence
|
|
if ::syntax::print::pprust::ty_to_string(&field.ty) == "u8" {
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer"))));
|
|
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx)))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
|
|
tt.push(Token(_sp, token::Comma));
|
|
continue;
|
|
}
|
|
|
|
// special case for [u8], it just takes a byte sequence
|
|
if ::syntax::print::pprust::ty_to_string(&field.ty) == "[u8]" {
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer"))));
|
|
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx)))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::DotDot));
|
|
|
|
if idx+1 != fields.len() {
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx+1)))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
}
|
|
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
|
|
tt.push(Token(_sp, token::Comma));
|
|
continue;
|
|
}
|
|
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("try!"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Paren)));
|
|
tt.push(Token(
|
|
_sp,
|
|
token::Ident(
|
|
ext_cx.ident_of(&replace_qualified(&::syntax::print::pprust::ty_to_string(&field.ty)))
|
|
)));
|
|
tt.push(Token(_sp, token::ModSep));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("from_bytes"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Paren)));
|
|
|
|
tt.push(Token(_sp, token::BinOp(token::And)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer"))));
|
|
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx)))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::DotDot));
|
|
if idx + 1 != fields.len() {
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Bracket)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx+1)))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
}
|
|
|
|
tt.push(Token(_sp, token::CloseDelim(token::Bracket)));
|
|
|
|
tt.push(Token(_sp, token::Comma));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("length_stack"))));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Paren)));
|
|
|
|
// name member if it has resulted in the error
|
|
tt.push(Token(_sp, token::Dot));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map_err"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Paren)));
|
|
tt.push(Token(_sp, token::BinOp(token::Or)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("e"))));
|
|
tt.push(Token(_sp, token::BinOp(token::Or)));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("e"))));
|
|
tt.push(Token(_sp, token::Dot));
|
|
tt.push(Token(_sp, token::Ident(ext_cx.ident_of("named"))));
|
|
tt.push(Token(_sp, token::OpenDelim(token::Paren)));
|
|
tt.push(Token(_sp, token::Literal(token::Lit::Str_(
|
|
field.ident.unwrap_or(ext_cx.ident_of(&format!("f{}", idx))).name),
|
|
None))
|
|
);
|
|
tt.push(Token(_sp, token::CloseDelim(token::Paren)));
|
|
tt.push(Token(_sp, token::CloseDelim(token::Paren)));
|
|
|
|
tt.push(Token(_sp, token::CloseDelim(token::Paren)));
|
|
tt.push(Token(_sp, token::Comma));
|
|
}
|
|
|
|
tt.push(Token(_sp, token::CloseDelim(token::Brace)));
|
|
tt
|
|
})
|
|
).unwrap()
|
|
}
|
|
|
|
fn binary_expr_variant(
|
|
cx: &ExtCtxt,
|
|
builder: &aster::AstBuilder,
|
|
type_ident: Ident,
|
|
_generics: &ast::Generics,
|
|
ty: P<ast::Ty>,
|
|
_span: Span,
|
|
variant: &ast::Variant,
|
|
variant_index: usize,
|
|
) -> Result<BinaryArm, Error> {
|
|
let variant_ident = variant.node.name;
|
|
let variant_index_ident = builder.id(format!("{}", variant_index));
|
|
|
|
match variant.node.data {
|
|
ast::VariantData::Unit(_) => {
|
|
let pat = builder.pat().path()
|
|
.id(type_ident).id(variant_ident)
|
|
.build();
|
|
|
|
let variant_val = builder.id(format!("{}::{}", type_ident, variant_ident));
|
|
|
|
Ok(BinaryArm {
|
|
size: quote_arm!(cx, $pat => { 0usize } ),
|
|
write: quote_arm!(cx, $pat => { buffer[0] = $variant_index_ident; Ok(()) } ),
|
|
read: quote_arm!(cx, $variant_index_ident => { Ok($variant_val) } ),
|
|
})
|
|
},
|
|
ast::VariantData::Tuple(ref fields, _) => {
|
|
let field_names: Vec<ast::Ident> = (0 .. fields.len())
|
|
.map(|i| builder.id(format!("__field{}", i)))
|
|
.collect();
|
|
|
|
let pat = builder.pat().enum_()
|
|
.id(type_ident).id(variant_ident).build()
|
|
.with_pats(
|
|
field_names.iter()
|
|
.map(|field| builder.pat().ref_id(field))
|
|
)
|
|
.build();
|
|
|
|
let binary_expr = try!(binary_expr_struct(
|
|
cx,
|
|
&builder,
|
|
ty,
|
|
fields,
|
|
None,
|
|
Some(builder.id(format!("{}::{}", type_ident, variant_ident))),
|
|
));
|
|
|
|
let (size_expr, write_expr, read_expr) = (binary_expr.size, vec![binary_expr.write], binary_expr.read);
|
|
Ok(BinaryArm {
|
|
size: quote_arm!(cx, $pat => { $size_expr } ),
|
|
write: quote_arm!(cx,
|
|
$pat => {
|
|
buffer[0] = $variant_index_ident;
|
|
let buffer = &mut buffer[1..];
|
|
$write_expr
|
|
}),
|
|
read: quote_arm!(cx,
|
|
$variant_index_ident => {
|
|
let buffer = &buffer[1..];
|
|
$read_expr
|
|
}
|
|
),
|
|
})
|
|
},
|
|
ast::VariantData::Struct(ref fields, _) => {
|
|
let field_names: Vec<_> = (0 .. fields.len())
|
|
.map(|i| builder.id(format!("__field{}", i)))
|
|
.collect();
|
|
|
|
let pat = builder.pat().struct_()
|
|
.id(type_ident).id(variant_ident).build()
|
|
.with_pats(
|
|
field_names.iter()
|
|
.zip(fields.iter())
|
|
.map(|(id, field)|(field.ident.unwrap(), builder.pat().ref_id(id))))
|
|
.build();
|
|
|
|
let binary_expr = try!(binary_expr_struct(
|
|
cx,
|
|
&builder,
|
|
ty,
|
|
fields,
|
|
None,
|
|
Some(builder.id(format!("{}::{}", type_ident, variant_ident))),
|
|
));
|
|
|
|
let (size_expr, write_expr, read_expr) = (binary_expr.size, vec![binary_expr.write], binary_expr.read);
|
|
|
|
Ok(BinaryArm {
|
|
size: quote_arm!(cx, $pat => { $size_expr } ),
|
|
write: quote_arm!(cx,
|
|
$pat => {
|
|
buffer[0] = $variant_index_ident;
|
|
let buffer = &mut buffer[1..];
|
|
$write_expr
|
|
}),
|
|
read: quote_arm!(cx,
|
|
$variant_index_ident => {
|
|
let buffer = &buffer[1..];
|
|
$read_expr
|
|
}
|
|
),
|
|
})
|
|
},
|
|
}
|
|
}
|