use lazy_static::lazy_static;
use std::fmt::Write;
use std::collections::HashMap;
use swipc_parser::{Alias, Func, HandleType, TypeDef, Type, Decorator, Interface};
use bit_field::BitField;
fn remap_keywords(s: &str) -> &'_ str {
match s {
"type" => "ty",
s => s
}
}
lazy_static! {
static ref BUILTINS: HashMap<&'static str, (u8, &'static str)> = {
let mut types = HashMap::new();
types.insert("bool", (1, "bool"));
types.insert("u8", (1, "u8"));
types.insert("i8", (1, "i8"));
types.insert("u16", (2, "u16"));
types.insert("u32", (4, "u32"));
types.insert("i32", (4, "i32"));
types.insert("f32", (4, "f32"));
types.insert("u64", (8, "u64"));
types.insert("i64", (8, "i64"));
types.insert("u128", (16, "u128"));
types.insert("uint8_t", (1, "u8"));
types
};
}
macro_rules! matches {
(let $pat:pat = $x:expr) => { if let $pat = $x { true } else { false } };
}
#[derive(Debug)]
enum Error {
UnsupportedStruct,
}
fn is_raw(val: &Alias) -> bool {
match val {
Alias::Bytes(_) | Alias::Align(_, _) | Alias::Other(_) => true,
_ => false
}
}
fn named_iterator<'a, I>(it: I, is_output: bool) -> impl Iterator<Item = (&'a Alias, String)>
where
I: IntoIterator<Item = &'a (Alias, Option<String>)>
{
it.into_iter().filter(move |(ty, _)| {
match ty {
Alias::Array(..) | Alias::Buffer(..) => !is_output,
_ => true,
}
}).enumerate().map(|(idx, (v, name))| {
(v, name.as_ref().map(|v| remap_keywords(v).to_string()).unwrap_or_else(|| format!("unknown_{}", idx)))
})
}
fn raw_iterator<'a, I>(it: I, is_output: bool) -> impl Iterator<Item = (&'a Alias, String)>
where
I: IntoIterator<Item = &'a (Alias, Option<String>)>,
{
named_iterator(it, is_output).filter(|(v, _name)| is_raw(v))
}
fn format_args(args: &[(Alias, Option<String>)], ret: &[(Alias, Option<String>)], server: bool, async_: bool) -> Result<String, Error> {
let mut arg_list = Vec::new();
for (idx, (ty, name)) in args.iter().chain(ret.iter().filter(|(ty, _)| {
match ty { Alias::Array(..) | Alias::Buffer(..) => true, _ => false }
})).enumerate()
{
let mut s = String::new();
if !server {
if let Alias::Pid = ty {
continue;
}
}
match name.as_ref().map(|v| &**v) {
Some(name) => s += remap_keywords(name),
None => s += &format!("unknown_{}", idx)
}
s += ": ";
s += &get_type(idx >= args.len(), ty, server, if async_ { "'a" } else { "" })?;
arg_list.push(s);
}
Ok(arg_list.join(", "))
}
fn format_ret_ty(ret: &[(Alias, Option<String>)], server: bool) -> Result<String, Error> {
let mut v = Vec::new();
for (ty, _name) in named_iterator(ret, true) {
v.push(get_type(true, ty, server, "")?);
}
match v.len() {
0 => Ok("()".to_string()),
1 => Ok(v[0].clone()),
_ => Ok(format!("({})", v.join(", ")))
}
}
fn get_handle_type(ty: &Option<HandleType>) -> Option<&'static str> {
match ty {
Some(HandleType::ClientSession) => Some("self::sunrise_libuser::types::ClientSession"),
Some(HandleType::ServerSession) => Some("self::sunrise_libuser::types::ServerSession"),
Some(HandleType::ClientPort) => Some("self::sunrise_libuser::types::ClientPort"),
Some(HandleType::ServerPort) => Some("self::sunrise_libuser::types::ServerPort"),
Some(HandleType::SharedMemory) => Some("self::sunrise_libuser::types::SharedMemory"),
Some(HandleType::Process) => Some("self::sunrise_libuser::types::Process"),
Some(HandleType::Thread) => Some("self::sunrise_libuser::types::Thread"),
Some(HandleType::ReadableEvent) => Some("self::sunrise_libuser::types::ReadableEvent"),
_ => None
}
}
fn format_ret(ret: (&Alias, String)) -> Result<String, Error> {
match ret.0 {
Alias::Object(ty) => Ok(format!("{}Proxy::from(ClientSession(res__.pop_handle_move()?))", ty)),
Alias::Handle(is_copy, ty) => if let Some(s) = get_handle_type(ty) {
Ok(format!("{}(res__.pop_handle_{}()?)", s, if *is_copy { "copy" } else { "move" }))
} else {
Ok(format!("res__.pop_handle_{}()?", if *is_copy { "copy" } else { "move" }))
},
Alias::Pid => Ok("res__.pop_pid()?".to_string()),
Alias::Bytes(..) |
Alias::Align(..) |
Alias::Other(..) => Ok(format!("res__.raw().{}", ret.1)),
_ => unreachable!()
}
}
fn get_type(output: bool, ty: &Alias, is_server: bool, out_lifetime: &str) -> Result<String, Error> {
let lifetime = if out_lifetime.is_empty() { String::new() } else { format!("{} ", out_lifetime) };
let is_mut = if output { "mut " } else { "" };
match ty {
Alias::Array(underlying, _) => Ok(format!("&{}{}[{}]", lifetime, is_mut, get_type(output, underlying, is_server, out_lifetime)?)),
Alias::Buffer(box Alias::Other(name), _, None) if name == "unknown" => Err(Error::UnsupportedStruct),
Alias::Buffer(box Alias::Other(name), _, Some(size)) if name == "unknown" => Ok(format!("&{}{}[u8; {:#x}]", lifetime, is_mut, size)),
Alias::Buffer(inner @ box Alias::Other(_), _, None) => Ok(format!("&{}{}[{}]", lifetime, is_mut, get_type(output, inner, is_server, out_lifetime)?)),
Alias::Buffer(inner @ box Alias::Bytes(_), _, _) |
Alias::Buffer(inner @ box Alias::Other(_), _, _) => Ok(format!("&{}{}{}", lifetime, is_mut, get_type(output, inner, is_server, out_lifetime)?)),
Alias::Buffer(underlying, _, _) => panic!("Buffer with underlying type {:?}", underlying),
Alias::Object(name) => {
Ok(name.clone() + "Proxy")
},
Alias::Bytes(Some(0)) | Alias::Bytes(None) => Ok("[u8]".to_string()),
Alias::Bytes(Some(len)) => Ok(format!("[u8; {}]", len)),
Alias::Align(_alignment, _underlying) => Err(Error::UnsupportedStruct),
Alias::Handle(is_copy, ty) => if *is_copy && is_server && output {
Ok("self::sunrise_libuser::types::HandleRef<'static>".to_string())
} else if let Some(s) = get_handle_type(ty) {
Ok(format!("{}{}", if *is_copy && !is_server && !output { "&" } else { "" }, s))
} else {
Ok(format!("self::sunrise_libuser::types::{}", if *is_copy && !is_server && !output { "HandleRef" } else { "Handle" }))
},
Alias::Pid => Ok("self::sunrise_libuser::types::Pid".to_string()),
Alias::Other(ty) if ty == "unknown" => Err(Error::UnsupportedStruct),
Alias::Other(ty) => Ok(ty.clone()),
}
}
fn gen_in_raw(s: &mut String, cmd: &Func) -> Result<&'static str, Error> {
if cmd.args.iter().any(|(argty, _)| is_raw(argty)) {
writeln!(s, " #[repr(C)]").unwrap();
writeln!(s, " #[derive(Clone, Copy)]").unwrap();
writeln!(s, " #[allow(clippy::missing_docs_in_private_items)]").unwrap();
writeln!(s, " struct InRaw {{").unwrap();
for (argty, argname) in raw_iterator(&cmd.args, false) {
writeln!(s, " {}: {},", argname, get_type(false, argty, false, "")?).unwrap();
}
writeln!(s, " }}").unwrap();
Ok("InRaw")
} else {
Ok("()")
}
}
fn gen_out_raw(s: &mut String, cmd: &Func) -> Result<&'static str, Error> {
if cmd.ret.iter().any(|(argty, _)| is_raw(argty)) {
writeln!(s, " #[repr(C)]").unwrap();
writeln!(s, " #[derive(Clone, Copy)]").unwrap();
writeln!(s, " #[allow(clippy::missing_docs_in_private_items)]").unwrap();
writeln!(s, " struct OutRaw {{").unwrap();
for (argty, argname) in raw_iterator(&cmd.ret, true) {
writeln!(s, " {}: {},", argname, get_type(true, argty, false, "")?).unwrap();
}
writeln!(s, " }}").unwrap();
Ok("OutRaw")
} else {
Ok("()")
}
}
fn format_cmd(cmd: &Func) -> Result<String, Error> {
let mut s = String::new();
for line in cmd.doc.lines() {
writeln!(s, " /// {}", line).unwrap();
}
writeln!(s, " #[allow(unused, clippy::trivially_copy_pass_by_ref)]").unwrap();
writeln!(s, " pub fn {}(&self, {}) -> Result<{}, Error> {{", &cmd.name, format_args(&cmd.args, &cmd.ret, false, false)?, format_ret_ty(&cmd.ret, false)?).unwrap();
writeln!(s, " use self::sunrise_libuser::ipc::Message;").unwrap();
writeln!(s, " let mut buf__ = [0; 0x100];").unwrap();
writeln!(s).unwrap();
let in_raw = gen_in_raw(&mut s, cmd)?;
let ipc_count = cmd.args.iter().chain(&cmd.ret).filter(|(argty, _)| match argty {
Alias::Array(..) | Alias::Buffer(..) => true,
_ => false
}).count();
let handle_move_count = cmd.args.iter().filter(|(argty, _)| match argty {
Alias::Handle(false, _) | Alias::Object(_) => true,
_ => false
}).count();
let handle_copy_count = cmd.args.iter().filter(|(argty, _)| match argty {
Alias::Handle(true, _) => true,
_ => false
}).count();
writeln!(s, " let mut msg__ = Message::<{}, [_; {}], [_; {}], [_; {}]>::new_request(None, {});",
in_raw, ipc_count, handle_copy_count, handle_move_count, cmd.num).unwrap();
if cmd.args.iter().any(|(argty, _)| is_raw(argty)) {
writeln!(s, " msg__.push_raw(InRaw {{").unwrap();
for (_argty, argname) in raw_iterator(&cmd.args, false) {
writeln!(s, " {},", argname).unwrap();
}
writeln!(s, " }});").unwrap();
}
for (idx, (argty, argname)) in cmd.args.iter().chain(cmd.ret.iter().filter(|(argty, _)| match argty {
Alias::Array(..) | Alias::Buffer(..) => true, _ => false
})).enumerate()
{
let argname = argname.clone().unwrap_or_else(|| format!("unknown_{}", idx));
match argty {
Alias::Array(_alias, ty) => {
match (ty.get_bits(0..2), ty.get_bits(2..4), ty.get_bit(5)) {
(1, 1, false) => writeln!(s, " msg__.push_out_buffer({});", argname).unwrap(),
(2, 1, false) => writeln!(s, " msg__.push_in_buffer({});", argname).unwrap(),
(1, 2, false) => writeln!(s, " msg__.push_out_pointer({});", argname).unwrap(),
(2, 2, false) => writeln!(s, " msg__.push_in_pointer({}, {});", argname, !ty.get_bit(4)).unwrap(),
(1, 0, true) => return Err(Error::UnsupportedStruct),
(2, 0, true) => return Err(Error::UnsupportedStruct),
_ => panic!("Illegal buffer type: {}", ty)
}
},
Alias::Buffer(_alias, ty, _) => {
match (ty.get_bits(0..2), ty.get_bits(2..4), ty.get_bit(5)) {
(1, 1, false) => writeln!(s, " msg__.push_out_buffer({});", argname).unwrap(),
(2, 1, false) => writeln!(s, " msg__.push_in_buffer({});", argname).unwrap(),
(1, 2, false) => writeln!(s, " msg__.push_out_pointer({});", argname).unwrap(),
(2, 2, false) => writeln!(s, " msg__.push_in_pointer({}, {});", argname, !ty.get_bit(4)).unwrap(),
(1, 0, true) => return Err(Error::UnsupportedStruct),
(2, 0, true) => return Err(Error::UnsupportedStruct),
_ => panic!("Illegal buffer type: {}", ty)
}
},
Alias::Object(_) => writeln!(s, " msg__.push_handle_move(sunrise_libuser::types::ClientSession::from({}).into_handle());", argname).unwrap(),
Alias::Handle(false, ty) if get_handle_type(ty).is_some() =>
writeln!(s, " msg__.push_handle_move(({}).0);", argname).unwrap(),
Alias::Handle(false, _) =>
writeln!(s, " msg__.push_handle_move({});", argname).unwrap(),
Alias::Handle(true, ty) if get_handle_type(ty).is_some() =>
writeln!(s, " msg__.push_handle_copy(({}).0.as_ref());", argname).unwrap(),
Alias::Handle(true, _) =>
writeln!(s, " msg__.push_handle_copy({});", argname).unwrap(),
Alias::Pid => writeln!(s, " msg__.send_pid(None);").unwrap(),
_ => continue,
}
}
writeln!(s, " msg__.pack(&mut buf__[..]);").unwrap();
writeln!(s, " self.0.send_sync_request_with_user_buffer(&mut buf__[..])?;").unwrap();
let ipc_count = 0;
let handle_move_count = cmd.ret.iter().filter(|(argty, _)| match argty {
Alias::Handle(false, _) | Alias::Object(_) => true,
_ => false
}).count();
let handle_copy_count = cmd.ret.iter().filter(|(argty, _)| match argty {
Alias::Handle(true, _) => true,
_ => false
}).count();
writeln!(s).unwrap();
let out_raw = gen_out_raw(&mut s, cmd)?;
writeln!(s, " let mut res__: Message<'_, {}, [_; {}], [_; {}], [_; {}]> = Message::unpack(&buf__[..]);",
out_raw, ipc_count, handle_copy_count, handle_move_count).unwrap();
writeln!(s, " res__.error()?;").unwrap();
match named_iterator(&cmd.ret, true).count() {
0 => writeln!(s, " Ok(())").unwrap(),
1 => writeln!(s, " Ok({})", format_ret(named_iterator(&cmd.ret, true).next().unwrap())?).unwrap(),
_ => writeln!(s, " Ok(({}))", named_iterator(&cmd.ret, true).map(format_ret).collect::<Result<Vec<String>, Error>>()?.join(", ")).unwrap()
}
writeln!(s, " }}").unwrap();
Ok(s)
}
fn format_type(struct_name: &str, ty: &TypeDef) -> Result<String, Error> {
let mut s = String::new();
if let Type::Enum(_) = &ty.ty {
} else {
for line in ty.doc.lines() {
writeln!(s, "/// {}", line).unwrap();
}
}
match &ty.ty {
Type::Struct(struc) => {
let mut can_derive_debug = true;
for (_, _, ty) in &struc.fields {
if let Type::Alias(Alias::Bytes(_)) = ty {
can_derive_debug = false;
break;
}
}
writeln!(s, "#[repr(C)]").unwrap();
if can_derive_debug {
writeln!(s, "#[derive(Clone, Copy, Debug)]").unwrap();
} else {
writeln!(s, "#[derive(Clone, Copy)]").unwrap();
}
writeln!(s, "pub struct {} {{", struct_name).unwrap();
for (doc, name, ty) in &struc.fields {
for line in doc.lines() {
writeln!(s, " /// {}", line).unwrap();
}
let tyname = match ty {
Type::Alias(alias) => get_type(false, alias, false, "")?,
_ => unimplemented!()
};
writeln!(s, " pub {}: {},", remap_keywords(name), tyname).unwrap();
}
writeln!(s, "}}").unwrap();
if !can_derive_debug {
writeln!(s, "impl core::fmt::Debug for {} {{", struct_name).unwrap();
writeln!(s, " fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {{").unwrap();
writeln!(s, " f.debug_struct(\"{}\")", struct_name).unwrap();
for (_, name, ty) in &struc.fields {
if let Type::Alias(Alias::Bytes(_)) = ty {
writeln!(s, " .field(\"{}\", &&self.{}[..])", name, name).unwrap();
} else {
writeln!(s, " .field(\"{}\", &self.{})", name, name).unwrap();
}
}
writeln!(s, " .finish()").unwrap();
writeln!(s, " }}").unwrap();
writeln!(s, "}}").unwrap();
}
},
Type::Enum(enu) => {
writeln!(s, "enum_with_val! {{").unwrap();
for line in ty.doc.lines() {
writeln!(s, " /// {}", line).unwrap();
}
writeln!(s, " #[derive(PartialEq, Eq, Clone, Copy)]").unwrap();
writeln!(s, " pub struct {}(pub {}) {{", struct_name, enu.tyname).unwrap();
for (doc, name, num) in &enu.fields {
for line in doc.lines() {
writeln!(s, " /// {}", line).unwrap();
}
writeln!(s, " {} = {},", remap_keywords(name), num).unwrap();
}
writeln!(s, " }}").unwrap();
writeln!(s, "}}").unwrap();
},
Type::Alias(alias) => {
writeln!(s, "pub type {} = {};", struct_name, get_type(false, &alias, false, "")?).unwrap();
},
}
Ok(s)
}
#[derive(Debug)]
struct Mod {
types: Vec<String>,
ifaces: Vec<String>,
mods: HashMap<String, Mod>,
}
fn generate_mod(m: Mod, depth: usize, mod_name: &str, crate_name: &str, is_root_mod: bool) -> String {
let mut s = String::new();
let depthstr = " ".repeat(depth);
if !is_root_mod {
writeln!(s, "{}pub mod {} {{", depthstr, mod_name).unwrap();
}
writeln!(s, "{} //! Auto-generated documentation", depthstr).unwrap();
writeln!(s, "{} use crate as {};", depthstr, crate_name.replace("-", "_")).unwrap();
writeln!(s).unwrap();
if !m.ifaces.is_empty() {
writeln!(s, "{} use self::sunrise_libuser::types::ClientSession;", depthstr).unwrap();
writeln!(s, "{} use self::sunrise_libuser::error::Error;", depthstr).unwrap();
}
for (mod_name, modinfo) in m.mods {
writeln!(s).unwrap();
writeln!(s, "{}", generate_mod(modinfo, depth + 1, &mod_name, crate_name, false)).unwrap();
}
for ty in m.types {
writeln!(s).unwrap();
for line in ty.lines() {
writeln!(s, "{} {}", depthstr, line).unwrap();
}
}
for iface in m.ifaces {
writeln!(s).unwrap();
for line in iface.lines() {
writeln!(s, "{} {}", depthstr, line).unwrap();
}
}
if !is_root_mod {
writeln!(s, "{}}}", depthstr).unwrap();
}
s
}
fn gen_call(cmd: &Func, is_async: bool) -> Result<String, Error> {
let mut s = String::new();
let in_raw = gen_in_raw(&mut s, cmd)?;
let ipc_count = cmd.args.iter().chain(&cmd.ret).filter(|(argty, _)| match argty {
Alias::Array(..) | Alias::Buffer(..) => true,
_ => false
}).count();
let handle_move_count = cmd.args.iter().filter(|(argty, _)| match argty {
Alias::Handle(false, _) | Alias::Object(_) => true,
_ => false
}).count();
let handle_copy_count = cmd.args.iter().filter(|(argty, _)| match argty {
Alias::Handle(true, _) => true,
_ => false
}).count();
writeln!(s, " let mut msg__ = Message::<{}, [_; {}], [_; {}], [_; {}]>::unpack(buf);",
in_raw, ipc_count, handle_copy_count, handle_move_count).unwrap();
let mut args = String::new();
for (item, name) in named_iterator(&cmd.args, false)
.chain(named_iterator(&cmd.ret, false).filter(|(ty, _)|
match ty { Alias::Array(..) | Alias::Buffer(..) => true, _ => false }))
{
match item {
Alias::Array(underlying_ty, bufty) | Alias::Buffer(underlying_ty, bufty, _) => {
let (ismut,direction, ty) = match (bufty.get_bits(0..2), bufty.get_bits(2..4)) {
(0b01, 0b01) => ("", "in", "buffer"),
(0b01, 0b10) => ("", "in", "pointer"),
(0b10, 0b01) => ("mut", "out", "buffer"),
(0b10, 0b10) => ("mut", "out", "pointer"),
_ => panic!("Invalid bufty")
};
let realty = get_type(false, underlying_ty, false, "")?;
if let Alias::Array(..) = item {
args += &format!("unsafe {{ &{} *msg__.pop_{}_{}::<[{}]>().unwrap() }}, ", ismut, direction, ty, realty);
} else {
args += &format!("unsafe {{ &{} *msg__.pop_{}_{}::<{}>().unwrap() }}, ", ismut, direction, ty, realty);
}
},
Alias::Object(ty) => {
args += &format!("{}Proxy(self::sunrise_libuser::types::ClientSession(msg__.pop_handle_move().unwrap())), ", ty);
},
Alias::Handle(is_copy, ty) => {
let handle = if *is_copy {
"msg__.pop_handle_copy().unwrap()"
} else {
"msg__.pop_handle_move().unwrap()"
};
let to_add = match get_handle_type(ty) {
Some(ty) => format!("{}({}), ", ty, handle),
_ => format!("{}, ", handle)
};
args += &to_add;
},
Alias::Pid => {
args += "msg__.pop_pid().unwrap(), ";
},
Alias::Align(..) | Alias::Bytes(..) | Alias::Other(..) => {
args += &format!("msg__.raw().{}, ", name);
},
}
}
if is_async {
writeln!(s, " futures::future::FutureObj::new(alloc::boxed::Box::new(self.{}(work_queue, {}).map(move |ret__| {{", &cmd.name, args).unwrap();
} else {
writeln!(s, " let ret__ = self.{}(manager, {});", &cmd.name, args).unwrap();
}
let out_raw = gen_out_raw(&mut s, cmd)?;
let handle_move_count = cmd.ret.iter().filter(|(argty, _)| match argty {
Alias::Handle(false, _) | Alias::Object(_) => true,
_ => false
}).count();
let handle_copy_count = cmd.ret.iter().filter(|(argty, _)| match argty {
Alias::Handle(true, _) => true,
_ => false
}).count();
writeln!(s, " let mut msg__ = Message::<{}, [_; 0], [_; {}], [_; {}]>::new_response(None);",
out_raw, handle_copy_count, handle_move_count).unwrap();
writeln!(s, " match ret__ {{").unwrap();
writeln!(s, " Ok(ret) => {{").unwrap();
let retcount = named_iterator(&cmd.ret, true).count();
for (idx, (item, _)) in named_iterator(&cmd.ret, true).enumerate().filter(|(_, (ty, _))| !is_raw(ty))
{
let ret = if retcount == 1 {
"ret".to_string()
} else {
format!("ret.{}", idx)
};
match item {
Alias::Object(_) => {
writeln!(s, " msg__.push_handle_move(self::sunrise_libuser::types::ClientSession::from({}).into_handle());", ret).unwrap();
},
Alias::Handle(is_copy, ty) => {
if *is_copy {
writeln!(s, " msg__.push_handle_copy({});", ret).unwrap();
} else {
match (get_handle_type(ty), ty) {
(_, Some(HandleType::ClientSession)) => writeln!(s, " msg__.push_handle_move(({}).into_handle());", ret).unwrap(),
(Some(_), _) => writeln!(s, " msg__.push_handle_move(({}).0);", ret).unwrap(),
_ => writeln!(s, " msg__.push_handle_move({});", ret).unwrap(),
};
};
},
Alias::Pid => {
writeln!(s, " msg__.push_pid().unwrap();").unwrap();
},
_ => unreachable!()
}
}
if raw_iterator(&cmd.ret, true).count() > 0 {
if named_iterator(&cmd.ret, true).count() == 1 {
let (_, name) = raw_iterator(&cmd.ret, true).next().unwrap();
writeln!(s, " msg__.push_raw({} {{ {}: ret }});", out_raw, name).unwrap();
} else {
writeln!(s, " msg__.push_raw({} {{", out_raw).unwrap();
for (idx, (_, name)) in named_iterator(&cmd.ret, true).enumerate().filter(|(_, (ty, _))| is_raw(ty))
{
writeln!(s, " {}: ret.{},", name, idx).unwrap();
}
writeln!(s, " }});").unwrap();
}
}
writeln!(s, " }},").unwrap();
writeln!(s, " Err(err) => {{ msg__.set_error(err.as_code()); }}").unwrap();
writeln!(s, " }}").unwrap();
writeln!(s).unwrap();
writeln!(s, " msg__.pack(buf);").unwrap();
writeln!(s, " Ok(())").unwrap();
if is_async {
writeln!(s, " }})))").unwrap();
}
Ok(s)
}
pub fn generate_trait_async(ifacename: &str, interface: &Interface) -> String {
let mut s = String::new();
let trait_name = ifacename.split("::").last().unwrap().to_string() + "Async";
for line in interface.doc.lines() {
writeln!(s, "/// {}", line).unwrap();
}
writeln!(s, "pub trait {} {{", trait_name).unwrap();
for cmd in &interface.funcs {
match format_args(&cmd.args, &cmd.ret, true, true).and_then(|v| format_ret_ty(&cmd.ret, true).map(|u| (v, u))) {
Ok((args, ret)) => {
for line in cmd.doc.lines() {
writeln!(s, " /// {}", line).unwrap();
}
writeln!(s, " #[allow(clippy::trivially_copy_pass_by_ref)]").unwrap();
writeln!(s, " fn {}<'a>(&'a mut self, work_queue: self::sunrise_libuser::futures::WorkQueue<'static>, {}) -> futures::future::FutureObj<'a, Result<{}, Error>>;", &cmd.name, args, ret).unwrap();
},
Err(_) => writeln!(s, " // fn {}<'a>(&'a mut self) -> FutureObj<'a, Result<(), Error>>;", &cmd.name).unwrap()
}
}
writeln!(s, " /// Handle an incoming IPC request.").unwrap();
writeln!(s, " #[allow(unused)]").unwrap();
writeln!(s, " #[allow(clippy::match_single_binding)]").unwrap();
writeln!(s, " fn dispatch<'a>(&'a mut self, work_queue: self::sunrise_libuser::futures::WorkQueue<'static>, cmdid: u32, buf: &'a mut [u8]) -> futures::future::FutureObj<'_, Result<(), Error>> {{").unwrap();
writeln!(s, " use self::sunrise_libuser::ipc::Message;").unwrap();
writeln!(s, " use futures::future::FutureExt;").unwrap();
writeln!(s, " match cmdid {{").unwrap();
for func in &interface.funcs {
if let Ok(val) = gen_call(&func, true) {
writeln!(s, " {} => {{", func.num).unwrap();
writeln!(s, "{}", val).unwrap();
writeln!(s, " }},").unwrap();
} else {
writeln!(s, " // Unsupported: {}", func.num).unwrap();
}
}
writeln!(s, " _ => {{").unwrap();
writeln!(s, " let mut msg__ = Message::<(), [_; 0], [_; 0], [_; 0]>::new_response(None);").unwrap();
writeln!(s, " msg__.set_error(sunrise_libkern::error::KernelError::PortRemoteDead.make_ret() as u32);").unwrap();
writeln!(s, " msg__.pack(buf);").unwrap();
writeln!(s, " futures::future::FutureObj::new(alloc::boxed::Box::new(futures::future::ready(Ok(()))))").unwrap();
writeln!(s, " }}").unwrap();
writeln!(s, " }}").unwrap();
writeln!(s, " }}").unwrap();
writeln!(s, "}}").unwrap();
s
}
pub fn generate_trait(ifacename: &str, interface: &Interface) -> String {
let mut s = String::new();
let trait_name = ifacename.split("::").last().unwrap().to_string();
for line in interface.doc.lines() {
writeln!(s, "/// {}", line).unwrap();
}
writeln!(s, "pub trait {} {{", trait_name).unwrap();
for cmd in &interface.funcs {
match format_args(&cmd.args, &cmd.ret, true, false).and_then(|v| format_ret_ty(&cmd.ret, true).map(|u| (v, u))) {
Ok((args, ret)) => {
for line in cmd.doc.lines() {
writeln!(s, "/// {}", line).unwrap();
}
writeln!(s, " #[allow(clippy::trivially_copy_pass_by_ref)]").unwrap();
writeln!(s, " fn {}(&mut self, manager: self::sunrise_libuser::futures::WorkQueue<'static>, {}) -> Result<{}, Error>;", &cmd.name, args, ret).unwrap();
},
Err(_) => writeln!(s, " // fn {}(&mut self) -> Result<(), Error>;", &cmd.name).unwrap()
}
}
writeln!(s, " /// Handle an incoming IPC request.").unwrap();
writeln!(s, " #[allow(unused)]").unwrap();
writeln!(s, " #[allow(clippy::match_single_binding)]").unwrap();
writeln!(s, " fn dispatch<'a>(&'a mut self, manager: self::sunrise_libuser::futures::WorkQueue<'static>, cmdid: u32, buf: &'a mut [u8]) -> futures::future::FutureObj<'_, Result<(), Error>> {{").unwrap();
writeln!(s, " use self::sunrise_libuser::ipc::Message;").unwrap();
writeln!(s, " let res = match cmdid {{").unwrap();
for func in &interface.funcs {
if let Ok(val) = gen_call(&func, false) {
writeln!(s, " {} => {{", func.num).unwrap();
writeln!(s, "{}", val).unwrap();
writeln!(s, " }},").unwrap();
} else {
writeln!(s, " // Unsupported: {}", func.num).unwrap();
}
}
writeln!(s, " _ => {{").unwrap();
writeln!(s, " let mut msg__ = Message::<(), [_; 0], [_; 0], [_; 0]>::new_response(None);").unwrap();
writeln!(s, " msg__.set_error(sunrise_libkern::error::KernelError::PortRemoteDead.make_ret() as u32);").unwrap();
writeln!(s, " msg__.pack(buf);").unwrap();
writeln!(s, " Ok(())").unwrap();
writeln!(s, " }}").unwrap();
writeln!(s, " }};").unwrap();
writeln!(s, " futures::future::FutureObj::new(alloc::boxed::Box::new(futures::future::ready(res)))").unwrap();
writeln!(s, " }}").unwrap();
writeln!(s, "}}").unwrap();
s
}
pub fn generate_proxy(ifacename: &str, interface: &Interface) -> String {
let struct_name = ifacename.split("::").last().unwrap().to_string() + "Proxy";
let mut s = String::new();
for line in interface.doc.lines() {
writeln!(s, "/// {}", line).unwrap();
}
writeln!(s, "#[derive(Debug)]").unwrap();
writeln!(s, "pub struct {}(ClientSession);", struct_name).unwrap();
writeln!(s).unwrap();
writeln!(s, "impl From<{}> for ClientSession {{", struct_name).unwrap();
writeln!(s, " fn from(sess: {}) -> ClientSession {{", struct_name).unwrap();
writeln!(s, " sess.0").unwrap();
writeln!(s, " }}").unwrap();
writeln!(s, "}}").unwrap();
writeln!(s).unwrap();
writeln!(s, "impl From<ClientSession> for {} {{", struct_name).unwrap();
writeln!(s, " fn from(sess: ClientSession) -> {} {{", struct_name).unwrap();
writeln!(s, " {}(sess)", struct_name).unwrap();
writeln!(s, " }}").unwrap();
writeln!(s, "}}").unwrap();
if !interface.service_list.is_empty() {
writeln!(s, "\nimpl {} {{", struct_name).unwrap();
for (decorators, service) in &interface.service_list {
let name = if interface.service_list.len() == 1 {
"".to_string()
} else {
format!("_{}", service.replace(":", "_"))
};
writeln!(s, " /// Creates a new [{}] by connecting to the `{}` service.", struct_name, service).unwrap();
writeln!(s, " #[allow(unused_imports)]").unwrap();
writeln!(s, " pub fn raw_new{}() -> Result<{}, Error> {{", name, struct_name).unwrap();
writeln!(s, " use self::sunrise_libuser::syscalls;").unwrap();
writeln!(s, " use self::sunrise_libuser::error::KernelError;").unwrap();
if decorators.iter().any(|v| matches!(let Decorator::ManagedPort = v)) {
writeln!(s, " loop {{").unwrap();
let mut service_name = service.to_string();
service_name += &"\\0";
writeln!(s, r#" let _ = match syscalls::connect_to_named_port("{}") {{"#, service_name).unwrap();
writeln!(s, " Ok(s) => return Ok({}(s)),", struct_name).unwrap();
writeln!(s, " Err(KernelError::NoSuchEntry) => syscalls::sleep_thread(0),").unwrap();
writeln!(s, " Err(err) => Err(err)?").unwrap();
writeln!(s, " }};").unwrap();
writeln!(s, " }}").unwrap();
} else {
writeln!(s, " use self::sunrise_libuser::error::SmError;").unwrap();
writeln!(s).unwrap();
writeln!(s, " loop {{").unwrap();
writeln!(s, " let svcname = unsafe {{").unwrap();
let mut service_name = service.to_string();
service_name += &"\\0".repeat(8 - service_name.len());
writeln!(s, r#" core::mem::transmute(*b"{}")"#, service_name).unwrap();
writeln!(s, " }};").unwrap();
writeln!(s, " let _ = match self::sunrise_libuser::sm::IUserInterfaceProxy::raw_new()?.get_service(svcname) {{").unwrap();
writeln!(s, " Ok(s) => return Ok({}(s)),", struct_name).unwrap();
writeln!(s, " Err(Error::Sm(SmError::ServiceNotRegistered, ..)) => syscalls::sleep_thread(0),").unwrap();
writeln!(s, " Err(err) => return Err(err)").unwrap();
writeln!(s, " }};").unwrap();
writeln!(s, " }}").unwrap();
}
writeln!(s, " }}").unwrap();
writeln!(s, " /// Acquires the shared handle to the `{}` service - connecting if it wasn't already.", service).unwrap();
writeln!(s, " pub fn new{}() -> Result<&'static {}, Error> {{", name, struct_name).unwrap();
writeln!(s, " /// Handle static session storage").unwrap();
writeln!(s, " static HANDLE : spin::Once<{}> = spin::Once::new();", struct_name).unwrap();
writeln!(s, " if let Some(s) = HANDLE.r#try() {{").unwrap();
writeln!(s, " Ok(s)").unwrap();
writeln!(s, " }} else {{").unwrap();
writeln!(s, " let hnd = Self::raw_new{}()?;", name).unwrap();
writeln!(s, " let val = HANDLE.call_once(|| hnd);").unwrap();
writeln!(s, " Ok(val)").unwrap();
writeln!(s, " }}").unwrap();
writeln!(s, " }}").unwrap();
}
writeln!(s, "}}").unwrap();
}
writeln!(s, "impl {} {{", struct_name).unwrap();
writeln!(s, " /// Clones the current object, returning a new handle.").unwrap();
writeln!(s, " /// The returned handle has its own IPC buffer - it may be used concurrently with the original.").unwrap();
writeln!(s, " pub fn clone_current_object(&self) -> Result<Self, Error> {{").unwrap();
writeln!(s, " Ok({}::from(self.0.try_clone()?))", struct_name).unwrap();
writeln!(s, " }}").unwrap();
for cmd in &interface.funcs {
match format_cmd(&cmd) {
Ok(out) => write!(s, "{}", out).unwrap(),
Err(_) => writeln!(s, " // pub fn {}(&self) -> Result<(), Error>", &cmd.name).unwrap()
}
}
writeln!(s, "}}").unwrap();
s
}
pub fn generate_ipc(s: &str, prefix: String, mod_name: String, crate_name: String, is_root_mod: bool) -> String {
let ctx = swipc_parser::parse(s);
let mut root_mod = Mod {
types: Vec::new(),
ifaces: Vec::new(),
mods: HashMap::new()
};
for (typename, ty) in ctx.types {
let path = typename.split("::");
let path = if !prefix.is_empty() {
let mut it = prefix.split("::").zip(path);
while let Some((item1, item2)) = it.next() {
if item1 != item2 {
panic!("{} is outside of the prefix {}", typename, prefix);
}
}
let (_, path): (Vec<_>, Vec<_>) = it.unzip();
path
} else {
path.collect()
};
let mut cur_mod = &mut root_mod;
if !path.is_empty() {
for elem in &path[..path.len() - 1] {
cur_mod = cur_mod.mods.entry(elem.to_string()).or_insert(Mod {
types: Vec::new(),
ifaces: Vec::new(),
mods: HashMap::new()
});
}
}
let struct_name = typename.split("::").last().unwrap();
match format_type(struct_name, &ty) {
Ok(s) => cur_mod.types.push(s),
Err(Error::UnsupportedStruct) => cur_mod.types.push(format!("// struct {}", struct_name))
}
}
for (ifacename, interface) in ctx.interfaces {
let path = ifacename.split("::");
let path = if !prefix.is_empty() {
let mut it = prefix.split("::").zip(path);
while let Some((item1, item2)) = it.next() {
if item1 != item2 {
panic!("{} is outside of the prefix {}", ifacename, prefix);
}
}
let (_, path): (Vec<_>, Vec<_>) = it.unzip();
path
} else {
path.collect()
};
let mut cur_mod = &mut root_mod;
if !path.is_empty() {
for elem in &path[..path.len() - 1] {
cur_mod = cur_mod.mods.entry(elem.to_string()).or_insert(Mod {
types: Vec::new(),
ifaces: Vec::new(),
mods: HashMap::new()
});
}
}
cur_mod.ifaces.push(generate_proxy(&ifacename, &interface));
cur_mod.ifaces.push(generate_trait(&ifacename, &interface));
cur_mod.ifaces.push(generate_trait_async(&ifacename, &interface));
}
generate_mod(root_mod, 0, &mod_name, &crate_name, is_root_mod)
}