finished removing ipc
This commit is contained in:
@@ -1,898 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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,
|
||||
ImplItemKind,
|
||||
MethodSig,
|
||||
Arg,
|
||||
PatKind,
|
||||
FunctionRetTy,
|
||||
Ty,
|
||||
TraitRef,
|
||||
Ident,
|
||||
Generics,
|
||||
TraitItemKind,
|
||||
};
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::ptr::P;
|
||||
|
||||
pub struct Error;
|
||||
|
||||
const RESERVED_MESSAGE_IDS: u16 = 16;
|
||||
|
||||
pub fn expand_ipc_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, "`#[ipc]` may only be applied to implementations and traits");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
let builder = aster::AstBuilder::new().span(span);
|
||||
|
||||
let interface_map = match implement_interface(cx, &builder, &item, push, meta_item) {
|
||||
Ok(interface_map) => interface_map,
|
||||
Err(Error) => { return; },
|
||||
};
|
||||
|
||||
push_client(cx, &builder, &interface_map, push);
|
||||
|
||||
push(Annotatable::Item(interface_map.item));
|
||||
}
|
||||
|
||||
macro_rules! literal {
|
||||
($builder:ident, $($arg:tt)*) => {
|
||||
$builder.expr().lit().str::<&str>(&format!($($arg)*))
|
||||
}
|
||||
}
|
||||
|
||||
fn field_name(builder: &aster::AstBuilder, arg: &Arg) -> ast::Ident {
|
||||
match arg.pat.node {
|
||||
PatKind::Ident(_, ref ident, _) => builder.id(ident.node),
|
||||
_ => { panic!("unexpected param in interface: {:?}", arg.pat.node) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_slice_u8(builder: &aster::AstBuilder, ty: &P<ast::Ty>) -> P<ast::Ty> {
|
||||
if ::syntax::print::pprust::ty_to_string(&strip_ptr(ty)) == "[u8]" {
|
||||
return builder.ty().id("Vec<u8>")
|
||||
}
|
||||
ty.clone()
|
||||
}
|
||||
|
||||
struct NamedSignature<'a> {
|
||||
sig: &'a MethodSig,
|
||||
ident: &'a Ident,
|
||||
}
|
||||
|
||||
fn push_invoke_signature_aster(
|
||||
builder: &aster::AstBuilder,
|
||||
named_signature: &NamedSignature,
|
||||
push: &mut FnMut(Annotatable),
|
||||
) -> Dispatch {
|
||||
let inputs = &named_signature.sig.decl.inputs;
|
||||
let (input_type_name, input_arg_names, input_arg_tys) = if inputs.len() > 0 {
|
||||
let first_field_name = field_name(builder, &inputs[0]).name.as_str();
|
||||
if &*first_field_name == "self" && inputs.len() == 1 { (None, vec![], vec![]) }
|
||||
else {
|
||||
let skip = if &*first_field_name == "self" { 2 } else { 1 };
|
||||
let name_str = format!("{}_input", named_signature.ident.name.as_str());
|
||||
|
||||
let mut arg_names = Vec::new();
|
||||
let mut arg_tys = Vec::new();
|
||||
|
||||
let arg_name = format!("{}", field_name(builder, &inputs[skip-1]).name);
|
||||
let arg_ty = &inputs[skip-1].ty;
|
||||
|
||||
let mut tree = builder.item()
|
||||
.attr().word("binary")
|
||||
.attr().word("allow(non_camel_case_types)")
|
||||
.struct_(name_str.as_str())
|
||||
.field(arg_name.as_str())
|
||||
.ty().build(replace_slice_u8(builder, &strip_ptr(arg_ty)));
|
||||
|
||||
arg_names.push(arg_name);
|
||||
arg_tys.push(arg_ty.clone());
|
||||
for arg in inputs.iter().skip(skip) {
|
||||
let arg_name = format!("{}", field_name(builder, &arg));
|
||||
let arg_ty = &arg.ty;
|
||||
|
||||
tree = tree.field(arg_name.as_str()).ty().build(replace_slice_u8(builder, &strip_ptr(arg_ty)));
|
||||
arg_names.push(arg_name);
|
||||
arg_tys.push(arg_ty.clone());
|
||||
}
|
||||
|
||||
push(Annotatable::Item(tree.build()));
|
||||
(Some(name_str.to_owned()), arg_names, arg_tys)
|
||||
}
|
||||
}
|
||||
else {
|
||||
(None, vec![], vec![])
|
||||
};
|
||||
|
||||
let return_type_ty = match named_signature.sig.decl.output {
|
||||
FunctionRetTy::Ty(ref ty) => {
|
||||
let name_str = format!("{}_output", named_signature.ident.name.as_str());
|
||||
let tree = builder.item()
|
||||
.attr().word("binary")
|
||||
.attr().word("allow(non_camel_case_types)")
|
||||
.struct_(name_str.as_str())
|
||||
.field(format!("payload")).ty().build(ty.clone());
|
||||
push(Annotatable::Item(tree.build()));
|
||||
Some(ty.clone())
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
|
||||
Dispatch {
|
||||
function_name: format!("{}", named_signature.ident.name.as_str()),
|
||||
input_type_name: input_type_name,
|
||||
input_arg_names: input_arg_names,
|
||||
input_arg_tys: input_arg_tys,
|
||||
return_type_ty: return_type_ty,
|
||||
}
|
||||
}
|
||||
|
||||
struct Dispatch {
|
||||
function_name: String,
|
||||
input_type_name: Option<String>,
|
||||
input_arg_names: Vec<String>,
|
||||
input_arg_tys: Vec<P<Ty>>,
|
||||
return_type_ty: Option<P<Ty>>,
|
||||
}
|
||||
|
||||
// This is the expanded version of this:
|
||||
//
|
||||
// let invoke_serialize_stmt = quote_stmt!(cx, {
|
||||
// ::bincode::serde::serialize(& $output_type_id { payload: self. $function_name ($hand_param_a, $hand_param_b) }, ::bincode::SizeLimit::Infinite).unwrap()
|
||||
// });
|
||||
//
|
||||
// But the above does not allow comma-separated expressions for arbitrary number
|
||||
// of parameters ...$hand_param_a, $hand_param_b, ... $hand_param_n
|
||||
fn implement_dispatch_arm_invoke_stmt(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
dispatch: &Dispatch,
|
||||
) -> ast::Stmt
|
||||
{
|
||||
use ::syntax::tokenstream::TokenTree::Token;
|
||||
let function_name = builder.id(dispatch.function_name.as_str());
|
||||
|
||||
let input_args_exprs = dispatch.input_arg_names.iter().enumerate().map(|(arg_index, arg_name)| {
|
||||
let arg_ident = builder.id(arg_name);
|
||||
let expr = quote_expr!(cx, input. $arg_ident);
|
||||
if has_ptr(&dispatch.input_arg_tys[arg_index]) { quote_expr!(cx, & $expr) }
|
||||
else { expr }
|
||||
}).collect::<Vec<P<ast::Expr>>>();
|
||||
|
||||
let ext_cx = &*cx;
|
||||
::quasi::parse_stmt_panic(&mut ::syntax::parse::new_parser_from_tts(
|
||||
ext_cx.parse_sess(),
|
||||
{
|
||||
let _sp = ext_cx.call_site();
|
||||
let mut tt = ::std::vec::Vec::new();
|
||||
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
|
||||
|
||||
if dispatch.return_type_ty.is_some() {
|
||||
tt.push(Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("ipc"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("binary"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
|
||||
}
|
||||
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Dot));
|
||||
tt.extend(::quasi::ToTokens::to_tokens(&function_name, ext_cx).into_iter());
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
|
||||
for arg_expr in input_args_exprs {
|
||||
tt.extend(::quasi::ToTokens::to_tokens(&arg_expr, ext_cx).into_iter());
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Comma));
|
||||
}
|
||||
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
|
||||
if dispatch.return_type_ty.is_some() {
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Dot));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
}
|
||||
else {
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Semi));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Vec"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::ModSep));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("new"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
|
||||
}
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
|
||||
|
||||
tt
|
||||
}
|
||||
)).unwrap()
|
||||
}
|
||||
|
||||
fn implement_dispatch_arm_invoke(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
dispatch: &Dispatch,
|
||||
buffer: bool,
|
||||
) -> P<ast::Expr>
|
||||
{
|
||||
let deserialize_expr = if buffer {
|
||||
quote_expr!(cx,
|
||||
::ipc::binary::deserialize(buf)
|
||||
.unwrap_or_else(|e| { panic!("ipc error while deserializing payload, aborting \n payload: {:?}, \n error: {:?}", buf, e); } )
|
||||
)
|
||||
} else {
|
||||
quote_expr!(cx,
|
||||
::ipc::binary::deserialize_from(r)
|
||||
.unwrap_or_else(|e| { panic!("ipc error while deserializing payload, aborting \n error: {:?}", e); } )
|
||||
)
|
||||
};
|
||||
|
||||
let invoke_serialize_stmt = implement_dispatch_arm_invoke_stmt(cx, builder, dispatch);
|
||||
dispatch.input_type_name.as_ref().map(|val| {
|
||||
let input_type_id = builder.id(val.clone().as_str());
|
||||
quote_expr!(cx, {
|
||||
let input: $input_type_id = $deserialize_expr;
|
||||
$invoke_serialize_stmt
|
||||
})
|
||||
}).unwrap_or(quote_expr!(cx, { $invoke_serialize_stmt }))
|
||||
}
|
||||
|
||||
/// generates dispatch match for method id
|
||||
fn implement_dispatch_arm(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
index: u32,
|
||||
dispatch: &Dispatch,
|
||||
buffer: bool,
|
||||
) -> ast::Arm
|
||||
{
|
||||
let index_ident = builder.id(format!("{}", index + (RESERVED_MESSAGE_IDS as u32)).as_str());
|
||||
let invoke_expr = implement_dispatch_arm_invoke(cx, builder, dispatch, buffer);
|
||||
let trace = literal!(builder, "Dispatching: {}", &dispatch.function_name);
|
||||
quote_arm!(cx, $index_ident => {
|
||||
trace!(target: "ipc", $trace);
|
||||
$invoke_expr
|
||||
})
|
||||
}
|
||||
|
||||
fn implement_dispatch_arms(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
dispatches: &[Dispatch],
|
||||
buffer: bool,
|
||||
) -> Vec<ast::Arm>
|
||||
{
|
||||
let mut index = -1;
|
||||
dispatches.iter()
|
||||
.map(|dispatch| { index = index + 1; implement_dispatch_arm(cx, builder, index as u32, dispatch, buffer) }).collect()
|
||||
}
|
||||
|
||||
pub fn strip_ptr(ty: &P<ast::Ty>) -> P<ast::Ty> {
|
||||
if let ast::TyKind::Rptr(_, ref ptr_mut) = ty.node {
|
||||
ptr_mut.ty.clone()
|
||||
}
|
||||
else { ty.clone() }
|
||||
}
|
||||
|
||||
pub fn has_ptr(ty: &P<ast::Ty>) -> bool {
|
||||
if let ast::TyKind::Rptr(_, ref _ptr_mut) = ty.node {
|
||||
true
|
||||
}
|
||||
else { false }
|
||||
}
|
||||
|
||||
/// returns an expression with the body for single operation that is being sent to server
|
||||
/// operation itself serializes input, writes to socket and waits for socket to respond
|
||||
/// (the latter only if original method signature returns anyting)
|
||||
///
|
||||
/// assuming expanded class contains method
|
||||
/// fn commit(&self, f: u32) -> u32
|
||||
///
|
||||
/// the expanded implementation will generate method for the client like that
|
||||
/// #[binary]
|
||||
/// struct Request<'a> {
|
||||
/// f: &'a u32,
|
||||
/// }
|
||||
/// let payload = Request{f: &f,};
|
||||
/// let mut socket_ref = self.socket.borrow_mut();
|
||||
/// let mut socket = socket_ref.deref_mut();
|
||||
/// let serialized_payload = ::bincode::serde::serialize(&payload, ::bincode::SizeLimit::Infinite).unwrap();
|
||||
/// ::ipc::invoke(0, &Some(serialized_payload), &mut socket);
|
||||
/// while !socket.ready().load(::std::sync::atomic::Ordering::Relaxed) { }
|
||||
/// ::bincode::serde::deserialize_from::<_, u32>(&mut socket, ::bincode::SizeLimit::Infinite).unwrap()
|
||||
fn implement_client_method_body(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
index: u16,
|
||||
interface_map: &InterfaceMap,
|
||||
) -> P<ast::Expr>
|
||||
{
|
||||
use ::syntax::tokenstream::TokenTree::Token;
|
||||
|
||||
let dispatch = &interface_map.dispatches[index as usize];
|
||||
let index_ident = builder.id(format!("{}", index + RESERVED_MESSAGE_IDS).as_str());
|
||||
|
||||
let request = if dispatch.input_arg_names.len() > 0 {
|
||||
|
||||
let arg_name = dispatch.input_arg_names[0].as_str();
|
||||
let static_ty = strip_ptr(&dispatch.input_arg_tys[0]);
|
||||
let arg_ty = builder
|
||||
.ty().ref_()
|
||||
.lifetime("'a")
|
||||
.ty()
|
||||
.build(static_ty.clone());
|
||||
|
||||
let mut tree = builder.item()
|
||||
.attr().word("binary")
|
||||
.struct_("Request")
|
||||
.generics()
|
||||
.lifetime_name("'a")
|
||||
.build()
|
||||
.field(arg_name).ty()
|
||||
.build(arg_ty);
|
||||
|
||||
for arg_idx in 1..dispatch.input_arg_names.len() {
|
||||
let arg_name = dispatch.input_arg_names[arg_idx].as_str();
|
||||
let static_ty = strip_ptr(&dispatch.input_arg_tys[arg_idx]);
|
||||
|
||||
let arg_ty = builder
|
||||
.ty().ref_()
|
||||
.lifetime("'a")
|
||||
.ty()
|
||||
.build(static_ty);
|
||||
tree = tree.field(arg_name).ty().build(arg_ty);
|
||||
|
||||
}
|
||||
let mut request_serialization_statements = Vec::new();
|
||||
|
||||
let struct_tree = tree.build();
|
||||
let struct_stmt = quote_stmt!(cx, $struct_tree);
|
||||
request_serialization_statements.push(struct_stmt);
|
||||
|
||||
// actually this is just expanded version of this:
|
||||
// request_serialization_statements.push(quote_stmt!(cx, let payload = Request { p1: &p1, p2: &p2, ... pn: &pn, }));
|
||||
// again, cannot dynamically create expression with arbitrary number of comma-separated members
|
||||
request_serialization_statements.push({
|
||||
let ext_cx = &*cx;
|
||||
::quasi::parse_stmt_panic(&mut ::syntax::parse::new_parser_from_tts(
|
||||
ext_cx.parse_sess(),
|
||||
{
|
||||
let _sp = ext_cx.call_site();
|
||||
let mut tt = ::std::vec::Vec::new();
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("let"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("payload"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Eq));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Request"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
|
||||
|
||||
for arg in dispatch.input_arg_names.iter() {
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg.as_str()))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Colon));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
|
||||
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg.as_str()))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Comma));
|
||||
}
|
||||
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
|
||||
tt
|
||||
}))
|
||||
});
|
||||
|
||||
request_serialization_statements.push(
|
||||
quote_stmt!(cx, let mut socket = self.socket.write().unwrap(); ));
|
||||
|
||||
request_serialization_statements.push(
|
||||
quote_stmt!(cx, let serialized_payload = ::ipc::binary::serialize(&payload).unwrap()));
|
||||
|
||||
request_serialization_statements.push(
|
||||
quote_stmt!(cx, ::ipc::invoke($index_ident, &Some(serialized_payload), &mut *socket)));
|
||||
|
||||
|
||||
request_serialization_statements
|
||||
}
|
||||
else {
|
||||
let mut request_serialization_statements = Vec::new();
|
||||
request_serialization_statements.push(
|
||||
quote_stmt!(cx, let mut socket = self.socket.write().unwrap(); ));
|
||||
request_serialization_statements.push(
|
||||
quote_stmt!(cx, ::ipc::invoke($index_ident, &None, &mut *socket)));
|
||||
request_serialization_statements
|
||||
};
|
||||
|
||||
let trace = literal!(builder, "Invoking: {}", &dispatch.function_name);
|
||||
if let Some(ref return_ty) = dispatch.return_type_ty {
|
||||
let return_expr = quote_expr!(cx,
|
||||
::ipc::binary::deserialize_from::<$return_ty, _>(&mut *socket).unwrap()
|
||||
);
|
||||
quote_expr!(cx, {
|
||||
trace!(target: "ipc", $trace);
|
||||
$request;
|
||||
$return_expr
|
||||
})
|
||||
}
|
||||
else {
|
||||
quote_expr!(cx, {
|
||||
trace!(target: "ipc", $trace);
|
||||
$request
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates signature and body (see `implement_client_method_body`)
|
||||
/// for the client (signature is identical to the original method)
|
||||
fn implement_client_method(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
index: u16,
|
||||
interface_map: &InterfaceMap,
|
||||
)
|
||||
-> ast::ImplItem
|
||||
{
|
||||
use ::syntax::tokenstream::TokenTree::Token;
|
||||
|
||||
let dispatch = &interface_map.dispatches[index as usize];
|
||||
let method_name = builder.id(dispatch.function_name.as_str());
|
||||
let body = implement_client_method_body(cx, builder, index, interface_map);
|
||||
|
||||
let ext_cx = &*cx;
|
||||
// expanded version of this
|
||||
// pub fn $method_name(&self, p1: p1_ty, p2: p2_ty ... pn: pn_ty, ) [-> return_ty] { $body }
|
||||
// looks like it's tricky to build function declaration with aster if body already generated
|
||||
let signature = ::syntax::parse::parser::Parser::parse_impl_item(
|
||||
&mut ::syntax::parse::new_parser_from_tts(
|
||||
ext_cx.parse_sess(),
|
||||
{
|
||||
let _sp = ext_cx.call_site();
|
||||
let mut tt = ::std::vec::Vec::new();
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("fn"))));
|
||||
tt.extend(::quasi::ToTokens::to_tokens(&method_name, ext_cx).into_iter());
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Comma));
|
||||
|
||||
for arg_idx in 0..dispatch.input_arg_names.len() {
|
||||
let arg_name = dispatch.input_arg_names[arg_idx].as_str();
|
||||
let arg_ty = dispatch.input_arg_tys[arg_idx].clone();
|
||||
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg_name))));
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Colon));
|
||||
tt.extend(::quasi::ToTokens::to_tokens(&arg_ty, ext_cx).into_iter());
|
||||
tt.push(Token(_sp, ::syntax::parse::token::Comma));
|
||||
}
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||
|
||||
if let Some(ref return_ty) = dispatch.return_type_ty {
|
||||
tt.push(Token(_sp, ::syntax::parse::token::RArrow));
|
||||
tt.extend(::quasi::ToTokens::to_tokens(return_ty, ext_cx).into_iter());
|
||||
}
|
||||
|
||||
tt.push(Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
|
||||
tt.extend(::quasi::ToTokens::to_tokens(&body, ext_cx).into_iter());
|
||||
tt.push(Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
|
||||
|
||||
tt
|
||||
}));
|
||||
|
||||
signature.unwrap()
|
||||
}
|
||||
|
||||
fn client_generics(builder: &aster::AstBuilder, interface_map: &InterfaceMap) -> Generics {
|
||||
let ty_param = aster::ty_param::TyParamBuilder::new(
|
||||
builder.id("S")).trait_bound(
|
||||
builder.path().global().ids(&["ipc", "IpcSocket"]).build()
|
||||
).build().build();
|
||||
|
||||
builder.from_generics(interface_map.generics.clone())
|
||||
.with_ty_param(ty_param)
|
||||
.build()
|
||||
}
|
||||
|
||||
fn client_qualified_ident(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap) -> P<Ty> {
|
||||
let generics = client_generics(builder, interface_map);
|
||||
aster::ty::TyBuilder::new().path().segment(interface_map.ident_map.client_ident(cx, builder))
|
||||
.with_generics(generics).build()
|
||||
.build()
|
||||
}
|
||||
|
||||
fn client_phantom_ident(builder: &aster::AstBuilder, interface_map: &InterfaceMap) -> P<Ty> {
|
||||
let generics = client_generics(builder, interface_map);
|
||||
aster::ty::TyBuilder::new().phantom_data()
|
||||
.tuple().with_tys(generics.ty_params.iter().map(|x| aster::ty::TyBuilder::new().id(x.ident)))
|
||||
.build()
|
||||
}
|
||||
|
||||
/// generates client type for specified server type
|
||||
/// for say `Service` it generates `ServiceClient`
|
||||
fn push_client_struct(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap, push: &mut FnMut(Annotatable)) {
|
||||
let generics = client_generics(builder, interface_map);
|
||||
let client_short_ident = interface_map.ident_map.client_ident(cx, builder);
|
||||
let phantom = client_phantom_ident(builder, interface_map);
|
||||
|
||||
let client_struct_item = quote_item!(cx,
|
||||
pub struct $client_short_ident $generics {
|
||||
socket: ::std::sync::RwLock<S>,
|
||||
phantom: $phantom,
|
||||
});
|
||||
|
||||
push(Annotatable::Item(client_struct_item.expect(&format!("could not generate client struct for {:?}", client_short_ident.name))));
|
||||
}
|
||||
|
||||
/// pushes generated code for the client class (type declaration and method invocation implementations)
|
||||
fn push_client(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
interface_map: &InterfaceMap,
|
||||
push: &mut FnMut(Annotatable),
|
||||
) {
|
||||
push_client_struct(cx, builder, interface_map, push);
|
||||
push_client_implementation(cx, builder, interface_map, push);
|
||||
push_with_socket_client_implementation(cx, builder, interface_map, push);
|
||||
}
|
||||
|
||||
fn push_with_socket_client_implementation(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
interface_map: &InterfaceMap,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
let generics = client_generics(builder, interface_map);
|
||||
let client_ident = client_qualified_ident(cx, builder, interface_map);
|
||||
let where_clause = &generics.where_clause;
|
||||
let client_short_ident = interface_map.ident_map.client_ident(cx, builder);
|
||||
|
||||
let implement = quote_item!(cx,
|
||||
impl $generics ::ipc::WithSocket<S> for $client_ident $where_clause {
|
||||
fn init(socket: S) -> $client_ident {
|
||||
$client_short_ident {
|
||||
socket: ::std::sync::RwLock::new(socket),
|
||||
phantom: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}).unwrap();
|
||||
push(Annotatable::Item(implement));
|
||||
}
|
||||
|
||||
/// pushes full client side code for the original class exposed via ipc
|
||||
fn push_client_implementation(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
interface_map: &InterfaceMap,
|
||||
push: &mut FnMut(Annotatable),
|
||||
) {
|
||||
let mut index = -1i32;
|
||||
let items = interface_map.dispatches.iter()
|
||||
.map(|_| { index = index + 1; P(implement_client_method(cx, builder, index as u16, interface_map)) })
|
||||
.collect::<Vec<P<ast::ImplItem>>>();
|
||||
|
||||
let generics = client_generics(builder, interface_map);
|
||||
let client_ident = client_qualified_ident(cx, builder, interface_map);
|
||||
let where_clause = &generics.where_clause;
|
||||
let endpoint = interface_map.endpoint;
|
||||
|
||||
let handshake_item = quote_impl_item!(cx,
|
||||
pub fn handshake(&self) -> Result<(), ::ipc::Error> {
|
||||
let payload = ::ipc::Handshake {
|
||||
protocol_version: $endpoint::protocol_version(),
|
||||
api_version: $endpoint::api_version(),
|
||||
};
|
||||
|
||||
::ipc::invoke(
|
||||
0,
|
||||
&Some(::ipc::binary::serialize(&::ipc::BinHandshake::from(payload)).unwrap()),
|
||||
&mut *self.socket.write().unwrap());
|
||||
|
||||
let mut result = vec![0u8; 1];
|
||||
if try!(self.socket.write().unwrap().read(&mut result).map_err(|_| ::ipc::Error::HandshakeFailed)) == 1 {
|
||||
match result[0] {
|
||||
1 => Ok(()),
|
||||
_ => Err(::ipc::Error::RemoteServiceUnsupported),
|
||||
}
|
||||
}
|
||||
else { Err(::ipc::Error::HandshakeFailed) }
|
||||
}).unwrap();
|
||||
|
||||
let socket_item = quote_impl_item!(cx,
|
||||
#[cfg(test)]
|
||||
pub fn socket(&self) -> &::std::sync::RwLock<S> {
|
||||
&self.socket
|
||||
}).unwrap();
|
||||
|
||||
let generic_items = vec![P(handshake_item), P(socket_item)];
|
||||
|
||||
if interface_map.impl_trait.is_some() {
|
||||
let trait_ty = builder.id(
|
||||
::syntax::print::pprust::path_to_string(
|
||||
&interface_map.impl_trait.as_ref().unwrap().path));
|
||||
|
||||
let implement_trait =
|
||||
quote_item!(cx,
|
||||
impl $generics $trait_ty for $client_ident $where_clause {
|
||||
$items
|
||||
}
|
||||
).unwrap();
|
||||
push(Annotatable::Item(implement_trait));
|
||||
|
||||
let implement =
|
||||
quote_item!(cx,
|
||||
impl $generics $client_ident $where_clause {
|
||||
$generic_items
|
||||
}
|
||||
).unwrap();
|
||||
push(Annotatable::Item(implement));
|
||||
}
|
||||
else {
|
||||
let pub_items = items.iter().map(|item| {
|
||||
let pub_item = item.clone();
|
||||
pub_item.map(|mut val| { val.vis = ast::Visibility::Public; val })
|
||||
}).collect::<Vec<P<ast::ImplItem>>>();
|
||||
|
||||
let implement = quote_item!(cx,
|
||||
impl $generics $client_ident $where_clause {
|
||||
$pub_items
|
||||
$generic_items
|
||||
}).unwrap();
|
||||
push(Annotatable::Item(implement));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// implements dispatching of system handshake invocation (method_num 0)
|
||||
fn implement_handshake_arm(
|
||||
cx: &ExtCtxt,
|
||||
) -> (ast::Arm, ast::Arm)
|
||||
{
|
||||
let handshake_deserialize = quote_stmt!(&cx,
|
||||
let handshake_payload = ::ipc::binary::deserialize_from::<::ipc::BinHandshake, _>(r).unwrap();
|
||||
);
|
||||
|
||||
let handshake_deserialize_buf = quote_stmt!(&cx,
|
||||
let handshake_payload = ::ipc::binary::deserialize::<::ipc::BinHandshake>(buf).unwrap();
|
||||
);
|
||||
|
||||
let handshake_serialize = quote_expr!(&cx,
|
||||
::ipc::binary::serialize::<bool>(&Self::handshake(&handshake_payload.to_semver())).unwrap()
|
||||
);
|
||||
|
||||
(
|
||||
quote_arm!(&cx, 0 => {
|
||||
$handshake_deserialize
|
||||
$handshake_serialize
|
||||
}),
|
||||
quote_arm!(&cx, 0 => {
|
||||
$handshake_deserialize_buf
|
||||
$handshake_serialize
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<String, ()> {
|
||||
match lit.node {
|
||||
ast::LitKind::Str(ref s, _) => Ok(format!("{}", s)),
|
||||
_ => {
|
||||
cx.span_err(
|
||||
lit.span,
|
||||
&format!("ipc client_ident annotation `{}` must be a string, not `{}`",
|
||||
name,
|
||||
::syntax::print::pprust::lit_to_string(lit)));
|
||||
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn client_ident_renamed(cx: &ExtCtxt, meta_item: &MetaItem) -> Option<String> {
|
||||
if let ast::MetaItemKind::List(ref list) = meta_item.node {
|
||||
for nested in list {
|
||||
match nested.node {
|
||||
ast::NestedMetaItemKind::MetaItem(ref meta_item) => {
|
||||
let is_client_ident = &*meta_item.name.as_str() == "client_ident";
|
||||
match meta_item.node {
|
||||
ast::MetaItemKind::NameValue(ref lit) if is_client_ident => {
|
||||
if let Ok(s) = get_str_from_lit(cx, "client_ident", lit) {
|
||||
return Some(s);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
cx.span_err(
|
||||
meta_item.span,
|
||||
&format!("unknown client_ident container attribute `{}`",
|
||||
::syntax::print::pprust::meta_item_to_string(&meta_item)));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
struct InterfaceMap {
|
||||
pub original_item: Item,
|
||||
pub item: P<ast::Item>,
|
||||
pub dispatches: Vec<Dispatch>,
|
||||
pub generics: Generics,
|
||||
pub impl_trait: Option<TraitRef>,
|
||||
pub ident_map: IdentMap,
|
||||
pub endpoint: Ident,
|
||||
}
|
||||
|
||||
struct IdentMap {
|
||||
original_path: ast::Path,
|
||||
meta_item: MetaItem,
|
||||
}
|
||||
|
||||
impl IdentMap {
|
||||
fn ident(&self, builder: &aster::AstBuilder) -> Ident {
|
||||
builder.id(format!("{}", ::syntax::print::pprust::path_to_string(&self.original_path)))
|
||||
}
|
||||
|
||||
fn client_ident(&self, cx: &ExtCtxt, builder: &aster::AstBuilder) -> Ident {
|
||||
if let Some(new_name) = client_ident_renamed(cx, &self.meta_item) {
|
||||
builder.id(new_name)
|
||||
}
|
||||
else {
|
||||
builder.id(format!("{}Client", self.original_path.segments[0].identifier))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ty_ident_map(original_ty: &P<Ty>, meta_item: &MetaItem) -> IdentMap {
|
||||
let original_path = match original_ty.node {
|
||||
::syntax::ast::TyKind::Path(_, ref path) => path.clone(),
|
||||
_ => { panic!("incompatible implementation"); }
|
||||
};
|
||||
let ident_map = IdentMap { original_path: original_path, meta_item: meta_item.clone() };
|
||||
ident_map
|
||||
}
|
||||
|
||||
/// implements `IpcInterface` for the given class `C`
|
||||
fn implement_interface(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
item: &Item,
|
||||
push: &mut FnMut(Annotatable),
|
||||
meta_item: &MetaItem,
|
||||
) -> Result<InterfaceMap, Error> {
|
||||
let (generics, impl_trait, original_ty, dispatch_table) = match item.node {
|
||||
ast::ItemKind::Impl(_, _, ref generics, ref impl_trait, ref ty, ref impl_items) => {
|
||||
let mut method_signatures = Vec::new();
|
||||
for impl_item in impl_items {
|
||||
if let ImplItemKind::Method(ref signature, _) = impl_item.node {
|
||||
method_signatures.push(NamedSignature { ident: &impl_item.ident, sig: signature });
|
||||
}
|
||||
}
|
||||
|
||||
let dispatch_table = method_signatures.iter().map(|named_signature|
|
||||
push_invoke_signature_aster(builder, named_signature, push))
|
||||
.collect::<Vec<Dispatch>>();
|
||||
|
||||
(generics, impl_trait.clone(), ty.clone(), dispatch_table)
|
||||
},
|
||||
ast::ItemKind::Trait(_, ref generics, _, ref trait_items) => {
|
||||
let mut method_signatures = Vec::new();
|
||||
for trait_item in trait_items {
|
||||
if let TraitItemKind::Method(ref signature, _) = trait_item.node {
|
||||
method_signatures.push(NamedSignature { ident: &trait_item.ident, sig: signature });
|
||||
}
|
||||
}
|
||||
|
||||
let dispatch_table = method_signatures.iter().map(|named_signature|
|
||||
push_invoke_signature_aster(builder, named_signature, push))
|
||||
.collect::<Vec<Dispatch>>();
|
||||
|
||||
(
|
||||
generics,
|
||||
Some(ast::TraitRef {
|
||||
path: builder.path().ids(&[item.ident.name]).build(),
|
||||
ref_id: item.id,
|
||||
}),
|
||||
builder.ty().id(item.ident),
|
||||
dispatch_table
|
||||
)
|
||||
},
|
||||
_ => {
|
||||
cx.span_err(
|
||||
item.span,
|
||||
"`#[ipc]` may only be applied to implementations and traits");
|
||||
return Err(Error);
|
||||
},
|
||||
};
|
||||
let impl_generics = builder.from_generics(generics.clone()).build();
|
||||
let where_clause = &impl_generics.where_clause;
|
||||
|
||||
let dispatch_arms = implement_dispatch_arms(cx, builder, &dispatch_table, false);
|
||||
let dispatch_arms_buffered = implement_dispatch_arms(cx, builder, &dispatch_table, true);
|
||||
|
||||
let (handshake_arm, handshake_arm_buf) = implement_handshake_arm(cx);
|
||||
|
||||
let ty = ty_ident_map(&original_ty, meta_item).ident(builder);
|
||||
let (interface_endpoint, host_generics) = match impl_trait {
|
||||
Some(ref trait_) => (builder.id(::syntax::print::pprust::path_to_string(&trait_.path)), None),
|
||||
None => (ty, Some(&impl_generics)),
|
||||
};
|
||||
|
||||
let ipc_item = quote_item!(cx,
|
||||
impl $host_generics ::ipc::IpcInterface for $interface_endpoint $where_clause {
|
||||
fn dispatch<R>(&self, r: &mut R) -> Vec<u8>
|
||||
where R: ::std::io::Read
|
||||
{
|
||||
let mut method_num = vec![0u8;2];
|
||||
match r.read(&mut method_num) {
|
||||
Ok(size) if size == 0 => { panic!("method id not supplied" ); }
|
||||
Err(e) => { panic!("ipc read error: {:?}, aborting", e); }
|
||||
_ => { }
|
||||
}
|
||||
|
||||
// method_num is a 16-bit little-endian unsigned number
|
||||
match method_num[1] as u16 + (method_num[0] as u16)*256 {
|
||||
// handshake
|
||||
$handshake_arm
|
||||
// user methods
|
||||
$dispatch_arms
|
||||
_ => vec![]
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_buf(&self, method_num: u16, buf: &[u8]) -> Vec<u8>
|
||||
{
|
||||
match method_num {
|
||||
$handshake_arm_buf
|
||||
$dispatch_arms_buffered
|
||||
_ => vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
).unwrap();
|
||||
|
||||
Ok(InterfaceMap {
|
||||
ident_map: ty_ident_map(&original_ty, meta_item),
|
||||
original_item: item.clone(),
|
||||
item: ipc_item,
|
||||
dispatches: dispatch_table,
|
||||
generics: generics.clone(),
|
||||
impl_trait: impl_trait.clone(),
|
||||
endpoint: interface_endpoint,
|
||||
})
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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/>.
|
||||
|
||||
//! Codegen for IPC RPC
|
||||
|
||||
#![cfg_attr(feature = "nightly-testing", plugin(clippy))]
|
||||
#![cfg_attr(feature = "nightly-testing", feature(plugin))]
|
||||
#![cfg_attr(feature = "nightly-testing", allow(used_underscore_binding))]
|
||||
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]
|
||||
#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))]
|
||||
|
||||
extern crate aster;
|
||||
extern crate quasi;
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
extern crate syntex;
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
extern crate syntex_syntax as syntax;
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
#[macro_use]
|
||||
extern crate syntax;
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
extern crate rustc_plugin;
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
use syntax::feature_gate::AttributeType;
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
use syntax::{ast, fold};
|
||||
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
include!("lib.rs.in");
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
pub fn expand(src: &std::path::Path, dst: &std::path::Path) {
|
||||
let mut registry = syntex::Registry::new();
|
||||
register(&mut registry);
|
||||
registry.expand("", src, dst).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
struct StripAttributeFolder<'a> {
|
||||
attr_title: &'a str,
|
||||
}
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
impl<'a> fold::Folder for StripAttributeFolder<'a> {
|
||||
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
||||
let is_self = &*attr.value.name.as_str() == self.attr_title;
|
||||
|
||||
match attr.value.node {
|
||||
ast::MetaItemKind::List(_) if is_self => { return None; }
|
||||
ast::MetaItemKind::Word if is_self => { return None; }
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Some(attr)
|
||||
}
|
||||
|
||||
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
||||
fold::noop_fold_mac(mac, self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
pub fn register_cleaner_ipc(reg: &mut syntex::Registry) {
|
||||
#[cfg(feature = "with-syntex")]
|
||||
fn strip_attributes(krate: ast::Crate) -> ast::Crate {
|
||||
let mut folder = StripAttributeFolder { attr_title: "ipc" };
|
||||
fold::Folder::fold_crate(&mut folder, krate)
|
||||
}
|
||||
|
||||
reg.add_post_expansion_pass(strip_attributes);
|
||||
}
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
pub fn register_cleaner_binary(reg: &mut syntex::Registry) {
|
||||
#[cfg(feature = "with-syntex")]
|
||||
fn strip_attributes(krate: ast::Crate) -> ast::Crate {
|
||||
let mut folder = StripAttributeFolder { attr_title: "binary" };
|
||||
fold::Folder::fold_crate(&mut folder, krate)
|
||||
}
|
||||
|
||||
reg.add_post_expansion_pass(strip_attributes);
|
||||
}
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
pub fn register(reg: &mut syntex::Registry) {
|
||||
reg.add_attr("feature(custom_derive)");
|
||||
reg.add_attr("feature(custom_attribute)");
|
||||
|
||||
reg.add_decorator("ipc", codegen::expand_ipc_implementation);
|
||||
reg.add_decorator("binary", serialization::expand_serialization_implementation);
|
||||
|
||||
register_cleaner_ipc(reg);
|
||||
register_cleaner_binary(reg);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
pub fn register(reg: &mut rustc_plugin::Registry) {
|
||||
reg.register_syntax_extension(
|
||||
syntax::parse::token::intern("ipc"),
|
||||
syntax::ext::base::MultiDecorator(
|
||||
Box::new(codegen::expand_ipc_implementation)));
|
||||
reg.register_syntax_extension(
|
||||
syntax::parse::token::intern("binary"),
|
||||
syntax::ext::base::MultiDecorator(
|
||||
Box::new(serialization::expand_serialization_implementation)));
|
||||
|
||||
reg.register_attribute("ipc".to_owned(), AttributeType::Normal);
|
||||
reg.register_attribute("binary".to_owned(), AttributeType::Normal);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error { InvalidFileName, ExpandFailure, Io(std::io::Error) }
|
||||
|
||||
impl std::convert::From<std::io::Error> for Error {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn derive_ipc_cond(src_path: &str, has_feature: bool) -> Result<(), Error> {
|
||||
if has_feature { derive_ipc(src_path) }
|
||||
else { cleanup_ipc(src_path) }
|
||||
}
|
||||
|
||||
pub fn cleanup_ipc(src_path: &str) -> Result<(), Error> {
|
||||
cleanup(src_path, AttributeKind::Ipc)
|
||||
}
|
||||
|
||||
pub fn cleanup_binary(src_path: &str) -> Result<(), Error> {
|
||||
cleanup(src_path, AttributeKind::Binary)
|
||||
}
|
||||
|
||||
enum AttributeKind {
|
||||
Ipc,
|
||||
Binary,
|
||||
}
|
||||
|
||||
fn cleanup(src_path: &str, attr: AttributeKind) -> Result<(), Error> {
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let file_name = PathBuf::from(src_path).file_name().ok_or(Error::InvalidFileName).map(|val| val.to_str().unwrap().to_owned())?;
|
||||
let mut registry = syntex::Registry::new();
|
||||
|
||||
match attr {
|
||||
AttributeKind::Ipc => { register_cleaner_ipc(&mut registry); }
|
||||
AttributeKind::Binary => { register_cleaner_binary(&mut registry); }
|
||||
}
|
||||
|
||||
if let Err(_) = registry.expand("", &Path::new(src_path), &Path::new(&out_dir).join(&file_name))
|
||||
{
|
||||
// will be reported by compiler
|
||||
return Err(Error::ExpandFailure)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn derive_ipc(src_path: &str) -> Result<(), Error> {
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let file_name = PathBuf::from(src_path).file_name().ok_or(Error::InvalidFileName).map(|val| val.to_str().unwrap().to_owned())?;
|
||||
|
||||
let final_path = Path::new(&out_dir).join(&file_name);
|
||||
|
||||
let mut intermediate_file_name = file_name.clone();
|
||||
intermediate_file_name.push_str(".rpc.in");
|
||||
let intermediate_path = Path::new(&out_dir).join(&intermediate_file_name);
|
||||
|
||||
{
|
||||
let mut registry = syntex::Registry::new();
|
||||
register(&mut registry);
|
||||
if let Err(_) = registry.expand("", &Path::new(src_path), &intermediate_path) {
|
||||
// will be reported by compiler
|
||||
return Err(Error::ExpandFailure)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut registry = syntex::Registry::new();
|
||||
register(&mut registry);
|
||||
if let Err(_) = registry.expand("", &intermediate_path, &final_path) {
|
||||
// will be reported by compiler
|
||||
return Err(Error::ExpandFailure)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn derive_binary(src_path: &str) -> Result<(), Error> {
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let file_name = PathBuf::from(src_path).file_name().ok_or(Error::InvalidFileName).map(|val| val.to_str().unwrap().to_owned())?;
|
||||
let final_path = Path::new(&out_dir).join(&file_name);
|
||||
|
||||
let mut registry = syntex::Registry::new();
|
||||
register(&mut registry);
|
||||
if let Err(_) = registry.expand("", &Path::new(src_path), &final_path) {
|
||||
// will be reported by compiler
|
||||
return Err(Error::ExpandFailure)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn derive_binary_cond(src_path: &str, has_feature: bool) -> Result<(), Error> {
|
||||
if has_feature { derive_binary(src_path) }
|
||||
else { cleanup_binary(src_path) }
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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/>.
|
||||
|
||||
mod codegen;
|
||||
mod serialization;
|
||||
@@ -1,810 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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) as Result<BinaryExpressions, 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));
|
||||
let field_id = match field.ident {
|
||||
Some(ident) => builder.id(ident),
|
||||
None => builder.id(format!("{}", index)),
|
||||
};
|
||||
|
||||
match raw_ident.as_ref() {
|
||||
"u8" => {
|
||||
quote_expr!(cx, 1)
|
||||
},
|
||||
"[u8]" => {
|
||||
value_ident.and_then(|x| {
|
||||
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|
|
||||
{
|
||||
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;).expect("stmt1"));
|
||||
|
||||
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];).expect("stmt2"));
|
||||
map_stmts.push(quote_stmt!(cx, let mut total = 0usize;).expect("stmt3"));
|
||||
|
||||
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 field_id = match field.ident {
|
||||
Some(ident) => builder.id(ident),
|
||||
None => builder.id(format!("{}", index)),
|
||||
};
|
||||
let member_expr = match value_ident {
|
||||
Some(x) => {
|
||||
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;).expect("stmt4"));
|
||||
write_stmts.push(quote_stmt!(cx, buffer[offset] = $member_expr; ).expect("stm5"));
|
||||
},
|
||||
"[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) {
|
||||
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) as Result<BinaryExpressions, 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::tokenstream::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(),
|
||||
{
|
||||
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::tokenstream::TokenTree::Token;
|
||||
|
||||
::quasi::parse_stmt_panic(&mut ::syntax::parse::new_parser_from_tts(
|
||||
ext_cx.parse_sess(),
|
||||
{
|
||||
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, match field.ident {
|
||||
Some(ident) => token::Ident(ident),
|
||||
None => token::Ident(ext_cx.ident_of(&format!("{}", idx))),
|
||||
}));
|
||||
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
|
||||
}
|
||||
),
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user