,
+ input_arg_tys: Vec>,
+ return_type_ty: Option
>,
+}
+
+// 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::>>();
+
+ 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
+{
+ 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
+{
+ 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) -> P {
+ if let ast::TyKind::Rptr(_, ref ptr_mut) = ty.node {
+ ptr_mut.ty.clone()
+ }
+ else { ty.clone() }
+}
+
+pub fn has_ptr(ty: &P) -> 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
+{
+ 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 {
+ 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 {
+ 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,
+ 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 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::>>();
+
+ 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 {
+ &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::>>();
+
+ 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::(&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 {
+ 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 {
+ 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,
+ pub dispatches: Vec,
+ pub generics: Generics,
+ pub impl_trait: Option,
+ 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, 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 {
+ 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::>();
+
+ (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::>();
+
+ (
+ 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(&self, r: &mut R) -> Vec
+ 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
+ {
+ 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,
+ })
+}
diff --git a/ipc/codegen/src/lib.rs b/ipc/codegen/src/lib.rs
new file mode 100644
index 000000000..6e7fc441b
--- /dev/null
+++ b/ipc/codegen/src/lib.rs
@@ -0,0 +1,237 @@
+// 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 .
+
+//! 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 {
+ 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 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) }
+}
diff --git a/ipc/codegen/src/lib.rs.in b/ipc/codegen/src/lib.rs.in
new file mode 100644
index 000000000..c8aa8ebf2
--- /dev/null
+++ b/ipc/codegen/src/lib.rs.in
@@ -0,0 +1,18 @@
+// 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 .
+
+mod codegen;
+mod serialization;
diff --git a/ipc/codegen/src/serialization.rs b/ipc/codegen/src/serialization.rs
new file mode 100644
index 000000000..fd908725c
--- /dev/null
+++ b/ipc/codegen/src/serialization.rs
@@ -0,0 +1,810 @@
+// 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 .
+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, 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) -> Result<(), ::ipc::BinaryConvertError> {
+ $write_expr
+ }
+
+ fn from_bytes(buffer: &[u8], length_stack: &mut ::std::collections::VecDeque) -> Result {
+ $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,
+) -> Result {
+ 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
+ },
+ }
+}
+
+struct BinaryExpressions {
+ pub size: P,
+ pub write: P,
+ pub read: P,
+}
+
+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,
+ fields: &[ast::StructField],
+ value_ident: Option,
+ instance_ident: Option,
+) -> Result {
+
+ let size_exprs: Vec> = 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::::new();
+ write_stmts.push(quote_stmt!(cx, let mut offset = 0usize;).expect("stmt1"));
+
+ let mut map_stmts = Vec::::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::::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,
+ span: Span,
+ variant_data: &ast::VariantData,
+) -> Result {
+ 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
+ },
+ }
+}
+
+fn binary_expr_enum(
+ cx: &ExtCtxt,
+ builder: &aster::AstBuilder,
+ type_ident: Ident,
+ impl_generics: &ast::Generics,
+ ty: P,
+ span: Span,
+ enum_def: &ast::EnumDef,
+) -> Result {
+ 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::>(),
+ arms.iter().map(|x| x.write.clone()).collect::>(),
+ arms.iter().map(|x| x.read.clone()).collect::>());
+
+ 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,
+ 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,
+ 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,
+ _span: Span,
+ variant: &ast::Variant,
+ variant_index: usize,
+) -> Result {
+ 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 = (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
+ }
+ ),
+ })
+ },
+ }
+}
diff --git a/ipc/hypervisor/Cargo.toml b/ipc/hypervisor/Cargo.toml
new file mode 100644
index 000000000..1db60c904
--- /dev/null
+++ b/ipc/hypervisor/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "ethcore-ipc-hypervisor"
+version = "1.2.0"
+authors = ["Parity Technologies "]
+license = "GPL-3.0"
+build = "build.rs"
+
+[features]
+
+[dependencies]
+ethcore-ipc = { path = "../rpc" }
+nanomsg = { git = "https://github.com/paritytech/nanomsg.rs.git", branch = "parity-1.7" }
+ethcore-ipc-nano = { path = "../nano" }
+semver = "0.6"
+log = "0.3"
+time = "0.1"
+
+[build-dependencies]
+ethcore-ipc-codegen = { path = "../codegen" }
diff --git a/ipc/hypervisor/build.rs b/ipc/hypervisor/build.rs
new file mode 100644
index 000000000..097aba046
--- /dev/null
+++ b/ipc/hypervisor/build.rs
@@ -0,0 +1,21 @@
+// 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 .
+
+extern crate ethcore_ipc_codegen;
+
+fn main() {
+ ethcore_ipc_codegen::derive_ipc("src/service.rs.in").unwrap();
+}
diff --git a/ipc/hypervisor/src/lib.rs b/ipc/hypervisor/src/lib.rs
new file mode 100644
index 000000000..b522122b5
--- /dev/null
+++ b/ipc/hypervisor/src/lib.rs
@@ -0,0 +1,273 @@
+// 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 .
+
+//! Parity interprocess hypervisor module
+
+#![cfg_attr(feature="dev", allow(used_underscore_binding))]
+
+extern crate ethcore_ipc as ipc;
+extern crate ethcore_ipc_nano as nanoipc;
+extern crate semver;
+#[macro_use] extern crate log;
+extern crate time;
+
+pub mod service;
+
+/// Default value for hypervisor ipc listener
+pub const HYPERVISOR_IPC_URL: &'static str = "parity-internal-hyper-status.ipc";
+
+use std::sync::{Arc,RwLock};
+use service::{HypervisorService, IpcModuleId};
+use std::process::{Command,Child};
+use std::collections::HashMap;
+
+pub use service::{HypervisorServiceClient, ControlService, CLIENT_MODULE_ID, SYNC_MODULE_ID};
+
+pub type BinaryId = &'static str;
+
+pub struct Hypervisor {
+ ipc_addr: String,
+ service: Arc,
+ ipc_worker: RwLock>,
+ processes: RwLock>,
+ modules: HashMap,
+ pub io_path: String,
+}
+
+/// Boot arguments for binary
+pub struct BootArgs {
+ cli: Option>,
+ stdin: Option>,
+}
+
+impl BootArgs {
+ /// New empty boot arguments
+ pub fn new() -> BootArgs {
+ BootArgs {
+ cli: None,
+ stdin: None,
+ }
+ }
+
+ /// Set command-line arguments for boot
+ pub fn cli(mut self, cli: Vec) -> BootArgs {
+ self.cli = Some(cli);
+ self
+ }
+
+ /// Set std-in stream for boot
+ pub fn stdin(mut self, stdin: Vec) -> BootArgs {
+ self.stdin = Some(stdin);
+ self
+ }
+}
+
+impl Hypervisor {
+ /// initializes the Hypervisor service with the open ipc socket for incoming clients
+ pub fn new() -> Hypervisor {
+ Hypervisor::with_url(HYPERVISOR_IPC_URL)
+ }
+
+ pub fn module(mut self, module_id: IpcModuleId, args: BootArgs) -> Hypervisor {
+ self.modules.insert(module_id, args);
+ self.service.add_module(module_id);
+ self
+ }
+
+ pub fn local_module(self, module_id: IpcModuleId) -> Hypervisor {
+ self.service.add_module(module_id);
+ self
+ }
+
+ pub fn io_path(mut self, directory: &str) -> Hypervisor {
+ self.io_path = directory.to_owned();
+ self
+ }
+
+ /// Starts with the specified address for the ipc listener and
+ /// the specified list of modules in form of created service
+ pub fn with_url(addr: &str) -> Hypervisor {
+ let service = HypervisorService::new();
+ let worker = nanoipc::Worker::new(&service);
+ Hypervisor{
+ ipc_addr: addr.to_owned(),
+ service: service,
+ ipc_worker: RwLock::new(worker),
+ processes: RwLock::new(HashMap::new()),
+ modules: HashMap::new(),
+ io_path: "/tmp".to_owned(),
+ }
+ }
+
+ /// Since one binary can host multiple modules
+ /// we match binaries
+ fn match_module(&self, module_id: &IpcModuleId) -> Option<&BootArgs> {
+ self.modules.get(module_id)
+ }
+
+ /// Creates IPC listener and starts all binaries
+ pub fn start(&self) {
+ let mut worker = self.ipc_worker.write().unwrap();
+ worker.add_reqrep(&self.ipc_addr).unwrap_or_else(|e| panic!("Hypervisor ipc worker can not start - critical! ({:?})", e));
+
+ for module_id in self.service.module_ids() {
+ self.start_module(module_id);
+ }
+ }
+
+ /// Start binary for the specified module
+ /// Does nothing when it is already started on module is inside the
+ /// main binary
+ fn start_module(&self, module_id: IpcModuleId) {
+ use std::io::Write;
+
+ self.match_module(&module_id).map(|boot_args| {
+ let mut processes = self.processes.write().unwrap();
+ {
+ if processes.get(&module_id).is_some() {
+ // already started for another module
+ return;
+ }
+ }
+
+ let mut command = Command::new(&std::env::current_exe().unwrap());
+ command.stderr(std::process::Stdio::inherit());
+
+ if let Some(ref cli_args) = boot_args.cli {
+ for arg in cli_args { command.arg(arg); }
+ }
+
+ command.stdin(std::process::Stdio::piped());
+
+ trace!(target: "hypervisor", "Spawn executable: {:?}", command);
+
+ let mut child = command.spawn().unwrap_or_else(
+ |e| panic!("Hypervisor cannot execute command ({:?}): {}", command, e));
+
+ if let Some(ref std_in) = boot_args.stdin {
+ trace!(target: "hypervisor", "Pushing std-in payload...");
+ child.stdin.as_mut()
+ .expect("std-in should be piped above")
+ .write(std_in)
+ .unwrap_or_else(|e| panic!(format!("Error trying to pipe stdin for {:?}: {:?}", &command, e)));
+ drop(child.stdin.take());
+ }
+
+ processes.insert(module_id, child);
+ });
+ }
+
+ /// Reports if all modules are checked in
+ pub fn modules_ready(&self) -> bool {
+ self.service.unchecked_count() == 0
+ }
+
+ pub fn modules_shutdown(&self) -> bool {
+ self.service.running_count() == 0
+ }
+
+ /// Waits for every required module to check in
+ pub fn wait_for_startup(&self) {
+ let mut worker = self.ipc_worker.write().unwrap();
+ while !self.modules_ready() {
+ worker.poll()
+ }
+ }
+
+ /// Waits for every required module to check in
+ pub fn wait_for_shutdown(&self) -> bool {
+ use time::{PreciseTime, Duration};
+
+ let mut worker = self.ipc_worker.write().unwrap();
+ let start = PreciseTime::now();
+ while !self.modules_shutdown() {
+ worker.poll();
+ if start.to(PreciseTime::now()) > Duration::seconds(30) {
+ warn!("Some modules failed to shutdown gracefully, they will be terminated.");
+ break;
+ }
+ }
+ self.modules_shutdown()
+ }
+
+ /// Shutdown the ipc and all managed child processes
+ pub fn shutdown(&self) {
+ let mut childs = self.processes.write().unwrap();
+ for (ref module, _) in childs.iter() {
+ trace!(target: "hypervisor", "Stopping process module: {}", module);
+ self.service.send_shutdown(**module);
+ }
+ trace!(target: "hypervisor", "Waiting for shutdown...");
+ if self.wait_for_shutdown() {
+ trace!(target: "hypervisor", "All modules reported shutdown");
+ return;
+ }
+
+ for (ref module, ref mut process) in childs.iter_mut() {
+ if self.service.is_running(**module) {
+ process.kill().unwrap();
+ trace!("Terminated {}", module);
+ }
+ }
+ }
+}
+
+impl Drop for Hypervisor {
+ fn drop(&mut self) {
+ self.shutdown();
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::sync::atomic::{AtomicBool,Ordering};
+ use std::sync::Arc;
+ use nanoipc;
+
+ #[test]
+ fn can_init() {
+ let url = "ipc:///tmp/test-parity-hypervisor-10.ipc";
+ let test_module_id = 8080u64;
+
+ let hypervisor = Hypervisor::with_url(url).local_module(test_module_id);
+ assert_eq!(false, hypervisor.modules_ready());
+ }
+
+ #[test]
+ fn can_wait_for_startup() {
+ let url = "ipc:///tmp/test-parity-hypervisor-20.ipc";
+ let test_module_id = 8080u64;
+
+ let hypervisor_ready = Arc::new(AtomicBool::new(false));
+ let hypervisor_ready_local = hypervisor_ready.clone();
+
+ ::std::thread::spawn(move || {
+ while !hypervisor_ready.load(Ordering::Relaxed) { }
+
+ let client = nanoipc::fast_client::>(url).unwrap();
+ client.handshake().unwrap();
+ client.module_ready(test_module_id, url.to_owned());
+ });
+
+ let hypervisor = Hypervisor::with_url(url).local_module(test_module_id);
+ hypervisor.start();
+ hypervisor_ready_local.store(true, Ordering::Relaxed);
+ hypervisor.wait_for_startup();
+
+ assert_eq!(true, hypervisor.modules_ready());
+ }
+}
diff --git a/ipc/hypervisor/src/service.rs b/ipc/hypervisor/src/service.rs
new file mode 100644
index 000000000..59040251e
--- /dev/null
+++ b/ipc/hypervisor/src/service.rs
@@ -0,0 +1,20 @@
+// 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 .
+
+//! Parity interprocess hypervisor IPC service
+#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues
+
+include!(concat!(env!("OUT_DIR"), "/service.rs.in"));
diff --git a/ipc/hypervisor/src/service.rs.in b/ipc/hypervisor/src/service.rs.in
new file mode 100644
index 000000000..6d6f38268
--- /dev/null
+++ b/ipc/hypervisor/src/service.rs.in
@@ -0,0 +1,125 @@
+// 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 .
+
+use std::sync::{RwLock,Arc};
+use ipc::IpcConfig;
+use std::collections::HashMap;
+use nanoipc;
+
+pub type IpcModuleId = u64;
+
+/// Blockhain database module id
+pub const CLIENT_MODULE_ID: IpcModuleId = 2000;
+
+/// Sync module id
+pub const SYNC_MODULE_ID: IpcModuleId = 2100;
+
+/// IPC service that handles module management
+pub struct HypervisorService {
+ modules: RwLock>,
+}
+
+#[derive(Default)]
+pub struct ModuleState {
+ started: bool,
+ control_url: String,
+ shutdown: bool,
+}
+
+#[ipc]
+pub trait ControlService {
+ fn shutdown(&self) -> bool;
+}
+
+#[ipc]
+impl HypervisorService {
+ // return type for making method synchronous
+ fn module_ready(&self, module_id: u64, control_url: String) -> bool {
+ let mut modules = self.modules.write().unwrap();
+ modules.get_mut(&module_id).map(|mut module| {
+ module.started = true;
+ module.control_url = control_url;
+ });
+ trace!(target: "hypervisor", "Module ready: {}", module_id);
+ true
+ }
+
+ // return type for making method synchronous
+ fn module_shutdown(&self, module_id: u64) -> bool {
+ let mut modules = self.modules.write().unwrap();
+ modules.get_mut(&module_id).map(|mut module| {
+ module.shutdown = true;
+ });
+ trace!(target: "hypervisor", "Module shutdown: {}", module_id);
+ true
+ }
+}
+
+impl HypervisorService {
+ /// New service with the default list of modules
+ pub fn new() -> Arc {
+ HypervisorService::with_modules(vec![])
+ }
+
+ /// New service with list of modules that will report for being ready
+ pub fn with_modules(module_ids: Vec) -> Arc {
+ let mut modules = HashMap::new();
+ for module_id in module_ids {
+ modules.insert(module_id, ModuleState::default());
+ }
+ Arc::new(HypervisorService {
+ modules: RwLock::new(modules),
+ })
+ }
+
+ /// Add the module to the check-list
+ pub fn add_module(&self, module_id: IpcModuleId) {
+ self.modules.write().unwrap().insert(module_id, ModuleState::default());
+ }
+
+ /// Number of modules still being waited for check-in
+ pub fn unchecked_count(&self) -> usize {
+ self.modules.read().unwrap().iter().filter(|&(_, module)| !module.started).count()
+ }
+
+ /// List of all modules within this service
+ pub fn module_ids(&self) -> Vec {
+ self.modules.read().unwrap().iter().map(|(module_id, _)| module_id).cloned().collect()
+ }
+
+ /// Number of modules started and running
+ pub fn running_count(&self) -> usize {
+ self.modules.read().unwrap().iter().filter(|&(_, module)| module.started && !module.shutdown).count()
+ }
+
+ pub fn is_running(&self, id: IpcModuleId) -> bool {
+ self.modules.read().unwrap().get(&id).map(|module| module.started && !module.shutdown).unwrap_or(false)
+ }
+
+ pub fn send_shutdown(&self, module_id: IpcModuleId) {
+ let modules = self.modules.read().unwrap();
+ modules.get(&module_id).map(|module| {
+ trace!(target: "hypervisor", "Sending shutdown to {}({})", module_id, &module.control_url);
+ let client = nanoipc::fast_client::>(&module.control_url).unwrap();
+ client.shutdown();
+ trace!(target: "hypervisor", "Sent shutdown to {}", module_id);
+ });
+ }
+}
+
+impl ::ipc::IpcConfig for HypervisorService {}
+
+impl ::ipc::IpcConfig for ControlService {}
diff --git a/ipc/nano/Cargo.toml b/ipc/nano/Cargo.toml
new file mode 100644
index 000000000..a714e1d00
--- /dev/null
+++ b/ipc/nano/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "ethcore-ipc-nano"
+version = "1.8.0"
+authors = ["Parity Technologies "]
+license = "GPL-3.0"
+
+[features]
+
+[dependencies]
+ethcore-ipc = { path = "../rpc" }
+nanomsg = { git = "https://github.com/paritytech/nanomsg.rs.git", branch = "parity-1.7" }
+log = "0.3"
+lazy_static = "0.2"
diff --git a/ipc/nano/src/lib.rs b/ipc/nano/src/lib.rs
new file mode 100644
index 000000000..9be3d2b1d
--- /dev/null
+++ b/ipc/nano/src/lib.rs
@@ -0,0 +1,355 @@
+// 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 .
+
+//! IPC over nanomsg transport
+
+extern crate ethcore_ipc as ipc;
+extern crate nanomsg;
+#[macro_use] extern crate log;
+#[macro_use] extern crate lazy_static;
+
+pub use ipc::{WithSocket, IpcInterface, IpcConfig};
+pub use nanomsg::Socket as NanoSocket;
+
+use std::sync::*;
+use nanomsg::{Socket, Protocol, Error, Endpoint, PollRequest, PollFd, PollInOut};
+use std::ops::Deref;
+
+const POLL_TIMEOUT: isize = 200;
+const DEFAULT_CONNECTION_TIMEOUT: isize = 30000;
+const DEBUG_CONNECTION_TIMEOUT: isize = 5000;
+
+/// Generic worker to handle service (binded) sockets
+pub struct Worker where S: IpcInterface {
+ service: Arc,
+ sockets: Vec<(Socket, Endpoint)>,
+ polls: Vec,
+ buf: Vec,
+}
+
+/// struct for guarding `_endpoint` (so that it wont drop)
+/// derefs to client `S`
+pub struct GuardedSocket where S: WithSocket {
+ client: Arc,
+ _endpoint: Endpoint,
+}
+
+impl GuardedSocket where S: WithSocket {
+ pub fn service(&self) -> Arc {
+ self.client.clone()
+ }
+}
+
+impl Deref for GuardedSocket where S: WithSocket {
+ type Target = Arc;
+
+ fn deref(&self) -> &Arc {
+ &self.client
+ }
+}
+
+/// Spawns client <`S`> over specified address
+/// creates socket and connects endpoint to it
+/// for duplex (paired) connections with the service
+pub fn init_duplex_client(socket_addr: &str) -> Result, SocketError> where S: WithSocket {
+ let mut socket = Socket::new(Protocol::Pair).map_err(|e| {
+ warn!(target: "ipc", "Failed to create ipc socket: {:?}", e);
+ SocketError::DuplexLink
+ })?;
+
+ socket.set_receive_timeout(DEFAULT_CONNECTION_TIMEOUT).unwrap();
+
+ let endpoint = socket.connect(socket_addr).map_err(|e| {
+ warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", socket_addr, e);
+ SocketError::DuplexLink
+ })?;
+
+ Ok(GuardedSocket {
+ client: Arc::new(S::init(socket)),
+ _endpoint: endpoint,
+ })
+}
+
+/// Spawns client <`S`> over specified address
+/// creates socket and connects endpoint to it
+/// for request-reply connections to the service
+pub fn client(socket_addr: &str, receive_timeout: Option) -> Result, SocketError> where S: WithSocket {
+ let mut socket = Socket::new(Protocol::Req).map_err(|e| {
+ warn!(target: "ipc", "Failed to create ipc socket: {:?}", e);
+ SocketError::RequestLink
+ })?;
+
+ if let Some(timeout) = receive_timeout {
+ socket.set_receive_timeout(timeout).unwrap();
+ }
+
+ let endpoint = socket.connect(socket_addr).map_err(|e| {
+ warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", socket_addr, e);
+ SocketError::RequestLink
+ })?;
+
+ trace!(target: "ipc", "Created client for {}", socket_addr);
+ Ok(GuardedSocket {
+ client: Arc::new(S::init(socket)),
+ _endpoint: endpoint,
+ })
+}
+
+lazy_static! {
+ /// Set PARITY_IPC_DEBUG=1 for fail-fast connectivity problems diagnostic
+ pub static ref DEBUG_FLAG: bool = {
+ use std::env;
+
+ if let Ok(debug) = env::var("PARITY_IPC_DEBUG") {
+ debug == "1" || debug.to_uppercase() == "TRUE"
+ }
+ else { false }
+ };
+}
+
+/// Client with no default timeout on operations
+pub fn generic_client(socket_addr: &str) -> Result, SocketError> where S: WithSocket {
+ if *DEBUG_FLAG {
+ client(socket_addr, Some(DEBUG_CONNECTION_TIMEOUT))
+ } else {
+ client(socket_addr, None)
+ }
+}
+
+/// Client over interface that is supposed to give quick almost non-blocking responses
+pub fn fast_client(socket_addr: &str) -> Result, SocketError> where S: WithSocket {
+ if *DEBUG_FLAG {
+ client(socket_addr, Some(DEBUG_CONNECTION_TIMEOUT))
+ } else {
+ client(socket_addr, Some(DEFAULT_CONNECTION_TIMEOUT))
+ }
+}
+
+/// Error occurred while establising socket or endpoint
+#[derive(Debug)]
+pub enum SocketError {
+ /// Error establising duplex (paired) socket and/or endpoint
+ DuplexLink,
+ /// Error establising duplex (paired) socket and/or endpoint
+ RequestLink,
+}
+
+impl Worker where S: IpcInterface {
+ /// New worker over specified `service`
+ pub fn new(service: &Arc) -> Worker {
+ Worker:: {
+ service: service.clone(),
+ sockets: Vec::new(),
+ polls: Vec::new(),
+ buf: Vec::new(),
+ }
+ }
+
+ /// Polls all sockets, reads and dispatches method invocations
+ pub fn poll(&mut self) {
+ use std::io::Write;
+
+ let mut request = PollRequest::new(&mut self.polls[..]);
+ let _result_guard = Socket::poll(&mut request, POLL_TIMEOUT);
+
+ for (fd_index, fd) in request.get_fds().iter().enumerate() {
+ if fd.can_read() {
+ let (ref mut socket, _) = self.sockets[fd_index];
+ unsafe { self.buf.set_len(0); }
+ match socket.nb_read_to_end(&mut self.buf) {
+ Ok(method_sign_len) => {
+ if method_sign_len >= 2 {
+
+ // method_num
+ let method_num = self.buf[0] as u16 * 256 + self.buf[1] as u16;
+ // payload
+ let payload = &self.buf[2..];
+
+ // dispatching for ipc interface
+ let result = self.service.dispatch_buf(method_num, payload);
+
+ if let Err(e) = socket.write(&result) {
+ warn!(target: "ipc", "Failed to write response: {:?}", e);
+ }
+ }
+ else {
+ warn!(target: "ipc", "Failed to read method signature from socket: unexpected message length({})", method_sign_len);
+ }
+ },
+ Err(Error::TryAgain) => {
+ },
+ Err(x) => {
+ warn!(target: "ipc", "Error polling connections {:?}", x);
+ panic!();
+ }
+ }
+ }
+ }
+ }
+
+ /// Stores nanomsg poll request for reuse
+ fn rebuild_poll_request(&mut self) {
+ self.polls = self.sockets.iter()
+ .map(|&(ref socket, _)| socket.new_pollfd(PollInOut::In))
+ .collect::>();
+ }
+
+ /// Add exclusive socket for paired client
+ /// Only one connection over this address is allowed
+ pub fn add_duplex(&mut self, addr: &str) -> Result<(), SocketError> {
+ let mut socket = Socket::new(Protocol::Pair).map_err(|e| {
+ warn!(target: "ipc", "Failed to create ipc socket: {:?}", e);
+ SocketError::DuplexLink
+ })?;
+
+ let endpoint = socket.bind(addr).map_err(|e| {
+ warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", addr, e);
+ SocketError::DuplexLink
+ })?;
+
+ self.sockets.push((socket, endpoint));
+
+ self.rebuild_poll_request();
+
+ trace!(target: "ipc", "Started duplex worker at {}", addr);
+
+ Ok(())
+ }
+
+ /// Add generic socket for request-reply style communications
+ /// with multiple clients
+ pub fn add_reqrep(&mut self, addr: &str) -> Result<(), SocketError> {
+ let mut socket = Socket::new(Protocol::Rep).map_err(|e| {
+ warn!(target: "ipc", "Failed to create ipc socket: {:?}", e);
+ SocketError::DuplexLink
+ })?;
+
+
+ let endpoint = socket.bind(addr).map_err(|e| {
+ warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", addr, e);
+ SocketError::DuplexLink
+ })?;
+
+ self.sockets.push((socket, endpoint));
+
+ self.rebuild_poll_request();
+
+ trace!(target: "ipc", "Started request-reply worker at {}", addr);
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod service_tests {
+
+ use super::Worker;
+ use ipc::*;
+ use std::io::{Read, Write};
+ use std::sync::{Arc, RwLock};
+ use nanomsg::{Socket, Protocol, Endpoint};
+
+ struct TestInvoke {
+ method_num: u16,
+ params: Vec,
+ }
+
+ struct DummyService {
+ methods_stack: RwLock>,
+ }
+
+ impl DummyService {
+ fn new() -> DummyService {
+ DummyService { methods_stack: RwLock::new(Vec::new()) }
+ }
+ }
+
+ impl IpcInterface for DummyService {
+ fn dispatch(&self, _r: &mut R) -> Vec where R: Read {
+ vec![]
+ }
+ fn dispatch_buf(&self, method_num: u16, buf: &[u8]) -> Vec {
+ self.methods_stack.write().unwrap().push(
+ TestInvoke {
+ method_num: method_num,
+ params: buf.to_vec(),
+ });
+ vec![]
+ }
+ }
+
+ impl IpcConfig for DummyService {}
+
+ fn dummy_write(addr: &str, buf: &[u8]) -> (Socket, Endpoint) {
+ let mut socket = Socket::new(Protocol::Pair).unwrap();
+ let endpoint = socket.connect(addr).unwrap();
+ socket.write(buf).unwrap();
+ (socket, endpoint)
+ }
+
+ #[test]
+ fn can_create_worker() {
+ let worker = Worker::::new(&Arc::new(DummyService::new()));
+ assert_eq!(0, worker.sockets.len());
+ }
+
+ #[test]
+ fn can_add_duplex_socket_to_worker() {
+ let mut worker = Worker::::new(&Arc::new(DummyService::new()));
+ worker.add_duplex("ipc:///tmp/parity-test10.ipc").unwrap();
+ assert_eq!(1, worker.sockets.len());
+ }
+
+ #[test]
+ fn worker_can_poll_empty() {
+ let service = Arc::new(DummyService::new());
+ let mut worker = Worker::::new(&service);
+ worker.add_duplex("ipc:///tmp/parity-test20.ipc").unwrap();
+ worker.poll();
+ assert_eq!(0, service.methods_stack.read().unwrap().len());
+ }
+
+ #[test]
+ fn worker_can_poll() {
+ let url = "ipc:///tmp/parity-test30.ipc";
+
+ let mut worker = Worker::::new(&Arc::new(DummyService::new()));
+ worker.add_duplex(url).unwrap();
+
+ let (_socket, _endpoint) = dummy_write(url, &vec![0, 0, 7, 7, 6, 6]);
+ worker.poll();
+
+ assert_eq!(1, worker.service.methods_stack.read().unwrap().len());
+ assert_eq!(0, worker.service.methods_stack.read().unwrap()[0].method_num);
+ assert_eq!([7, 7, 6, 6], worker.service.methods_stack.read().unwrap()[0].params[..]);
+ }
+
+ #[test]
+ fn worker_can_poll_long() {
+ let url = "ipc:///tmp/parity-test40.ipc";
+
+ let mut worker = Worker::::new(&Arc::new(DummyService::new()));
+ worker.add_duplex(url).unwrap();
+
+ let message = [0u8; 1024*1024];
+
+ let (_socket, _endpoint) = dummy_write(url, &message);
+ worker.poll();
+
+ assert_eq!(1, worker.service.methods_stack.read().unwrap().len());
+ assert_eq!(0, worker.service.methods_stack.read().unwrap()[0].method_num);
+ assert_eq!(vec![0u8; 1024*1024-2], worker.service.methods_stack.read().unwrap()[0].params);
+ }
+}
diff --git a/ipc/rpc/Cargo.toml b/ipc/rpc/Cargo.toml
new file mode 100644
index 000000000..12d73a118
--- /dev/null
+++ b/ipc/rpc/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "ethcore-ipc"
+version = "1.8.0"
+authors = ["Parity Technologies "]
+license = "GPL-3.0"
+
+[features]
+
+[dependencies]
+ethcore-devtools = { path = "../../devtools" }
+nanomsg = { git = "https://github.com/paritytech/nanomsg.rs.git", branch = "parity-1.7" }
+ethcore-bigint = { path = "../../util/bigint"}
+ethcore-util = { path = "../../util" }
+semver = "0.6"
diff --git a/ipc/rpc/src/binary.rs b/ipc/rpc/src/binary.rs
new file mode 100644
index 000000000..6466acdb1
--- /dev/null
+++ b/ipc/rpc/src/binary.rs
@@ -0,0 +1,1196 @@
+// 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 .
+
+//! Binary representation of types
+
+use bigint::prelude::{U256, U512};
+use bigint::hash::{H256, H512, H2048};
+use util::{Address};
+use std::mem;
+use std::collections::{VecDeque, BTreeMap};
+use std::ops::Range;
+use super::Handshake;
+
+#[derive(Debug)]
+pub enum BinaryConvertErrorKind {
+ SizeMismatch {
+ expected: usize,
+ found: usize,
+ },
+ TargetPayloadEmpty,
+ UnexpectedVariant(u8),
+ MissingLengthValue,
+ InconsistentBoundaries,
+ NotSupported,
+}
+
+#[derive(Debug)]
+pub struct BinaryConvertError {
+ member_tree: Vec<&'static str>,
+ kind: BinaryConvertErrorKind,
+}
+
+impl BinaryConvertError {
+ pub fn size(expected: usize, found: usize) -> BinaryConvertError {
+ BinaryConvertError {
+ member_tree: Vec::new(),
+ kind: BinaryConvertErrorKind::SizeMismatch {
+ expected: expected,
+ found: found,
+ }
+ }
+ }
+
+ pub fn empty() -> BinaryConvertError {
+ BinaryConvertError { member_tree: Vec::new(), kind: BinaryConvertErrorKind::TargetPayloadEmpty }
+ }
+
+ pub fn variant(val: u8) -> BinaryConvertError {
+ BinaryConvertError { member_tree: Vec::new(), kind: BinaryConvertErrorKind::UnexpectedVariant(val) }
+ }
+
+ pub fn length() -> BinaryConvertError {
+ BinaryConvertError { member_tree: Vec::new(), kind: BinaryConvertErrorKind::MissingLengthValue }
+ }
+
+ pub fn boundaries() -> BinaryConvertError {
+ BinaryConvertError { member_tree: Vec::new(), kind: BinaryConvertErrorKind::InconsistentBoundaries }
+ }
+
+ pub fn not_supported() -> BinaryConvertError {
+ BinaryConvertError { member_tree: Vec::new(), kind: BinaryConvertErrorKind::NotSupported }
+ }
+
+ pub fn named(mut self, name: &'static str) -> BinaryConvertError {
+ self.member_tree.push(name);
+ self
+ }
+}
+
+#[derive(Debug)]
+pub enum BinaryError {
+ Serialization(BinaryConvertError),
+ Io(::std::io::Error),
+}
+
+impl From<::std::io::Error> for BinaryError {
+ fn from(err: ::std::io::Error) -> Self { BinaryError::Io(err) }
+}
+
+impl From for BinaryError {
+ fn from(err: BinaryConvertError) -> Self { BinaryError::Serialization(err) }
+}
+
+pub trait BinaryConvertable : Sized {
+ fn size(&self) -> usize {
+ mem::size_of::()
+ }
+
+ fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError>;
+
+ fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result;
+
+ fn from_empty_bytes() -> Result {
+ Err(BinaryConvertError::size(mem::size_of::(), 0))
+ }
+
+ fn len_params() -> usize {
+ 0
+ }
+}
+
+impl BinaryConvertable for Option where T: BinaryConvertable {
+ fn size(&self) -> usize {
+ match * self { None => 0, Some(ref val) => val.size() }
+ }
+
+ fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> {
+ match *self { None => Err(BinaryConvertError::empty()), Some(ref val) => val.to_bytes(buffer, length_stack) }
+ }
+
+ fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result {
+ if buffer.len() == 0 { return Self::from_empty_bytes(); }
+ Ok(Some(T::from_bytes(buffer, length_stack)?))
+ }
+
+ fn from_empty_bytes() -> Result {
+ Ok(None)
+ }
+
+ fn len_params() -> usize {
+ 1
+ }
+}
+
+impl BinaryConvertable for Result<(), E> {
+ fn size(&self) -> usize {
+ match *self {
+ Ok(_) => 0,
+ Err(ref e) => e.size(),
+ }
+ }
+
+ fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> {
+ match *self {
+ Ok(_) => Err(BinaryConvertError::empty()),
+ Err(ref e) => Ok(e.to_bytes(buffer, length_stack)?),
+ }
+ }
+
+ fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result {
+ Ok(Err(E::from_bytes(&buffer, length_stack)?))
+ }
+
+ fn from_empty_bytes() -> Result {
+ Ok(Ok(()))
+ }
+
+ fn len_params() -> usize {
+ 1
+ }
+}
+
+
+impl BinaryConvertable for Result {
+ fn size(&self) -> usize {
+ match *self {
+ Ok(ref r) => r.size(),
+ Err(_) => 0,
+ }
+ }
+
+ fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> {
+ match *self {
+ Ok(ref r) => Ok(r.to_bytes(buffer, length_stack)?),
+ Err(_) => Err(BinaryConvertError::empty()),
+ }
+ }
+
+ fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result {
+ Ok(Ok(R::from_bytes(&buffer, length_stack)?))
+ }
+
+ fn from_empty_bytes() -> Result {
+ Ok(Err(()))
+ }
+
+ fn len_params() -> usize {
+ 1
+ }
+}
+
+impl BinaryConvertable for Result {
+ fn size(&self) -> usize {
+ 1usize + match *self {
+ Ok(ref r) => r.size(),
+ Err(ref e) => e.size(),
+ }
+ }
+
+ fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> {
+ match *self {
+ Ok(ref r) => {
+ buffer[0] = 0;
+ if r.size() > 0 {
+ Ok(r.to_bytes(&mut buffer[1..], length_stack)?)
+ }
+ else { Ok(()) }
+ },
+ Err(ref e) => {
+ buffer[0] = 1;
+ if e.size() > 0 {
+ Ok(e.to_bytes(&mut buffer[1..], length_stack)?)
+ }
+ else { Ok(()) }
+ },
+ }
+ }
+
+ fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result {
+ match buffer[0] {
+ 0 => {
+ match buffer.len() {
+ 1 => Ok(Ok(R::from_empty_bytes()?)),
+ _ => Ok(Ok(R::from_bytes(&buffer[1..], length_stack)?)),
+ }
+ }
+ 1 => Ok(Err(E::from_bytes(&buffer[1..], length_stack)?)),
+ _ => Err(BinaryConvertError::variant(buffer[0]))
+ }
+ }
+
+ fn len_params() -> usize {
+ 1
+ }
+}
+
+impl BinaryConvertable for BTreeMap where K : BinaryConvertable + Ord, V: BinaryConvertable {
+ fn size(&self) -> usize {
+ 0usize + match K::len_params() {
+ 0 => mem::size_of::() * self.len(),
+ _ => self.iter().fold(0usize, |acc, (k, _)| acc + k.size())
+ } + match V::len_params() {
+ 0 => mem::size_of::() * self.len(),
+ _ => self.iter().fold(0usize, |acc, (_, v)| acc + v.size())
+ }
+ }
+
+ fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> {
+ let mut offset = 0usize;
+ for (key, val) in self.iter() {
+ let key_size = match K::len_params() {
+ 0 => mem::size_of::(),
+ _ => { let size = key.size(); length_stack.push_back(size); size }
+ };
+ let val_size = match K::len_params() {
+ 0 => mem::size_of::(),
+ _ => { let size = val.size(); length_stack.push_back(size); size }
+ };
+
+ if key_size > 0 {
+ let item_end = offset + key_size;
+ key.to_bytes(&mut buffer[offset..item_end], length_stack)?;
+ offset = item_end;
+ }
+
+ if val_size > 0 {
+ let item_end = offset + key_size;
+ val.to_bytes(&mut buffer[offset..item_end], length_stack)?;
+ offset = item_end;
+ }
+ }
+ Ok(())
+ }
+
+ fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result {
+ let mut index = 0;
+ let mut result = Self::new();
+
+ if buffer.len() == 0 { return Ok(result); }
+
+ loop {
+ let key_size = match K::len_params() {
+ 0 => mem::size_of::(),
+ _ => length_stack.pop_front().ok_or(BinaryConvertError::length())?,
+ };
+ let key = if key_size == 0 {
+ K::from_empty_bytes()?
+ } else {
+ if index + key_size > buffer.len() {
+ return Err(BinaryConvertError::boundaries())
+ }
+ K::from_bytes(&buffer[index..index+key_size], length_stack)?
+ };
+ index = index + key_size;
+
+ let val_size = match V::len_params() {
+ 0 => mem::size_of::(),
+ _ => length_stack.pop_front().ok_or(BinaryConvertError::length())?,
+ };
+ let val = if val_size == 0 {
+ V::from_empty_bytes()?
+ } else {
+ if index + val_size > buffer.len() {
+ return Err(BinaryConvertError::boundaries())
+ }
+ V::from_bytes(&buffer[index..index+val_size], length_stack)?
+ };
+ result.insert(key, val);
+ index = index + val_size;
+
+ if index == buffer.len() { break; }
+ if index > buffer.len() {
+ return Err(BinaryConvertError::boundaries())
+ }
+ }
+
+ Ok(result)
+ }
+
+ fn from_empty_bytes() -> Result {
+ Ok(Self::new())
+ }
+
+ fn len_params() -> usize {
+ 1
+ }
+}
+
+impl BinaryConvertable for VecDeque where T: BinaryConvertable {
+ fn size(&self) -> usize {
+ match T::len_params() {
+ 0 => mem::size_of::() * self.len(),
+ _ => self.iter().fold(0usize, |acc, t| acc + t.size()),
+ }
+ }
+
+ fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> {
+ let mut offset = 0usize;
+ for item in self.iter() {
+ let next_size = match T::len_params() {
+ 0 => mem::size_of::(),
+ _ => { let size = item.size(); length_stack.push_back(size); size },
+ };
+ if next_size > 0 {
+ let item_end = offset + next_size;
+ item.to_bytes(&mut buffer[offset..item_end], length_stack)?;
+ offset = item_end;
+ }
+ }
+ Ok(())
+ }
+
+ fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result {
+ let mut index = 0;
+ let mut result = Self::with_capacity(
+ match T::len_params() {
+ 0 => buffer.len() / mem::size_of::(),
+ _ => 128,
+ });
+
+ if buffer.len() == 0 { return Ok(result); }
+
+ loop {
+ let next_size = match T::len_params() {
+ 0 => mem::size_of::(),
+ _ => length_stack.pop_front().ok_or(BinaryConvertError::length())?,
+ };
+ let item = if next_size == 0 {
+ T::from_empty_bytes()?
+ }
+ else {
+ if index + next_size > buffer.len() {
+ return Err(BinaryConvertError::boundaries())
+ }
+ T::from_bytes(&buffer[index..index+next_size], length_stack)?
+ };
+ result.push_back(item);
+
+ index = index + next_size;
+ if index == buffer.len() { break; }
+ if index > buffer.len() {
+ return Err(BinaryConvertError::boundaries())
+ }
+ }
+
+ Ok(result)
+ }
+
+ fn from_empty_bytes() -> Result {
+ Ok(Self::new())
+ }
+
+ fn len_params() -> usize {
+ 1
+ }
+}
+
+impl BinaryConvertable for Vec where T: BinaryConvertable {
+ fn size(&self) -> usize {
+ match T::len_params() {
+ 0 => mem::size_of::() * self.len(),
+ _ => self.iter().fold(0usize, |acc, t| acc + t.size()),
+ }
+ }
+
+ fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> {
+ let mut offset = 0usize;
+ for item in self.iter() {
+ let next_size = match T::len_params() {
+ 0 => mem::size_of::(),
+ _ => { let size = item.size(); length_stack.push_back(size); size },
+ };
+ if next_size > 0 {
+ let item_end = offset + next_size;
+ item.to_bytes(&mut buffer[offset..item_end], length_stack)?;
+ offset = item_end;
+ }
+ }
+ Ok(())
+ }
+
+ fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result {
+ let mut index = 0;
+ let mut result = Self::with_capacity(
+ match T::len_params() {
+ 0 => buffer.len() / mem::size_of::(),
+ _ => 128,
+ });
+
+ if buffer.len() == 0 { return Ok(result); }
+
+ loop {
+ let next_size = match T::len_params() {
+ 0 => mem::size_of::(),
+ _ => length_stack.pop_front().ok_or(BinaryConvertError::length())?,
+ };
+ let item = if next_size == 0 {
+ T::from_empty_bytes()?
+ }
+ else {
+ if index + next_size > buffer.len() {
+ return Err(BinaryConvertError::boundaries())
+ }
+ T::from_bytes(&buffer[index..index+next_size], length_stack)?
+ };
+ result.push(item);
+
+ index = index + next_size;
+ if index == buffer.len() { break; }
+ if index > buffer.len() {
+ return Err(BinaryConvertError::boundaries())
+ }
+ }
+
+ Ok(result)
+ }
+
+ fn from_empty_bytes() -> Result {
+ Ok(Self::new())
+ }
+
+ fn len_params() -> usize {
+ 1
+ }
+}
+
+impl BinaryConvertable for String {
+ fn size(&self) -> usize {
+ self.as_bytes().len()
+ }
+
+ fn from_empty_bytes() -> Result {
+ Ok(String::new())
+ }
+
+ fn to_bytes(&self, buffer: &mut [u8], _length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> {
+ buffer[..].clone_from_slice(self.as_bytes());
+ Ok(())
+ }
+
+ fn from_bytes(buffer: &[u8], _length_stack: &mut VecDeque