Merge pull request #809 from ethcore/ipc-syntax
Syntax helpers for IPC RPC
This commit is contained in:
commit
3f04f4e5c6
27
ipc/codegen/Cargo.toml
Normal file
27
ipc/codegen/Cargo.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "ethcore-ipc-codegen"
|
||||||
|
version = "1.1.0"
|
||||||
|
authors = ["Nikolay Volf"]
|
||||||
|
license = "GPL-3.0"
|
||||||
|
description = "Macros to auto-generate implementations for ipc call"
|
||||||
|
build = "build.rs"
|
||||||
|
keywords = ["ipc", "codegen"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["with-syntex"]
|
||||||
|
nightly = ["quasi_macros"]
|
||||||
|
nightly-testing = ["clippy"]
|
||||||
|
with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
quasi_codegen = { version = "^0.8.0", optional = true }
|
||||||
|
syntex = { version = "^0.30.0", optional = true }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aster = { version = "^0.14.0", default-features = false }
|
||||||
|
clippy = { version = "^0.*", optional = true }
|
||||||
|
quasi = { version = "^0.8.0", default-features = false }
|
||||||
|
quasi_macros = { version = "^0.8.0", optional = true }
|
||||||
|
syntex = { version = "^0.30.0", optional = true }
|
||||||
|
syntex_syntax = { version = "^0.30.0", optional = true }
|
||||||
|
"ethcore-ipc" = { path = "../rpc"}
|
45
ipc/codegen/build.rs
Normal file
45
ipc/codegen/build.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
mod inner {
|
||||||
|
extern crate syntex;
|
||||||
|
extern crate quasi_codegen;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let mut registry = syntex::Registry::new();
|
||||||
|
quasi_codegen::register(&mut registry);
|
||||||
|
|
||||||
|
let src = Path::new("src/lib.rs.in");
|
||||||
|
let dst = Path::new(&out_dir).join("lib.rs");
|
||||||
|
|
||||||
|
registry.expand("", &src, &dst).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
mod inner {
|
||||||
|
pub fn main() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
inner::main();
|
||||||
|
}
|
284
ipc/codegen/src/codegen.rs
Normal file
284
ipc/codegen/src/codegen.rs
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use aster;
|
||||||
|
|
||||||
|
use syntax::ast::{
|
||||||
|
MetaItem,
|
||||||
|
Item,
|
||||||
|
ImplItemKind,
|
||||||
|
ImplItem,
|
||||||
|
MethodSig,
|
||||||
|
Arg,
|
||||||
|
PatKind,
|
||||||
|
FunctionRetTy,
|
||||||
|
};
|
||||||
|
|
||||||
|
use syntax::ast;
|
||||||
|
use syntax::codemap::Span;
|
||||||
|
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||||
|
use syntax::ext::build::AstBuilder;
|
||||||
|
use syntax::ptr::P;
|
||||||
|
|
||||||
|
pub struct Error;
|
||||||
|
|
||||||
|
pub fn expand_ipc_implementation(
|
||||||
|
cx: &mut ExtCtxt,
|
||||||
|
span: Span,
|
||||||
|
meta_item: &MetaItem,
|
||||||
|
annotatable: &Annotatable,
|
||||||
|
push: &mut FnMut(Annotatable)
|
||||||
|
) {
|
||||||
|
let item = match *annotatable {
|
||||||
|
Annotatable::Item(ref item) => item,
|
||||||
|
_ => {
|
||||||
|
cx.span_err(meta_item.span, "`#[derive(Ipc)]` may only be applied to struct implementations");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let builder = aster::AstBuilder::new().span(span);
|
||||||
|
|
||||||
|
let impl_item = match implement_interface(cx, &builder, &item, push) {
|
||||||
|
Ok(item) => item,
|
||||||
|
Err(Error) => { return; }
|
||||||
|
};
|
||||||
|
|
||||||
|
push(Annotatable::Item(impl_item))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field_name(builder: &aster::AstBuilder, arg: &Arg) -> ast::Ident {
|
||||||
|
match arg.pat.node {
|
||||||
|
PatKind::Ident(_, ref ident, _) => builder.id(ident.node),
|
||||||
|
_ => { panic!("unexpected param in interface: {:?}", arg.pat.node) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_invoke_signature_aster(
|
||||||
|
builder: &aster::AstBuilder,
|
||||||
|
implement: &ImplItem,
|
||||||
|
signature: &MethodSig,
|
||||||
|
push: &mut FnMut(Annotatable),
|
||||||
|
) -> Dispatch {
|
||||||
|
|
||||||
|
let inputs = &signature.decl.inputs;
|
||||||
|
let (input_type_name, input_arg_names) = if inputs.len() > 0 {
|
||||||
|
let first_field_name = field_name(builder, &inputs[0]).name.as_str();
|
||||||
|
if first_field_name == "self" && inputs.len() == 1 { (None, vec![]) }
|
||||||
|
else {
|
||||||
|
let skip = if first_field_name == "self" { 2 } else { 1 };
|
||||||
|
let name_str = format!("{}_input", implement.ident.name.as_str());
|
||||||
|
|
||||||
|
let mut arg_names = Vec::new();
|
||||||
|
let arg_name = format!("{}", field_name(builder, &inputs[skip-1]).name);
|
||||||
|
let mut tree = builder.item()
|
||||||
|
.attr().word("derive(Serialize, Deserialize)")
|
||||||
|
.attr().word("allow(non_camel_case_types)")
|
||||||
|
.struct_(name_str.as_str())
|
||||||
|
.field(arg_name.as_str()).ty().build(inputs[skip-1].ty.clone());
|
||||||
|
arg_names.push(arg_name);
|
||||||
|
for arg in inputs.iter().skip(skip) {
|
||||||
|
let arg_name = format!("{}", field_name(builder, &arg));
|
||||||
|
tree = tree.field(arg_name.as_str()).ty().build(arg.ty.clone());
|
||||||
|
arg_names.push(arg_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
push(Annotatable::Item(tree.build()));
|
||||||
|
(Some(name_str.to_owned()), arg_names)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(None, vec![])
|
||||||
|
};
|
||||||
|
|
||||||
|
let return_type_name = match signature.decl.output {
|
||||||
|
FunctionRetTy::Ty(ref ty) => {
|
||||||
|
let name_str = format!("{}_output", implement.ident.name.as_str());
|
||||||
|
let tree = builder.item()
|
||||||
|
.attr().word("derive(Serialize, Deserialize)")
|
||||||
|
.attr().word("allow(non_camel_case_types)")
|
||||||
|
.struct_(name_str.as_str())
|
||||||
|
.field(format!("payload")).ty().build(ty.clone());
|
||||||
|
push(Annotatable::Item(tree.build()));
|
||||||
|
Some(name_str.to_owned())
|
||||||
|
}
|
||||||
|
_ => None
|
||||||
|
};
|
||||||
|
|
||||||
|
Dispatch {
|
||||||
|
function_name: format!("{}", implement.ident.name.as_str()),
|
||||||
|
input_type_name: input_type_name,
|
||||||
|
input_arg_names: input_arg_names,
|
||||||
|
return_type_name: return_type_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Dispatch {
|
||||||
|
function_name: String,
|
||||||
|
input_type_name: Option<String>,
|
||||||
|
input_arg_names: Vec<String>,
|
||||||
|
return_type_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn implement_dispatch_arm_invoke(
|
||||||
|
cx: &ExtCtxt,
|
||||||
|
builder: &aster::AstBuilder,
|
||||||
|
dispatch: &Dispatch,
|
||||||
|
) -> P<ast::Expr>
|
||||||
|
{
|
||||||
|
let deserialize_expr = quote_expr!(cx, ::bincode::serde::deserialize_from(r, ::bincode::SizeLimit::Infinite).expect("ipc deserialization error, aborting"));
|
||||||
|
let input_type_id = builder.id(dispatch.input_type_name.clone().unwrap().as_str());
|
||||||
|
let function_name = builder.id(dispatch.function_name.as_str());
|
||||||
|
let output_type_id = builder.id(dispatch.return_type_name.clone().unwrap().as_str());
|
||||||
|
|
||||||
|
let input_args_exprs = dispatch.input_arg_names.iter().map(|ref arg_name| {
|
||||||
|
let arg_ident = builder.id(arg_name);
|
||||||
|
quote_expr!(cx, input. $arg_ident)
|
||||||
|
}).collect::<Vec<P<ast::Expr>>>();
|
||||||
|
|
||||||
|
// 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
|
||||||
|
let invoke_serialize_stmt = {
|
||||||
|
let ext_cx = &*cx;
|
||||||
|
::quasi::parse_stmt_panic(&mut ::syntax::parse::new_parser_from_tts(
|
||||||
|
ext_cx.parse_sess(),
|
||||||
|
ext_cx.cfg(),
|
||||||
|
{
|
||||||
|
let _sp = ext_cx.call_site();
|
||||||
|
let mut tt = ::std::vec::Vec::new();
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("bincode"), ::syntax::parse::token::ModName)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serde"), ::syntax::parse::token::ModName)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize"), ::syntax::parse::token::Plain)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
|
||||||
|
tt.extend(::quasi::ToTokens::to_tokens(&output_type_id, ext_cx).into_iter());
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("payload"), ::syntax::parse::token::Plain)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Colon));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"), ::syntax::parse::token::Plain)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
|
||||||
|
tt.extend(::quasi::ToTokens::to_tokens(&function_name, ext_cx).into_iter());
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||||
|
|
||||||
|
for arg_expr in input_args_exprs {
|
||||||
|
tt.extend(::quasi::ToTokens::to_tokens(&arg_expr, ext_cx).into_iter());
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
|
||||||
|
}
|
||||||
|
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("bincode"), ::syntax::parse::token::ModName)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("SizeLimit"), ::syntax::parse::token::ModName)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Infinite"), ::syntax::parse::token::Plain)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap"), ::syntax::parse::token::Plain)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
|
||||||
|
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
|
||||||
|
tt
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
quote_expr!(cx, {
|
||||||
|
let input: $input_type_id = $deserialize_expr;
|
||||||
|
$invoke_serialize_stmt
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn implement_dispatch_arm(cx: &ExtCtxt, builder: &aster::AstBuilder, index: u32, dispatch: &Dispatch)
|
||||||
|
-> ast::Arm
|
||||||
|
{
|
||||||
|
let index_ident = builder.id(format!("{}", index).as_str());
|
||||||
|
let invoke_expr = implement_dispatch_arm_invoke(cx, builder, dispatch);
|
||||||
|
quote_arm!(cx, $index_ident => { $invoke_expr } )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn implement_interface(
|
||||||
|
cx: &ExtCtxt,
|
||||||
|
builder: &aster::AstBuilder,
|
||||||
|
item: &Item,
|
||||||
|
push: &mut FnMut(Annotatable),
|
||||||
|
) -> Result<P<ast::Item>, Error> {
|
||||||
|
let (generics, impl_items) = match item.node {
|
||||||
|
ast::ItemKind::Impl(_, _, ref generics, _, _, ref impl_items) => (generics, impl_items),
|
||||||
|
_ => {
|
||||||
|
cx.span_err(
|
||||||
|
item.span,
|
||||||
|
"`#[derive(Ipc)]` may only be applied to item implementations");
|
||||||
|
return Err(Error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let impl_generics = builder.from_generics(generics.clone())
|
||||||
|
.add_ty_param_bound(
|
||||||
|
builder.path().global().ids(&["ethcore_ipc"]).build()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let ty = builder.ty().path()
|
||||||
|
.segment(item.ident).with_generics(impl_generics.clone()).build()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let where_clause = &impl_generics.where_clause;
|
||||||
|
|
||||||
|
let mut dispatch_table = Vec::new();
|
||||||
|
for impl_item in impl_items {
|
||||||
|
if let ImplItemKind::Method(ref signature, _) = impl_item.node {
|
||||||
|
dispatch_table.push(push_invoke_signature_aster(builder, &impl_item, signature, push));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut index = -1;
|
||||||
|
let dispatch_arms: Vec<_> = dispatch_table.iter()
|
||||||
|
.map(|dispatch| { index = index + 1; implement_dispatch_arm(cx, builder, index as u32, dispatch) }).collect();
|
||||||
|
|
||||||
|
Ok(quote_item!(cx,
|
||||||
|
impl $impl_generics ::ipc::IpcInterface<$ty> for $ty $where_clause {
|
||||||
|
fn dispatch<R>(&self, r: &mut R) -> Vec<u8>
|
||||||
|
where R: ::std::io::Read
|
||||||
|
{
|
||||||
|
let mut method_num = vec![0u8;2];
|
||||||
|
match r.read(&mut method_num) {
|
||||||
|
Ok(size) if size == 0 => { panic!("method id not supplied" ); }
|
||||||
|
Err(e) => { panic!("ipc read error: {:?}, aborting", e); }
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
match method_num[0] as u16 + (method_num[1] as u16)*256 {
|
||||||
|
$dispatch_arms
|
||||||
|
_ => vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn invoke<W>(&self, _method_num: u16, _payload: &Option<Vec<u8>>, _w: &mut W)
|
||||||
|
where W: ::std::io::Write
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
).unwrap())
|
||||||
|
}
|
67
ipc/codegen/src/lib.rs
Normal file
67
ipc/codegen/src/lib.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! 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")]
|
||||||
|
#[macro_use]
|
||||||
|
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;
|
||||||
|
|
||||||
|
extern crate ethcore_ipc as ipc;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
use syntax::feature_gate::AttributeType;
|
||||||
|
|
||||||
|
#[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 register(reg: &mut syntex::Registry) {
|
||||||
|
reg.add_attr("feature(custom_derive)");
|
||||||
|
reg.add_attr("feature(custom_attribute)");
|
||||||
|
|
||||||
|
reg.add_decorator("derive_Ipc", codegen::expand_ipc_implementation);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
pub fn register(reg: &mut rustc_plugin::Registry) {
|
||||||
|
reg.register_syntax_extension(
|
||||||
|
syntax::parse::token::intern("derive_Ipc"),
|
||||||
|
syntax::ext::base::MultiDecorator(
|
||||||
|
Box::new(ser::expand_derive_serialize)));
|
||||||
|
}
|
17
ipc/codegen/src/lib.rs.in
Normal file
17
ipc/codegen/src/lib.rs.in
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
mod codegen;
|
9
ipc/rpc/Cargo.toml
Normal file
9
ipc/rpc/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "ethcore-ipc"
|
||||||
|
version = "1.1.0"
|
||||||
|
authors = ["Nikolay Volf <nikvolf@gmail.com>"]
|
||||||
|
license = "GPL-3.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
|
||||||
|
[dependencies]
|
24
ipc/rpc/src/interface.rs
Normal file
24
ipc/rpc/src/interface.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! IPC RPC interface
|
||||||
|
|
||||||
|
pub trait IpcInterface<T> {
|
||||||
|
/// reads the message from io, dispatches the call and returns result
|
||||||
|
fn dispatch<R>(&self, r: &mut R) -> Vec<u8> where R: ::std::io::Read;
|
||||||
|
/// encodes the invocation, writes payload and waits for result
|
||||||
|
fn invoke<W>(&self, method_num: u16, params: &Option<Vec<u8>>, w: &mut W) where W: ::std::io::Write;
|
||||||
|
}
|
20
ipc/rpc/src/lib.rs
Normal file
20
ipc/rpc/src/lib.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! IPC RPC interface
|
||||||
|
|
||||||
|
pub mod interface;
|
||||||
|
pub use interface::IpcInterface;
|
18
ipc/tests/Cargo.toml
Normal file
18
ipc/tests/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "ethcore-ipc-tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Nikolay Volf"]
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "run.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
"ethcore-ipc" = { path = "../rpc" }
|
||||||
|
bincode = "*"
|
||||||
|
serde = "0.7.0"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
syntex = "0.30.0"
|
||||||
|
serde_codegen = "0.7.0"
|
||||||
|
"ethcore-ipc-codegen" = { path = "../codegen" }
|
44
ipc/tests/build.rs
Normal file
44
ipc/tests/build.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate syntex;
|
||||||
|
extern crate ethcore_ipc_codegen as codegen;
|
||||||
|
extern crate serde_codegen;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
|
||||||
|
// ipc pass
|
||||||
|
{
|
||||||
|
let src = Path::new("service.rs.in");
|
||||||
|
let dst = Path::new(&out_dir).join("service_ipc.rs");
|
||||||
|
let mut registry = syntex::Registry::new();
|
||||||
|
codegen::register(&mut registry);
|
||||||
|
registry.expand("", &src, &dst).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// serde pass
|
||||||
|
{
|
||||||
|
let src = Path::new(&out_dir).join("service_ipc.rs");
|
||||||
|
let dst = Path::new(&out_dir).join("service_cg.rs");
|
||||||
|
let mut registry = syntex::Registry::new();
|
||||||
|
serde_codegen::register(&mut registry);
|
||||||
|
registry.expand("", &src, &dst).unwrap();
|
||||||
|
}
|
||||||
|
}
|
36
ipc/tests/examples.rs
Normal file
36
ipc/tests/examples.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::super::socket::*;
|
||||||
|
use super::super::service::*;
|
||||||
|
use ipc::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn call_service() {
|
||||||
|
// method_num = 0, f = 10 (method Service::commit)
|
||||||
|
let mut socket = TestSocket::new_ready(vec![0, 0, 0, 0, 0, 10]);
|
||||||
|
|
||||||
|
let service = Service::new();
|
||||||
|
assert_eq!(0, *service.commits.read().unwrap());
|
||||||
|
|
||||||
|
service.dispatch(&mut socket);
|
||||||
|
|
||||||
|
assert_eq!(10, *service.commits.read().unwrap());
|
||||||
|
}
|
||||||
|
}
|
23
ipc/tests/run.rs
Normal file
23
ipc/tests/run.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate bincode;
|
||||||
|
extern crate ethcore_ipc as ipc;
|
||||||
|
extern crate serde;
|
||||||
|
|
||||||
|
pub mod socket;
|
||||||
|
pub mod service;
|
||||||
|
mod examples;
|
17
ipc/tests/service.rs
Normal file
17
ipc/tests/service.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/service_cg.rs"));
|
45
ipc/tests/service.rs.in
Normal file
45
ipc/tests/service.rs.in
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
pub struct Service {
|
||||||
|
pub commits: RwLock<usize>,
|
||||||
|
pub rollbacks: RwLock<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Ipc)]
|
||||||
|
impl Service {
|
||||||
|
fn commit(&self, f: u32) -> u32 {
|
||||||
|
let mut lock = self.commits.write().unwrap();
|
||||||
|
*lock = *lock + f as usize;
|
||||||
|
f
|
||||||
|
}
|
||||||
|
pub fn rollback(&self, a: u32, b: u32) -> i32 {
|
||||||
|
let mut lock = self.rollbacks.write().unwrap();
|
||||||
|
*lock = *lock + a as usize - b as usize;
|
||||||
|
(a - b) as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service {
|
||||||
|
pub fn new() -> Service {
|
||||||
|
Service {
|
||||||
|
commits: RwLock::new(0usize),
|
||||||
|
rollbacks: RwLock::new(0usize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
ipc/tests/socket.rs
Normal file
94
ipc/tests/socket.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::io::*;
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
pub struct TestSocket {
|
||||||
|
pub read_buffer: Vec<u8>,
|
||||||
|
pub write_buffer: Vec<u8>,
|
||||||
|
pub cursor: usize,
|
||||||
|
pub buf_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TestSocket {
|
||||||
|
fn default() -> Self {
|
||||||
|
TestSocket::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestSocket {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
TestSocket {
|
||||||
|
read_buffer: vec![],
|
||||||
|
write_buffer: vec![],
|
||||||
|
cursor: 0,
|
||||||
|
buf_size: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_buf(buf_size: usize) -> TestSocket {
|
||||||
|
TestSocket {
|
||||||
|
read_buffer: vec![],
|
||||||
|
write_buffer: vec![],
|
||||||
|
cursor: 0,
|
||||||
|
buf_size: buf_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_ready(data: Vec<u8>) -> TestSocket {
|
||||||
|
TestSocket {
|
||||||
|
read_buffer: data,
|
||||||
|
write_buffer: vec![],
|
||||||
|
cursor: 0,
|
||||||
|
buf_size: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for TestSocket {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
let end_position = cmp::min(self.read_buffer.len(), self.cursor+buf.len());
|
||||||
|
let len = cmp::max(end_position - self.cursor, 0);
|
||||||
|
match len {
|
||||||
|
0 => Ok(0),
|
||||||
|
_ => {
|
||||||
|
for i in self.cursor..end_position {
|
||||||
|
buf[i-self.cursor] = self.read_buffer[i];
|
||||||
|
}
|
||||||
|
self.cursor = self.cursor + buf.len();
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for TestSocket {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||||
|
if self.buf_size == 0 || buf.len() < self.buf_size {
|
||||||
|
self.write_buffer.extend(buf.iter().cloned());
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.write_buffer.extend(buf.iter().take(self.buf_size).cloned());
|
||||||
|
Ok(self.buf_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> Result<()> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user