diff --git a/analysis/src/cdecl.rs b/analysis/src/cdecl.rs index d354c13fe..6915318e0 100644 --- a/analysis/src/cdecl.rs +++ b/analysis/src/cdecl.rs @@ -390,4 +390,92 @@ impl<'a> CDecl<'a> { bitfield_width, }) } + + pub fn to_pseudo_rust(&self) -> String { + self.to_pseudo_rust_with_external_lengths(&[]) + } + pub fn to_pseudo_rust_with_external_lengths(&self, external_lengths: &[&str]) -> String { + let CDecl { + ty, + name, + bitfield_width, + } = self; + let decl = format!( + "{name}: {}", + ty.to_pseudo_rust_with_external_lengths(external_lengths) + ); + match bitfield_width { + Some(width) => format!("#[bitfield({width})] {decl}"), + None => decl, + } + } +} + +impl CType<'_> { + pub fn to_pseudo_rust_with_external_lengths(&self, external_lengths: &[&str]) -> String { + if let Some((len, remaining_lengths)) = external_lengths.split_first() { + match self { + CType::Ptr { + implicit_for_decay: false, + is_const, + pointee, + } => { + let const_or_mut = if *is_const { "const" } else { "mut" }; + format!( + "*{const_or_mut} [{}; dyn {len}]", + pointee.to_pseudo_rust_with_external_lengths(remaining_lengths) + ) + } + _ => unreachable!(), + } + } else { + self.to_pseudo_rust() + } + } + pub fn to_pseudo_rust(&self) -> String { + match self { + &CType::Base(CBaseType { struct_tag, name }) => { + if struct_tag { + format!("/*struct*/{name}") + } else { + name.to_string() + } + } + CType::Ptr { + implicit_for_decay, + is_const, + pointee, + } => { + if let CType::Func { ret_ty, params } = &**pointee { + assert!(!implicit_for_decay); + assert!(!is_const); + let params = if params.is_empty() { + "".to_string() + } else { + params.iter().fold("\n".to_string(), |params, param| { + params + " " + ¶m.to_pseudo_rust() + ",\n" + }) + }; + format!( + "unsafe extern fn({params}){}", + ret_ty + .as_ref() + .map(|ty| format!(" -> {}", ty.to_pseudo_rust())) + .unwrap_or_default() + ) + } else { + let const_or_mut = if *is_const { "const" } else { "mut" }; + format!("*{const_or_mut} {}", pointee.to_pseudo_rust()) + } + } + CType::Array { element, len } => { + let len = match len { + CArrayLen::Named(name) => name.to_string(), + CArrayLen::Literal(len) => len.to_string(), + }; + format!("[{}; {len}]", element.to_pseudo_rust()) + } + CType::Func { .. } => unreachable!(), + } + } } diff --git a/analysis/src/lib.rs b/analysis/src/lib.rs index bfdab0fc7..8950b3ecf 100644 --- a/analysis/src/lib.rs +++ b/analysis/src/lib.rs @@ -18,6 +18,50 @@ impl Analysis { video: Library::new(vulkan_headers_path.join("registry/video.xml")), } } + + pub fn dump_as_pseudo_rust(&self) { + for fp in &self.vk._xml.funcpointers { + eprintln!( + "type {} = {};", + fp.c_decl.name, + fp.c_decl.ty.to_pseudo_rust() + ); + } + for st in &self.vk._xml.structs { + eprintln!("struct {} {{", st.name); + for m in &st.members { + if m.len.len() > 1 {} + let len = if !m.altlen.is_empty() { + &m.altlen + } else { + &m.len + }; + eprint!(" {}", m.c_decl.to_pseudo_rust_with_external_lengths(len)); + if let Some(val) = &m.values { + eprint!(" = {val}"); + } + eprintln!(","); + } + eprintln!("}}"); + } + for cmd in &self.vk._xml.commands { + eprintln!("unsafe extern fn {}(", cmd.name); + for p in &cmd.params { + let len = if !p.altlen.is_empty() { + &p.altlen + } else { + &p.len + }; + eprint!(" {}", p.c_decl.to_pseudo_rust_with_external_lengths(len)); + eprintln!(","); + } + eprint!(")"); + if let Some(ret_ty) = &cmd.return_type { + eprint!(" -> {}", ret_ty.to_pseudo_rust()); + } + eprintln!(";"); + } + } } #[derive(Debug)] diff --git a/analysis/src/xml.rs b/analysis/src/xml.rs index db8dc1338..40d26665f 100644 --- a/analysis/src/xml.rs +++ b/analysis/src/xml.rs @@ -15,49 +15,50 @@ pub trait UnwrapBorrowed<'a, B> where B: ToOwned + ?Sized, { - fn unwrap_borrowed(&self) -> &'a B; + fn unwrap_borrowed_or_leak_owned(self) -> &'a B; } impl<'a, B> UnwrapBorrowed<'a, B> for Cow<'a, B> where - B: ToOwned + ?Sized, + B: ToOwned + ?Sized + std::fmt::Debug, { - fn unwrap_borrowed(&self) -> &'a B { + fn unwrap_borrowed_or_leak_owned(self) -> &'a B { match self { Cow::Borrowed(b) => b, - Cow::Owned(_) => panic!("Called `unwrap_borrowed` on `Owned` value"), + Cow::Owned(o) => { + let leaked = std::borrow::Borrow::borrow(Box::leak(Box::new(o))); + debug!("unwrap_borrowed_or_leak_owned: leaking `Owned({leaked:?})`"); + leaked + } } } } /// Converts `roxmltree`'s `StringStorage` to a `XmlStr` -fn make_xml_str(string_storage: StringStorage<'static>) -> XmlStr { +fn make_xml_str(string_storage: &StringStorage<'static>) -> XmlStr { match string_storage { StringStorage::Borrowed(s) => Cow::Borrowed(s), - StringStorage::Owned(s) => Cow::Owned((*s).into()), + StringStorage::Owned(s) => Cow::Owned((**s).into()), } } /// Retrieves the value of the `node`'s attribute named `name`. fn attribute(node: Node, name: &str) -> Option { node.attribute_node(name) - .map(|attr| make_xml_str(attr.value_storage().clone())) + .map(|attr| make_xml_str(attr.value_storage())) } /// Retrieves the ','-separated values of the `node`'s attribute named `name`. fn attribute_comma_separated(node: Node, name: &str) -> Vec<&'static str> { attribute(node, name) - .map(|value| value.unwrap_borrowed().split(',').collect()) + .map(|value| value.unwrap_borrowed_or_leak_owned().split(',').collect()) .unwrap_or_default() } /// Retrieves the text inside the next child element of `node` named `name`. fn child_text(node: Node, name: &str) -> Option { let child = node.children().find(|node| node.has_tag_name(name)); - child.map(|node| match node.text_storage().unwrap().clone() { - StringStorage::Borrowed(s) => Cow::Borrowed(s), - StringStorage::Owned(s) => Cow::Owned((*s).into()), - }) + child.map(|node| make_xml_str(node.text_storage().unwrap())) } /// Returns [`true`] when the `node`'s "api" attribute matches the `expected` API. @@ -81,7 +82,8 @@ impl CDecl<'static> { fn from_xml(mode: CDeclMode, children: roxmltree::Children<'_, 'static>) -> CDecl<'static> { let mut c_tokens = vec![]; for child in children { - let text = || make_xml_str(child.text_storage().unwrap().clone()).unwrap_borrowed(); + let text = + || make_xml_str(child.text_storage().unwrap()).unwrap_borrowed_or_leak_owned(); match child.node_type() { NodeType::Text => { CTok::lex_into(text(), &mut c_tokens).unwrap(); @@ -424,7 +426,7 @@ pub struct StructureMember { pub c_decl: CDecl<'static>, pub values: Option, pub len: Vec<&'static str>, - pub altlen: Option, + pub altlen: Vec<&'static str>, pub optional: Vec<&'static str>, } @@ -434,7 +436,7 @@ impl StructureMember { c_decl: CDecl::from_xml(CDeclMode::StructMember, node.children()), values: attribute(node, "values"), len: attribute_comma_separated(node, "len"), - altlen: attribute(node, "altlen"), + altlen: attribute_comma_separated(node, "altlen"), optional: attribute_comma_separated(node, "optional"), } } @@ -581,8 +583,8 @@ impl BitMask { #[derive(Debug)] pub struct CommandParam { pub c_decl: CDecl<'static>, - pub len: Option, - pub altlen: Option, + pub len: Vec<&'static str>, + pub altlen: Vec<&'static str>, pub optional: Vec<&'static str>, } @@ -590,8 +592,8 @@ impl CommandParam { fn from_node(node: Node) -> CommandParam { CommandParam { c_decl: CDecl::from_xml(CDeclMode::FuncParam, node.children()), - len: attribute(node, "len"), - altlen: attribute(node, "altlen"), + len: attribute_comma_separated(node, "len"), + altlen: attribute_comma_separated(node, "altlen"), optional: attribute_comma_separated(node, "optional"), } } @@ -747,7 +749,13 @@ impl Require { fn from_node(node: Node, api: &str) -> Require { let mut value = Require { depends: attribute(node, "depends") - .map(|value| (value.unwrap_borrowed().split(',').map(Depends::from_str)).collect()) + .map(|value| { + (value + .unwrap_borrowed_or_leak_owned() + .split(',') + .map(Depends::from_str)) + .collect() + }) .unwrap_or_default(), ..Default::default() }; diff --git a/generator-rewrite/src/main.rs b/generator-rewrite/src/main.rs index fb5ff861d..13714bc5a 100644 --- a/generator-rewrite/src/main.rs +++ b/generator-rewrite/src/main.rs @@ -4,4 +4,7 @@ fn main() { tracing_subscriber::fmt::init(); let _analysis = Analysis::new("generator/Vulkan-Headers"); // dbg!(_analysis); + if true { + _analysis.dump_as_pseudo_rust(); + } }