openethereum/ipc/codegen/src/codegen.rs

689 lines
25 KiB
Rust
Raw Normal View History

2016-03-24 22:07:01 +01:00
// 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,
ImplItemKind,
ImplItem,
MethodSig,
Arg,
PatKind,
FunctionRetTy,
2016-03-29 00:55:04 +02:00
Ty,
2016-03-24 22:07:01 +01:00
};
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;
2016-04-13 02:46:36 +02:00
use super::typegen;
2016-03-24 22:07:01 +01:00
pub struct Error;
2016-04-07 22:18:48 +02:00
const RESERVED_MESSAGE_IDS: u16 = 16;
2016-03-24 22:07:01 +01:00
pub fn expand_ipc_implementation(
2016-03-24 22:42:09 +01:00
cx: &mut ExtCtxt,
span: Span,
meta_item: &MetaItem,
annotatable: &Annotatable,
push: &mut FnMut(Annotatable)
2016-03-24 22:07:01 +01:00
) {
2016-03-24 22:39:20 +01:00
let item = match *annotatable {
Annotatable::Item(ref item) => item,
_ => {
cx.span_err(meta_item.span, "`#[derive(Ipc)]` may only be applied to struct implementations");
return;
}
};
2016-03-24 22:07:01 +01:00
2016-03-24 22:39:20 +01:00
let builder = aster::AstBuilder::new().span(span);
2016-03-24 22:07:01 +01:00
2016-03-29 21:00:23 +02:00
let (impl_item, dispatches) = match implement_interface(cx, &builder, &item, push) {
Ok((item, dispatches)) => (item, dispatches),
2016-03-24 22:39:20 +01:00
Err(Error) => { return; }
};
2016-03-24 22:07:01 +01:00
2016-03-30 17:27:31 +02:00
push_client(cx, &builder, &item, &dispatches, push);
2016-04-08 13:07:25 +02:00
push_handshake_struct(cx, push);
2016-04-13 02:46:36 +02:00
push(Annotatable::Item(impl_item));
let all_tys = dispatches.iter().flat_map(|ref dispatch| &dispatch.input_arg_tys).cloned().collect::<Vec<P<Ty>>>();
typegen::match_unknown_tys(cx, &builder, &all_tys, push);
2016-03-24 22:07:01 +01:00
}
2016-04-08 13:07:25 +02:00
fn push_handshake_struct(cx: &ExtCtxt, push: &mut FnMut(Annotatable)) {
let handshake_item = quote_item!(cx,
#[derive(Serialize, Deserialize)]
pub struct BinHandshake {
api_version: String,
protocol_version: String,
_reserved: Vec<u8>,
}
).unwrap();
push(Annotatable::Item(handshake_item));
}
2016-03-24 22:07:01 +01:00
fn field_name(builder: &aster::AstBuilder, arg: &Arg) -> ast::Ident {
match arg.pat.node {
2016-03-24 22:41:15 +01:00
PatKind::Ident(_, ref ident, _) => builder.id(ident.node),
2016-03-24 22:07:01 +01:00
_ => { panic!("unexpected param in interface: {:?}", arg.pat.node) }
}
}
2016-03-24 22:44:02 +01:00
fn push_invoke_signature_aster(
2016-03-24 22:42:09 +01:00
builder: &aster::AstBuilder,
2016-03-24 22:07:01 +01:00
implement: &ImplItem,
signature: &MethodSig,
2016-03-24 22:42:09 +01:00
push: &mut FnMut(Annotatable),
2016-03-24 22:07:01 +01:00
) -> Dispatch {
let inputs = &signature.decl.inputs;
2016-03-29 00:55:04 +02:00
let (input_type_name, input_arg_names, input_arg_tys) = if inputs.len() > 0 {
2016-03-24 22:07:01 +01:00
let first_field_name = field_name(builder, &inputs[0]).name.as_str();
2016-03-29 00:55:04 +02:00
if first_field_name == "self" && inputs.len() == 1 { (None, vec![], vec![]) }
2016-03-24 22:07:01 +01:00
else {
let skip = if first_field_name == "self" { 2 } else { 1 };
let name_str = format!("{}_input", implement.ident.name.as_str());
let mut arg_names = Vec::new();
2016-03-29 00:55:04 +02:00
let mut arg_tys = Vec::new();
2016-03-24 22:07:01 +01:00
let arg_name = format!("{}", field_name(builder, &inputs[skip-1]).name);
2016-03-29 00:55:04 +02:00
let arg_ty = inputs[skip-1].ty.clone();
2016-03-24 22:07:01 +01:00
let mut tree = builder.item()
.attr().word("derive(Serialize, Deserialize)")
.attr().word("allow(non_camel_case_types)")
.struct_(name_str.as_str())
2016-03-29 00:55:04 +02:00
.field(arg_name.as_str()).ty().build(arg_ty.clone());
2016-03-24 22:07:01 +01:00
arg_names.push(arg_name);
2016-03-29 00:55:04 +02:00
arg_tys.push(arg_ty.clone());
2016-03-24 22:07:01 +01:00
for arg in inputs.iter().skip(skip) {
let arg_name = format!("{}", field_name(builder, &arg));
2016-03-29 00:55:04 +02:00
let arg_ty = arg.ty.clone();
tree = tree.field(arg_name.as_str()).ty().build(arg_ty.clone());
2016-03-24 22:07:01 +01:00
arg_names.push(arg_name);
2016-03-29 00:55:04 +02:00
arg_tys.push(arg_ty);
2016-03-24 22:07:01 +01:00
}
push(Annotatable::Item(tree.build()));
2016-03-29 00:55:04 +02:00
(Some(name_str.to_owned()), arg_names, arg_tys)
2016-03-24 22:07:01 +01:00
}
}
else {
2016-03-29 00:55:04 +02:00
(None, vec![], vec![])
2016-03-24 22:07:01 +01:00
};
2016-03-29 21:15:45 +02:00
let (return_type_name, return_type_ty) = match signature.decl.output {
2016-03-24 22:07:01 +01:00
FunctionRetTy::Ty(ref ty) => {
let name_str = format!("{}_output", implement.ident.name.as_str());
let tree = builder.item()
.attr().word("derive(Serialize, Deserialize)")
.attr().word("allow(non_camel_case_types)")
.struct_(name_str.as_str())
.field(format!("payload")).ty().build(ty.clone());
push(Annotatable::Item(tree.build()));
2016-03-29 21:15:45 +02:00
(Some(name_str.to_owned()), Some(ty.clone()))
2016-03-24 22:07:01 +01:00
}
2016-03-29 21:15:45 +02:00
_ => (None, None)
2016-03-24 22:07:01 +01:00
};
Dispatch {
function_name: format!("{}", implement.ident.name.as_str()),
input_type_name: input_type_name,
input_arg_names: input_arg_names,
2016-03-29 00:55:04 +02:00
input_arg_tys: input_arg_tys,
2016-03-24 22:07:01 +01:00
return_type_name: return_type_name,
2016-03-29 21:15:45 +02:00
return_type_ty: return_type_ty,
2016-03-24 22:07:01 +01:00
}
}
struct Dispatch {
function_name: String,
input_type_name: Option<String>,
input_arg_names: Vec<String>,
2016-03-29 00:55:04 +02:00
input_arg_tys: Vec<P<Ty>>,
2016-03-24 22:07:01 +01:00
return_type_name: Option<String>,
2016-03-29 21:15:45 +02:00
return_type_ty: Option<P<Ty>>,
2016-03-24 22:07:01 +01:00
}
2016-04-05 11:08:42 +02:00
// 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(
2016-03-24 22:07:01 +01:00
cx: &ExtCtxt,
2016-03-24 22:42:09 +01:00
builder: &aster::AstBuilder,
2016-03-24 22:07:01 +01:00
dispatch: &Dispatch,
2016-04-05 11:08:42 +02:00
) -> ast::Stmt
2016-03-24 22:07:01 +01:00
{
let function_name = builder.id(dispatch.function_name.as_str());
let output_type_id = builder.id(dispatch.return_type_name.clone().unwrap().as_str());
let input_args_exprs = dispatch.input_arg_names.iter().map(|ref arg_name| {
let arg_ident = builder.id(arg_name);
quote_expr!(cx, input. $arg_ident)
}).collect::<Vec<P<ast::Expr>>>();
2016-04-05 11:08:42 +02:00
let ext_cx = &*cx;
::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(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("bincode"), ::syntax::parse::token::ModName)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serde"), ::syntax::parse::token::ModName)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
tt.extend(::quasi::ToTokens::to_tokens(&output_type_id, ext_cx).into_iter());
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("payload"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Colon));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
tt.extend(::quasi::ToTokens::to_tokens(&function_name, ext_cx).into_iter());
tt.push(::syntax::ast::TokenTree::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(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
}
2016-03-24 22:07:01 +01:00
2016-04-05 11:08:42 +02:00
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("bincode"), ::syntax::parse::token::ModName)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("SizeLimit"), ::syntax::parse::token::ModName)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Infinite"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
tt
})).unwrap()
}
2016-03-24 22:07:01 +01:00
2016-04-05 11:08:42 +02:00
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, ::bincode::serde::deserialize(buf).expect("ipc deserialization error, aborting"))
} else {
quote_expr!(cx, ::bincode::serde::deserialize_from(r, ::bincode::SizeLimit::Infinite).expect("ipc deserialization error, aborting"))
2016-03-24 22:07:01 +01:00
};
2016-04-05 11:08:42 +02:00
let input_type_id = builder.id(dispatch.input_type_name.clone().unwrap().as_str());
let invoke_serialize_stmt = implement_dispatch_arm_invoke_stmt(cx, builder, dispatch);
2016-03-24 22:07:01 +01:00
quote_expr!(cx, {
let input: $input_type_id = $deserialize_expr;
$invoke_serialize_stmt
})
}
2016-03-30 18:27:39 +02:00
/// generates dispatch match for method id
2016-04-05 11:08:42 +02:00
fn implement_dispatch_arm(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
index: u32,
dispatch: &Dispatch,
buffer: bool,
) -> ast::Arm
2016-03-24 22:07:01 +01:00
{
let index_ident = builder.id(format!("{}", index + (RESERVED_MESSAGE_IDS as u32)).as_str());
2016-04-05 11:08:42 +02:00
let invoke_expr = implement_dispatch_arm_invoke(cx, builder, dispatch, buffer);
2016-03-24 22:07:01 +01:00
quote_arm!(cx, $index_ident => { $invoke_expr } )
}
2016-04-05 11:08:42 +02:00
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()
}
2016-03-30 18:27:39 +02:00
/// generates client type for specified server type
/// for say `Service` it generates `ServiceClient`
2016-03-30 17:27:31 +02:00
fn push_client_struct(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push: &mut FnMut(Annotatable)) {
2016-04-12 17:03:58 +02:00
let (_, client_ident) = get_item_idents(builder, item);
let client_struct_item = quote_item!(cx,
pub struct $client_ident <S: ::ipc::IpcSocket> {
socket: ::std::cell::RefCell<S>,
phantom: ::std::marker::PhantomData<S>,
});
2016-04-12 17:03:58 +02:00
push(Annotatable::Item(client_struct_item.expect(&format!("could not generate client struct for {:?}", client_ident.name))));
}
2016-03-30 18:27:39 +02:00
/// pushes generated code for the client class (type declaration and method invocation implementations)
2016-03-30 17:27:31 +02:00
fn push_client(
2016-03-29 00:55:04 +02:00
cx: &ExtCtxt,
2016-03-29 21:00:23 +02:00
builder: &aster::AstBuilder,
2016-03-29 00:55:04 +02:00
item: &Item,
2016-03-29 21:00:23 +02:00
dispatches: &[Dispatch],
2016-03-29 00:55:04 +02:00
push: &mut FnMut(Annotatable))
{
2016-03-30 17:27:31 +02:00
push_client_struct(cx, builder, item, push);
push_client_implementation(cx, builder, dispatches, item, push);
2016-04-12 09:18:39 +02:00
push_with_socket_client_implementation(cx, builder, item, push);
2016-03-29 21:00:23 +02:00
}
2016-03-29 00:55:04 +02:00
2016-03-30 18:27:39 +02:00
/// 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)
2016-03-30 18:20:39 +02:00
///
/// assuming expanded class contains method
/// fn commit(&self, f: u32) -> u32
///
/// the expanded implementation will generate method for the client like that
2016-03-30 18:27:39 +02:00
/// #[derive(Serialize)]
/// struct Request<'a> {
/// f: &'a u32,
2016-03-30 18:20:39 +02:00
/// }
2016-03-30 18:27:39 +02:00
/// 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()
2016-03-30 17:27:31 +02:00
fn implement_client_method_body(
2016-03-29 21:00:23 +02:00
cx: &ExtCtxt,
builder: &aster::AstBuilder,
2016-03-30 01:21:47 +02:00
index: u16,
dispatch: &Dispatch,
)
2016-03-29 21:00:23 +02:00
-> P<ast::Expr>
{
let request = if dispatch.input_arg_names.len() > 0 {
2016-04-13 02:46:36 +02:00
let substitutes = typegen::match_unknown_tys(cx, builder, dispatch.input_arg_tys
2016-03-29 21:00:23 +02:00
let arg_name = dispatch.input_arg_names[0].as_str();
let arg_ty = builder
.ty().ref_()
.lifetime("'a")
.ty().build(dispatch.input_arg_tys[0].clone());
let mut tree = builder.item()
.attr().word("derive(Serialize)")
.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 arg_ty = builder
.ty().ref_()
.lifetime("'a")
.ty().build(dispatch.input_arg_tys[arg_idx].clone());
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(),
ext_cx.cfg(),
{
let _sp = ext_cx.call_site();
let mut tt = ::std::vec::Vec::new();
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("let"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("payload"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Eq));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Request"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
for arg in dispatch.input_arg_names.iter() {
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg.as_str()), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Colon));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg.as_str()), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
}
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
tt
}))
});
request_serialization_statements.push(
quote_stmt!(cx, let mut socket_ref = self.socket.borrow_mut()));
request_serialization_statements.push(
quote_stmt!(cx, let mut socket = socket_ref.deref_mut()));
request_serialization_statements.push(
quote_stmt!(cx, let serialized_payload = ::bincode::serde::serialize(&payload, ::bincode::SizeLimit::Infinite).unwrap()));
let index_ident = builder.id(format!("{}", index + RESERVED_MESSAGE_IDS).as_str());
2016-03-30 01:21:47 +02:00
2016-03-29 21:00:23 +02:00
request_serialization_statements.push(
2016-03-30 01:21:47 +02:00
quote_stmt!(cx, ::ipc::invoke($index_ident, &Some(serialized_payload), &mut socket)));
2016-03-29 21:00:23 +02:00
request_serialization_statements
}
else {
vec![]
};
2016-03-29 00:55:04 +02:00
2016-03-29 21:15:45 +02:00
if let Some(ref return_ty) = dispatch.return_type_ty {
let return_expr = quote_expr!(cx,
::bincode::serde::deserialize_from::<_, $return_ty>(&mut socket, ::bincode::SizeLimit::Infinite).unwrap()
);
quote_expr!(cx, {
$request
$return_expr
})
}
else {
quote_expr!(cx, {
$request
})
}
2016-03-29 21:00:23 +02:00
}
2016-03-30 18:31:09 +02:00
/// Generates signature and body (see `implement_client_method_body`)
/// for the client (signature is identical to the original method)
2016-03-30 17:27:31 +02:00
fn implement_client_method(
2016-03-29 21:00:23 +02:00
cx: &ExtCtxt,
builder: &aster::AstBuilder,
2016-03-30 01:21:47 +02:00
index: u16,
2016-03-29 21:00:23 +02:00
dispatch: &Dispatch)
-> ast::ImplItem
{
let method_name = builder.id(dispatch.function_name.as_str());
2016-03-30 17:27:31 +02:00
let body = implement_client_method_body(cx, builder, index, dispatch);
2016-03-29 21:00:23 +02:00
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(),
ext_cx.cfg(),
{
let _sp = ext_cx.call_site();
let mut tt = ::std::vec::Vec::new();
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("pub"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("fn"), ::syntax::parse::token::Plain)));
tt.extend(::quasi::ToTokens::to_tokens(&method_name, ext_cx).into_iter());
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
2016-03-29 21:15:45 +02:00
2016-03-29 21:00:23 +02:00
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(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of(arg_name), ::syntax::parse::token::Plain)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Colon));
tt.extend(::quasi::ToTokens::to_tokens(&arg_ty, ext_cx).into_iter());
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
}
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
2016-03-29 21:15:45 +02:00
if let Some(ref return_ty) = dispatch.return_type_ty {
2016-03-29 21:00:23 +02:00
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::RArrow));
2016-03-29 21:15:45 +02:00
tt.extend(::quasi::ToTokens::to_tokens(return_ty, ext_cx).into_iter());
2016-03-29 21:00:23 +02:00
}
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
tt.extend(::quasi::ToTokens::to_tokens(&body, ext_cx).into_iter());
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
tt
}));
signature.unwrap()
2016-03-29 00:55:04 +02:00
}
2016-04-12 09:18:39 +02:00
fn push_with_socket_client_implementation(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
item: &Item,
push: &mut FnMut(Annotatable))
{
2016-04-12 17:03:58 +02:00
let (_, client_ident) = get_item_idents(builder, item);
2016-04-12 09:18:39 +02:00
let implement = quote_item!(cx,
impl<S> ::ipc::WithSocket<S> for $client_ident<S> where S: ::ipc::IpcSocket {
fn init(socket: S) -> $client_ident<S> {
$client_ident {
socket: ::std::cell::RefCell::new(socket),
phantom: ::std::marker::PhantomData,
}
}
}).unwrap();
push(Annotatable::Item(implement));
}
2016-03-30 18:31:09 +02:00
/// pushes full client side code for the original class exposed via ipc
2016-03-30 17:27:31 +02:00
fn push_client_implementation(
2016-03-29 00:55:04 +02:00
cx: &ExtCtxt,
2016-03-29 21:00:23 +02:00
builder: &aster::AstBuilder,
2016-03-29 00:55:04 +02:00
dispatches: &[Dispatch],
2016-03-30 17:17:49 +02:00
item: &Item,
2016-03-29 00:55:04 +02:00
push: &mut FnMut(Annotatable))
{
2016-04-12 17:03:58 +02:00
let (item_ident, client_ident) = get_item_idents(builder, item);
2016-03-30 01:21:47 +02:00
let mut index = -1i32;
2016-03-29 21:00:23 +02:00
let items = dispatches.iter()
2016-03-30 17:27:31 +02:00
.map(|dispatch| { index = index + 1; P(implement_client_method(cx, builder, index as u16, dispatch)) })
2016-03-29 21:00:23 +02:00
.collect::<Vec<P<ast::ImplItem>>>();
let implement = quote_item!(cx,
2016-03-30 17:17:49 +02:00
impl<S> $client_ident<S> where S: ::ipc::IpcSocket {
2016-04-12 06:13:31 +02:00
pub fn handshake(&self) -> Result<(), ::ipc::Error> {
2016-04-12 06:07:12 +02:00
let payload = BinHandshake {
protocol_version: $item_ident::protocol_version().to_string(),
api_version: $item_ident::api_version().to_string(),
2016-04-12 10:34:56 +02:00
_reserved: vec![0u8; 64],
2016-04-12 06:07:12 +02:00
};
let mut socket_ref = self.socket.borrow_mut();
let mut socket = socket_ref.deref_mut();
::ipc::invoke(
0,
&Some(::bincode::serde::serialize(&payload, ::bincode::SizeLimit::Infinite).unwrap()),
&mut socket);
let mut result = vec![0u8; 1];
2016-04-12 06:13:31 +02:00
if try!(socket.read(&mut result).map_err(|_| ::ipc::Error::HandshakeFailed)) == 1 {
match result[0] {
1 => Ok(()),
_ => Err(::ipc::Error::RemoteServiceUnsupported),
}
2016-04-12 06:07:12 +02:00
}
2016-04-12 06:13:31 +02:00
else { Err(::ipc::Error::HandshakeFailed) }
2016-04-12 06:07:12 +02:00
}
2016-03-29 21:29:43 +02:00
#[cfg(test)]
pub fn socket(&self) -> &::std::cell::RefCell<S> {
&self.socket
}
2016-03-29 21:00:23 +02:00
$items
}).unwrap();
push(Annotatable::Item(implement));
2016-03-29 00:55:04 +02:00
}
2016-04-12 06:07:12 +02:00
/// 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 = ::bincode::serde::deserialize_from::<_, BinHandshake>(r, ::bincode::SizeLimit::Infinite).unwrap();
);
let handshake_deserialize_buf = quote_stmt!(&cx,
let handshake_payload = ::bincode::serde::deserialize::<BinHandshake>(buf).unwrap();
);
let handshake_serialize = quote_expr!(&cx,
::bincode::serde::serialize::<bool>(&Self::handshake(&::ipc::Handshake {
api_version: ::semver::Version::parse(&handshake_payload.api_version).unwrap(),
protocol_version: ::semver::Version::parse(&handshake_payload.protocol_version).unwrap(),
}), ::bincode::SizeLimit::Infinite).unwrap()
);
(
quote_arm!(&cx, 0 => {
$handshake_deserialize
$handshake_serialize
}),
quote_arm!(&cx, 0 => {
$handshake_deserialize_buf
$handshake_serialize
}),
)
}
2016-04-12 17:03:58 +02:00
fn get_item_idents(builder: &aster::AstBuilder, item: &Item) -> (::syntax::ast::Ident, ::syntax::ast::Ident) {
let ty = match item.node {
ast::ItemKind::Impl(_, _, _, _, ref ty, _) => ty.clone(),
_ => { builder.ty().id("") }
};
let (item_ident, client_ident) = match ty.node {
::syntax::ast::TyKind::Path(_, ref path) => {
(
builder.id(format!("{}", path.segments[0].identifier)),
builder.id(format!("{}Client", path.segments[0].identifier))
)
},
_ => { panic!("incompatible implementation"); }
};
(item_ident, client_ident)
}
2016-03-30 18:31:09 +02:00
/// implements `IpcInterface<C>` for the given class `C`
2016-03-24 22:07:01 +01:00
fn implement_interface(
2016-03-24 22:42:09 +01:00
cx: &ExtCtxt,
builder: &aster::AstBuilder,
item: &Item,
push: &mut FnMut(Annotatable),
2016-03-29 21:00:23 +02:00
) -> Result<(P<ast::Item>, Vec<Dispatch>), Error> {
2016-03-24 22:07:01 +01:00
let (generics, impl_items) = match item.node {
ast::ItemKind::Impl(_, _, ref generics, _, _, ref impl_items) => (generics, impl_items),
_ => {
cx.span_err(
item.span,
"`#[derive(Ipc)]` may only be applied to item implementations");
return Err(Error);
}
};
let impl_generics = builder.from_generics(generics.clone())
.add_ty_param_bound(
builder.path().global().ids(&["ethcore_ipc"]).build()
)
.build();
let where_clause = &impl_generics.where_clause;
2016-04-12 17:03:58 +02:00
let (ty, _) = get_item_idents(builder, item);
2016-03-24 22:07:01 +01:00
let mut dispatch_table = Vec::new();
for impl_item in impl_items {
if let ImplItemKind::Method(ref signature, _) = impl_item.node {
dispatch_table.push(push_invoke_signature_aster(builder, &impl_item, signature, push));
}
}
2016-04-05 11:08:42 +02:00
let dispatch_arms = implement_dispatch_arms(cx, builder, &dispatch_table, false);
let dispatch_arms_buffered = implement_dispatch_arms(cx, builder, &dispatch_table, true);
2016-03-24 22:07:01 +01:00
2016-04-12 06:07:12 +02:00
let (handshake_arm, handshake_arm_buf) = implement_handshake_arm(cx);
2016-04-08 13:07:25 +02:00
2016-03-29 21:00:23 +02:00
Ok((quote_item!(cx,
2016-03-24 22:07:01 +01:00
impl $impl_generics ::ipc::IpcInterface<$ty> for $ty $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 {
2016-04-08 13:07:25 +02:00
// handshake
$handshake_arm
// user methods
2016-03-24 22:07:01 +01:00
$dispatch_arms
_ => vec![]
}
}
2016-04-03 22:39:49 +02:00
2016-04-05 11:08:42 +02:00
fn dispatch_buf(&self, method_num: u16, buf: &[u8]) -> Vec<u8>
2016-04-03 22:39:49 +02:00
{
match method_num {
2016-04-12 06:07:12 +02:00
$handshake_arm_buf
2016-04-05 11:08:42 +02:00
$dispatch_arms_buffered
2016-04-03 22:39:49 +02:00
_ => vec![]
}
}
2016-03-24 22:07:01 +01:00
}
2016-03-29 21:00:23 +02:00
).unwrap(), dispatch_table))
2016-03-24 22:07:01 +01:00
}