diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
commit | c23a457e72abe608715ac76f076f47dc42af07a5 (patch) | |
tree | 2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /vendor/windows-bindgen/src/rdl | |
parent | Releasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip |
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/windows-bindgen/src/rdl')
-rw-r--r-- | vendor/windows-bindgen/src/rdl/fmt.rs | 454 | ||||
-rw-r--r-- | vendor/windows-bindgen/src/rdl/from_reader.rs | 435 | ||||
-rw-r--r-- | vendor/windows-bindgen/src/rdl/mod.rs | 338 | ||||
-rw-r--r-- | vendor/windows-bindgen/src/rdl/to_winmd.rs | 335 |
4 files changed, 1562 insertions, 0 deletions
diff --git a/vendor/windows-bindgen/src/rdl/fmt.rs b/vendor/windows-bindgen/src/rdl/fmt.rs new file mode 100644 index 000000000..8b2df36aa --- /dev/null +++ b/vendor/windows-bindgen/src/rdl/fmt.rs @@ -0,0 +1,454 @@ +use super::*; + +// TODO: should we use rustfmt in the short term (with pre/post) + +#[derive(Default)] +pub struct Writer { + out: String, + indent: usize, + newline: bool, +} + +impl Writer { + pub fn new(file: &rdl::File) -> Self { + let mut writer = Self::default(); + writer.rdl_file(file); + writer + } + + pub fn into_string(mut self) -> String { + self.out.push('\n'); + self.out + } + + fn word(&mut self, value: &str) { + if self.newline { + self.newline = false; + self.out.push('\n'); + for _ in 0..self.indent { + self.out.push_str(" "); + } + } + + self.out.push_str(value); + } + + fn newline(&mut self) { + self.newline = true; + } + + fn rdl_file(&mut self, file: &rdl::File) { + if file.winrt { + self.word("#![winrt]\n"); + } else { + self.word("#![win32]\n"); + } + + self.newline(); + + for reference in &file.references { + self.item_use(reference); + } + + for module in &file.modules { + self.rdl_module(module); + } + } + + fn rdl_module(&mut self, module: &rdl::Module) { + self.word("mod "); + self.word(module.name()); + self.word(" {"); + self.newline(); + self.indent += 1; + + for member in &module.members { + self.rdl_module_member(member); + self.newline(); + } + + self.indent -= 1; + self.newline(); + self.word("}"); + self.newline(); + } + + fn rdl_module_member(&mut self, member: &rdl::ModuleMember) { + match member { + rdl::ModuleMember::Module(member) => self.rdl_module(member), + rdl::ModuleMember::Interface(member) => self.rdl_interface(member), + rdl::ModuleMember::Struct(member) => self.rdl_struct(member), + rdl::ModuleMember::Enum(member) => self.rdl_enum(member), + rdl::ModuleMember::Class(member) => self.rdl_class(member), + rdl::ModuleMember::Constant(member) => self.rdl_constant(member), + rdl::ModuleMember::Function(member) => self.rdl_function(member), + } + } + + fn rdl_class(&mut self, member: &rdl::Class) { + self.attrs(&member.attributes); + self.word("class "); + self.word(&member.name); + + if !member.extends.is_empty() || member.base.is_some() { + self.word(" : "); + + if let Some(path) = &member.base { + self.word("class "); + self.type_path(path); + + if !member.extends.is_empty() { + self.word(", "); + } + } + + let mut first = true; + for path in &member.extends { + if first { + first = false; + } else { + self.word(", "); + } + self.type_path(path); + } + } + + self.word(";"); + self.newline(); + } + + fn rdl_interface(&mut self, member: &rdl::Interface) { + self.attrs(&member.attributes); + self.word("interface "); + self.word(&member.name); + + if !member.generics.is_empty() { + self.word("<"); + + let mut first = true; + for generic in &member.generics { + if first { + first = false; + } else { + self.word(", "); + } + self.word(generic); + } + + self.word(">"); + } + + if !member.extends.is_empty() { + self.word(" : "); + + let mut first = true; + for path in &member.extends { + if first { + first = false; + } else { + self.word(", "); + } + self.type_path(path); + } + } + + self.word(" {"); + self.newline(); + self.indent += 1; + + for method in &member.methods { + self.trait_item_fn(method); + self.word(";"); + self.newline(); + } + + self.indent -= 1; + self.newline(); + self.word("}"); + } + + fn rdl_constant(&mut self, member: &rdl::Constant) { + self.item_const(&member.item); + } + + fn rdl_function(&mut self, member: &rdl::Function) { + self.trait_item_fn(&member.item); + self.word(";"); + self.newline(); + } + + fn item_const(&mut self, item: &syn::ItemConst) { + self.word("const "); + self.ident(&item.ident); + self.word(": "); + self.ty(&item.ty); + self.word(" = "); + self.expr(&item.expr); + self.word(";"); + self.newline(); + } + + fn attrs(&mut self, attrs: &[syn::Attribute]) { + for attr in attrs { + self.attr(attr); + } + } + + fn attr(&mut self, attr: &syn::Attribute) { + self.word("#["); + self.meta(&attr.meta); + self.word("]"); + self.newline(); + } + + fn meta(&mut self, meta: &syn::Meta) { + match meta { + syn::Meta::Path(path) => self.path(path), + syn::Meta::List(list) => self.meta_list(list), + syn::Meta::NameValue(meta) => self.meta_name_value(meta), + } + } + + fn meta_list(&mut self, meta_list: &syn::MetaList) { + self.path(&meta_list.path); + self.word("("); + self.word(&meta_list.tokens.to_string()); + self.word(")"); + } + + fn meta_name_value(&mut self, meta: &syn::MetaNameValue) { + self.path(&meta.path); + self.word(" = "); + self.expr(&meta.value); + } + + fn rdl_struct(&mut self, member: &rdl::Struct) { + self.attrs(&member.attributes); + + self.word("struct "); + self.word(&member.name); + self.word(" {"); + self.newline(); + self.indent += 1; + + for field in &member.fields { + self.word(&field.name); + self.word(": "); + self.ty(&field.ty); + self.word(","); + self.newline(); + } + + self.indent -= 1; + self.newline(); + self.word("}"); + } + + fn rdl_enum(&mut self, member: &rdl::Enum) { + self.attrs(&member.item.attrs); + + self.word("enum "); + self.ident(&member.item.ident); + self.word(" {"); + self.newline(); + self.indent += 1; + + for variant in &member.item.variants { + self.ident(&variant.ident); + if let Some((_, expr)) = &variant.discriminant { + self.word(" = "); + self.expr(expr); + } + self.word(","); + self.newline(); + } + + self.indent -= 1; + self.newline(); + self.word("}"); + } + + fn trait_item_fn(&mut self, method: &syn::TraitItemFn) { + self.attrs(&method.attrs); + self.signature(&method.sig); + } + + fn signature(&mut self, signature: &syn::Signature) { + self.word("fn "); + self.ident(&signature.ident); + self.word("("); + + let mut first = true; + for input in &signature.inputs { + if first { + first = false; + } else { + self.word(", "); + } + self.fn_arg(input); + } + + self.word(")"); + + if let syn::ReturnType::Type(_, ty) = &signature.output { + self.word(" -> "); + self.ty(ty); + } + } + + fn fn_arg(&mut self, fn_arg: &syn::FnArg) { + if let syn::FnArg::Typed(pat_type) = fn_arg { + self.pat_type(pat_type); + } + } + + fn pat_type(&mut self, pat_type: &syn::PatType) { + self.pat(&pat_type.pat); + self.word(": "); + self.ty(&pat_type.ty); + } + + fn pat(&mut self, pat: &syn::Pat) { + match pat { + syn::Pat::Ident(pat_ident) => self.pat_ident(pat_ident), + rest => unimplemented!("{rest:?}"), + } + } + + fn pat_ident(&mut self, pat_ident: &syn::PatIdent) { + self.ident(&pat_ident.ident); + } + + fn ty(&mut self, ty: &syn::Type) { + match ty { + syn::Type::Path(ty) => self.type_path(ty), + syn::Type::Ptr(ptr) => self.type_ptr(ptr), + syn::Type::Array(array) => self.type_array(array), + rest => unimplemented!("{rest:?}"), + } + } + + fn type_array(&mut self, array: &syn::TypeArray) { + self.word("["); + self.ty(&array.elem); + self.word("; "); + self.expr(&array.len); + self.word("]"); + } + + fn expr(&mut self, expr: &syn::Expr) { + match expr { + syn::Expr::Lit(lit) => self.expr_lit(lit), + syn::Expr::Unary(unary) => self.expr_unary(unary), + rest => unimplemented!("{rest:?}"), + } + } + + fn expr_unary(&mut self, unary: &syn::ExprUnary) { + self.word("-"); + self.expr(&unary.expr); + } + + fn expr_lit(&mut self, expr: &syn::ExprLit) { + self.lit(&expr.lit); + } + + fn lit(&mut self, lit: &syn::Lit) { + match lit { + syn::Lit::Int(lit) => self.lit_int(lit), + syn::Lit::Str(lit) => self.lit_str(lit), + _ => _ = dbg!(lit), + } + } + + fn lit_str(&mut self, lit: &syn::LitStr) { + self.word("\""); + self.word(&lit.value()); + self.word("\""); + } + + fn lit_int(&mut self, lit: &syn::LitInt) { + self.word(&lit.token().to_string()); + } + + fn type_ptr(&mut self, ptr: &syn::TypePtr) { + if ptr.mutability.is_some() { + self.word("*mut "); + } else { + self.word("*const "); + } + self.ty(&ptr.elem); + } + + fn type_path(&mut self, ty: &syn::TypePath) { + self.path(&ty.path); + } + + fn path(&mut self, path: &syn::Path) { + let mut first = true; + for segment in &path.segments { + if first { + first = false; + } else { + self.word("::"); + } + self.path_segment(segment); + } + } + + pub fn path_segment(&mut self, segment: &syn::PathSegment) { + self.ident(&segment.ident); + + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + self.word("<"); + + let mut first = true; + for arg in &args.args { + if first { + first = false; + } else { + self.word(", "); + } + self.generic_argument(arg); + } + + self.word(">"); + } + } + + fn generic_argument(&mut self, arg: &syn::GenericArgument) { + match arg { + syn::GenericArgument::Type(ty) => self.ty(ty), + rest => unimplemented!("{rest:?}"), + } + } + + fn item_use(&mut self, item: &syn::ItemUse) { + self.word("use "); + self.use_tree(&item.tree); + self.word(";"); + self.newline(); + } + + fn use_tree(&mut self, use_tree: &syn::UseTree) { + match use_tree { + syn::UseTree::Path(use_path) => self.use_path(use_path), + syn::UseTree::Name(use_name) => self.use_name(use_name), + _ => {} + } + } + + fn use_path(&mut self, use_path: &syn::UsePath) { + self.ident(&use_path.ident); + self.word("::"); + self.use_tree(&use_path.tree); + } + + fn use_name(&mut self, use_name: &syn::UseName) { + self.ident(&use_name.ident); + } + + pub fn ident(&mut self, ident: &syn::Ident) { + self.word(&ident.to_string()); + } +} diff --git a/vendor/windows-bindgen/src/rdl/from_reader.rs b/vendor/windows-bindgen/src/rdl/from_reader.rs new file mode 100644 index 000000000..136430a9a --- /dev/null +++ b/vendor/windows-bindgen/src/rdl/from_reader.rs @@ -0,0 +1,435 @@ +use super::*; +use crate::tokens::{quote, to_ident, TokenStream}; +use crate::{rdl, Error, Result, Tree}; +use metadata::RowReader; + +pub fn from_reader(reader: &metadata::Reader, filter: &metadata::Filter, mut config: std::collections::BTreeMap<&str, &str>, output: &str) -> Result<()> { + let dialect = match config.remove("type") { + Some("winrt") => Dialect::WinRT, + Some("win32") => Dialect::Win32, + _ => return Err(Error::new("configuration value `type` must be `win32` or `winrt`")), + }; + + let mut writer = Writer::new(reader, filter, output, dialect); + + // TODO: be sure to use the same "split" key for winmd splitting. + // May also want to support split=N similar to the way MIDLRT supports winmd splitting + // at different nesting levels. + writer.split = config.remove("split").is_some(); + + if let Some((key, _)) = config.first_key_value() { + return Err(Error::new(&format!("invalid configuration value `{key}`"))); + } + + if writer.split { + gen_split(&writer) + } else { + gen_file(&writer) + } +} + +fn gen_split(writer: &Writer) -> Result<()> { + let tree = Tree::new(writer.reader, writer.filter); + let directory = crate::directory(writer.output); + + // TODO: parallelize + for tree in tree.flatten() { + let tokens = writer.tree(tree); + + if !tokens.is_empty() { + let output = format!("{directory}/{}.rdl", tree.namespace); + writer.write_to_file(&output, tokens)?; + } + } + + Ok(()) +} + +fn gen_file(writer: &Writer) -> Result<()> { + let tree = Tree::new(writer.reader, writer.filter); + let tokens = writer.tree(&tree); + writer.write_to_file(writer.output, tokens) +} + +#[derive(Debug, Copy, Clone, PartialEq)] +enum Dialect { + Win32, + WinRT, +} + +struct Writer<'a> { + reader: &'a metadata::Reader<'a>, + filter: &'a metadata::Filter<'a>, + namespace: &'a str, + dialect: Dialect, + split: bool, + output: &'a str, +} + +impl<'a> Writer<'a> { + fn new(reader: &'a metadata::Reader, filter: &'a metadata::Filter, output: &'a str, dialect: Dialect) -> Self { + Self { reader, filter, namespace: "", output, dialect, split: false } + } + + fn with_namespace(&self, namespace: &'a str) -> Self { + Self { reader: self.reader, filter: self.filter, namespace, dialect: self.dialect, output: self.output, split: self.split } + } + + fn write_to_file(&self, output: &str, tokens: TokenStream) -> Result<()> { + let dialect = match self.dialect { + Dialect::Win32 => quote! { #![win32] }, + Dialect::WinRT => quote! { #![winrt] }, + }; + + let tokens = quote! { + #dialect + #tokens + }; + + let file = rdl::File::parse_str(&tokens.into_string())?; + crate::write_to_file(output, file.fmt()) + //crate::write_to_file(output, tokens.into_string()) + } + + fn tree(&self, tree: &'a Tree) -> TokenStream { + let items = self.items(tree); + + if self.split { + let mut tokens = items; + + if !tokens.is_empty() { + for name in tree.namespace.rsplit('.').map(to_ident) { + tokens = quote! { + mod #name { + #tokens + } + }; + } + } + + tokens + } else { + let name = to_ident(tree.namespace.rsplit_once('.').map_or(tree.namespace, |(_, name)| name)); + + let modules = tree.nested.values().map(|tree| self.with_namespace(tree.namespace).tree(tree)); + + if tree.namespace.is_empty() { + quote! { + #(#modules)* + #items + } + } else { + quote! { + mod #name { + #(#modules)* + #items + } + } + } + } + } + + fn items(&self, tree: &'a Tree) -> TokenStream { + let mut functions = vec![]; + let mut constants = vec![]; + let mut types = vec![]; + + if !tree.namespace.is_empty() { + for item in self.reader.namespace_items(tree.namespace, self.filter).filter(|item| match item { + metadata::Item::Type(def) => { + let winrt = self.reader.type_def_flags(*def).contains(metadata::TypeAttributes::WindowsRuntime); + match self.dialect { + Dialect::Win32 => !winrt, + Dialect::WinRT => winrt, + } + } + metadata::Item::Fn(_, _) | metadata::Item::Const(_) => self.dialect == Dialect::Win32, + }) { + match item { + metadata::Item::Type(def) => types.push(self.type_def(def)), + metadata::Item::Const(field) => constants.push(self.constant(field)), + metadata::Item::Fn(method, namespace) => functions.push(self.function(method, &namespace)), + } + } + } + + quote! { + #(#functions)* + #(#constants)* + #(#types)* + } + } + + fn function(&self, def: metadata::MethodDef, _namespace: &str) -> TokenStream { + let name = to_ident(self.reader.method_def_name(def)); + quote! { fn #name(); } + } + + fn constant(&self, def: metadata::Field) -> TokenStream { + let name = to_ident(self.reader.field_name(def)); + quote! { const #name: i32 = 0; } + } + + fn type_def(&self, def: metadata::TypeDef) -> TokenStream { + if let Some(extends) = self.reader.type_def_extends(def) { + if extends.namespace == "System" { + if extends.name == "Enum" { + self.enum_def(def) + } else if extends.name == "ValueType" { + self.struct_def(def) + } else if extends.name == "MulticastDelegate" { + self.delegate_def(def) + } else { + self.class_def(def) + } + } else { + self.class_def(def) + } + } else { + self.interface_def(def) + } + } + + fn enum_def(&self, def: metadata::TypeDef) -> TokenStream { + let name = to_ident(self.reader.type_def_name(def)); + + quote! { + struct #name { + + } + } + } + + fn struct_def(&self, def: metadata::TypeDef) -> TokenStream { + let name = to_ident(self.reader.type_def_name(def)); + + let fields = self.reader.type_def_fields(def).map(|field| { + let name = to_ident(self.reader.field_name(field)); + let ty = self.ty(&self.reader.field_type(field, Some(def))); + quote! { + #name: #ty + } + }); + + quote! { + struct #name { + #(#fields),* + } + } + } + + fn delegate_def(&self, def: metadata::TypeDef) -> TokenStream { + let name = to_ident(self.reader.type_def_name(def)); + + quote! { + struct #name { + + } + } + } + + fn class_def(&self, def: metadata::TypeDef) -> TokenStream { + let name = to_ident(self.reader.type_def_name(def)); + let implements = self.implements(def, &[]); + + quote! { + class #name #implements; + } + } + + fn interface_def(&self, def: metadata::TypeDef) -> TokenStream { + let name = to_ident(self.reader.type_def_name(def)); + let generics = &metadata::type_def_generics(self.reader, def); + let implements = self.implements(def, generics); + + let methods = self.reader.type_def_methods(def).map(|method| { + let name = to_ident(self.reader.method_def_name(method)); + + // TODO: use reader.method_def_signature instead + let signature = metadata::method_def_signature(self.reader, self.reader.type_def_namespace(def), method, generics); + + let return_type = self.return_type(&signature.return_type); + + let params = signature.params.iter().map(|param| { + let name = to_ident(self.reader.param_name(param.def)); + let ty = self.ty(¶m.ty); + quote! { #name: #ty } + }); + + quote! { + fn #name(#(#params),*) #return_type; + } + }); + + let generics = self.generics(generics); + + quote! { + interface #name #generics #implements { + #(#methods)* + } + } + } + + fn generics(&self, generics: &[metadata::Type]) -> TokenStream { + if generics.is_empty() { + quote! {} + } else { + let generics = generics.iter().map(|generic| self.ty(generic)); + + quote! { <#(#generics),*>} + } + } + + fn implements(&self, def: metadata::TypeDef, generics: &[metadata::Type]) -> TokenStream { + let mut types = Vec::<TokenStream>::new(); + + // TODO: if a winrt composable class then start with base + // TODO: then list default interface first + // Then everything else + + for imp in self.reader.type_def_interface_impls(def) { + let ty = self.reader.interface_impl_type(imp, generics); + if self.reader.has_attribute(imp, "DefaultAttribute") { + types.insert(0, self.ty(&ty)); + } else { + types.push(self.ty(&ty)); + } + } + + if let Some(type_name) = self.reader.type_def_extends(def) { + if type_name != metadata::TypeName::Object { + let namespace = self.namespace(type_name.namespace); + let name = to_ident(type_name.name); + // TODO: ideally the "class" contextual keyword wouldn't be needed here + // but currently there's no way to tell the base class apart from a required interface. + types.insert(0, quote! { class #namespace #name }); + } + } + + if types.is_empty() { + quote! {} + } else { + quote! { : #(#types),* } + } + } + + fn return_type(&self, ty: &metadata::Type) -> TokenStream { + match ty { + metadata::Type::Void => quote! {}, + _ => { + let ty = self.ty(ty); + quote! { -> #ty } + } + } + } + + fn ty(&self, ty: &metadata::Type) -> TokenStream { + match ty { + metadata::Type::Void => quote! { ::core::ffi::c_void }, + metadata::Type::Bool => quote! { bool }, + metadata::Type::Char => quote! { u16 }, + metadata::Type::I8 => quote! { i8 }, + metadata::Type::U8 => quote! { u8 }, + metadata::Type::I16 => quote! { i16 }, + metadata::Type::U16 => quote! { u16 }, + metadata::Type::I32 => quote! { i32 }, + metadata::Type::U32 => quote! { u32 }, + metadata::Type::I64 => quote! { i64 }, + metadata::Type::U64 => quote! { u64 }, + metadata::Type::F32 => quote! { f32 }, + metadata::Type::F64 => quote! { f64 }, + metadata::Type::ISize => quote! { isize }, + metadata::Type::USize => quote! { usize }, + + // TODO: dialect-specific keywords for "well-known types" that don't map to metadata in all cases. + metadata::Type::String => quote! { HSTRING }, + metadata::Type::HRESULT => quote! { HRESULT }, + metadata::Type::GUID => quote! { GUID }, + metadata::Type::IInspectable => quote! { IInspectable }, + metadata::Type::IUnknown => quote! { IUnknown }, + + metadata::Type::TypeDef(def, generics) => { + let namespace = self.namespace(self.reader.type_def_namespace(*def)); + let name = to_ident(self.reader.type_def_name(*def)); + if generics.is_empty() { + quote! { #namespace #name } + } else { + let generics = generics.iter().map(|ty| self.ty(ty)); + quote! { #namespace #name<#(#generics,)*> } + } + } + + metadata::Type::TypeRef(code) => { + let type_name = self.reader.type_def_or_ref(*code); + let namespace = self.namespace(type_name.namespace); + let name = to_ident(type_name.name); + quote! { #namespace #name } + } + + metadata::Type::GenericParam(generic) => self.reader.generic_param_name(*generic).into(), + metadata::Type::WinrtArray(ty) => self.ty(ty), + metadata::Type::WinrtArrayRef(ty) => self.ty(ty), + metadata::Type::ConstRef(ty) => self.ty(ty), + metadata::Type::MutPtr(ty, _pointers) => self.ty(ty), + metadata::Type::ConstPtr(ty, _pointers) => self.ty(ty), + metadata::Type::Win32Array(ty, _len) => self.ty(ty), + // TODO: these types should just be regular metadata type defs + metadata::Type::PSTR => quote! { PSTR }, + metadata::Type::PWSTR => quote! { PWSTR }, + metadata::Type::PCSTR => quote! { PCSTR }, + metadata::Type::PCWSTR => quote! { PCWSTR }, + metadata::Type::BSTR => quote! { BSTR }, + metadata::Type::PrimitiveOrEnum(_, ty) => self.ty(ty), + rest => unimplemented!("{rest:?}"), + } + } + + fn namespace(&self, namespace: &str) -> TokenStream { + // TODO: handle nested structs? + if namespace.is_empty() || self.namespace == namespace { + quote! {} + } else { + // TODO: problem with making relative paths here is that we don't have the context to disambiguate + + // let mut relative = self.namespace.split('.').peekable(); + // let mut namespace = namespace.split('.').peekable(); + // let mut related = false; + + // while relative.peek() == namespace.peek() { + // related = true; + + // if relative.next().is_none() { + // break; + // } + + // namespace.next(); + // } + + // let mut tokens = TokenStream::new(); + + // if related { + // for _ in 0..relative.count() { + // tokens.push_str("super::"); + // } + // } + + // for namespace in namespace { + // tokens.push_str(namespace); + // tokens.push_str("::"); + // } + + // tokens + + // TODO: so instead we just gen it out in full + + let mut tokens = TokenStream::new(); + + for namespace in namespace.split('.') { + tokens.push_str(namespace); + tokens.push_str("::"); + } + + tokens + } + } +} diff --git a/vendor/windows-bindgen/src/rdl/mod.rs b/vendor/windows-bindgen/src/rdl/mod.rs new file mode 100644 index 000000000..6cabb168b --- /dev/null +++ b/vendor/windows-bindgen/src/rdl/mod.rs @@ -0,0 +1,338 @@ +use super::*; +mod fmt; +mod from_reader; +mod to_winmd; +use crate::Result; +pub use from_reader::from_reader; +use syn::spanned::Spanned; + +// TODO: may want to finally get rid of `syn` as it also doesn't support preserving code comments + +impl File { + pub fn parse_str(input: &str) -> Result<Self> { + Ok(syn::parse_str::<Self>(input)?) + } + + // Note: this isn't called automatically by `parse_str` to avoid canonicalizing when we're merely formatting IDL. + pub fn canonicalize(&mut self) -> Result<()> { + // TODO maybe we rewrite the `File` here to resolve any `super` references and use declarations so that + // subsequently the rdl-to-winmd conversion can just assume everything's fully qualified? + // * super can't refer to something outside of the IDL file + // * use declarations are only used for unqualified names that aren't defined in the IDL file + // * use declarations don't support globs and must name all externally defined types + // This way we can quickly kick out common invalid IDL files before we lost file/span context info + + Ok(()) + } + + pub fn fmt(&self) -> String { + fmt::Writer::new(self).into_string() + } + + pub fn into_winmd(mut self) -> Result<Vec<u8>> { + self.canonicalize()?; + to_winmd::rdl_to_winmd(&self) + } +} + +// The value of the IDL-specific memory representation is that it allows for constructs that are not modeled in the abstract Module +// tree such as the use declarations and if we get rid of it we'd always "format" IDL by stripping out any of that into a single +// canonical form which would not be very friendly to developers. +#[derive(Debug)] +pub struct File { + pub winrt: bool, + pub references: Vec<syn::ItemUse>, + pub modules: Vec<Module>, +} + +// TODO: need to change these to unpack the syn types and store strings we can reference for efficiency along with spans since the syn +// is made for value semantics. + +#[derive(Clone, Debug)] +pub struct Module { + pub namespace: String, + pub members: Vec<ModuleMember>, +} + +#[derive(Clone, Debug)] +pub enum ModuleMember { + Module(Module), + Interface(Interface), + Struct(Struct), + Enum(Enum), + Class(Class), + Function(Function), + Constant(Constant), +} + +impl ModuleMember { + pub fn name(&self) -> &str { + match self { + Self::Module(module) => crate::extension(&module.namespace), + Self::Interface(member) => &member.name, + Self::Struct(member) => &member.name, + Self::Enum(member) => &member.name, + Self::Class(member) => &member.name, + Self::Function(member) => &member.name, + Self::Constant(member) => &member.name, + } + } +} + +#[derive(Clone, Debug)] +pub struct Enum { + pub winrt: bool, + pub name: String, + pub item: syn::ItemEnum, +} + +#[derive(Clone, Debug)] +pub struct Constant { + pub name: String, + pub item: syn::ItemConst, +} + +#[derive(Clone, Debug)] +pub struct Struct { + pub winrt: bool, + pub name: String, + pub attributes: Vec<syn::Attribute>, + pub span: proc_macro2::Span, + pub fields: Vec<Field>, +} + +#[derive(Clone, Debug)] +pub struct Field { + pub name: String, + pub attributes: Vec<syn::Attribute>, + pub span: proc_macro2::Span, + pub ty: syn::Type, +} + +#[derive(Clone, Debug)] +pub struct Class { + pub name: String, + pub attributes: Vec<syn::Attribute>, + pub base: Option<syn::TypePath>, + pub extends: Vec<syn::TypePath>, +} + +#[derive(Clone, Debug)] +pub struct Function { + pub name: String, + pub item: syn::TraitItemFn, +} + +#[derive(Clone, Debug)] +pub struct Interface { + pub winrt: bool, + pub name: String, + pub generics: Vec<String>, + pub attributes: Vec<syn::Attribute>, + pub extends: Vec<syn::TypePath>, + pub methods: Vec<syn::TraitItemFn>, +} + +syn::custom_keyword!(interface); +syn::custom_keyword!(class); + +fn winrt(input: syn::parse::ParseStream) -> syn::Result<bool> { + let attributes = input.call(syn::Attribute::parse_inner)?; + if attributes.len() == 1 { + if let syn::Meta::Path(path) = &attributes[0].meta { + if path.is_ident("winrt") { + return Ok(true); + } + + if path.is_ident("win32") { + return Ok(false); + } + } + } + + Err(syn::Error::new(input.span(), "A single `#![win32]` or `#![winrt]` attribute required")) +} + +impl syn::parse::Parse for File { + fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { + let mut references = vec![]; + let mut modules = vec![]; + let winrt = winrt(input)?; + + while !input.is_empty() { + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![mod]) { + modules.push(Module::parse("", winrt, input)?); + } else if lookahead.peek(syn::Token![use]) { + references.push(input.parse()?); + } else { + return Err(lookahead.error()); + } + } + Ok(Self { winrt, references, modules }) + } +} + +impl Module { + fn name(&self) -> &str { + self.namespace.rsplit_once('.').map_or(&self.namespace, |(_, name)| name) + } + + fn parse(namespace: &str, winrt: bool, input: syn::parse::ParseStream) -> syn::Result<Self> { + input.parse::<syn::Token![mod]>()?; + let name = input.parse::<syn::Ident>()?.to_string(); + + let namespace = if namespace.is_empty() { name.to_string() } else { format!("{namespace}.{name}") }; + + let content; + syn::braced!(content in input); + let mut members = vec![]; + while !content.is_empty() { + members.push(ModuleMember::parse(&namespace, winrt, &content)?); + } + Ok(Self { namespace, members }) + } +} + +impl ModuleMember { + fn parse(namespace: &str, winrt: bool, input: syn::parse::ParseStream) -> syn::Result<Self> { + let attributes: Vec<syn::Attribute> = input.call(syn::Attribute::parse_outer)?; + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![mod]) { + if let Some(attribute) = attributes.first() { + return Err(syn::Error::new(attribute.span(), "`use` attributes not supported")); + } + Ok(ModuleMember::Module(Module::parse(namespace, winrt, input)?)) + } else if lookahead.peek(interface) { + Ok(ModuleMember::Interface(Interface::parse(namespace, winrt, attributes, input)?)) + } else if lookahead.peek(syn::Token![struct]) { + Ok(ModuleMember::Struct(Struct::parse(namespace, winrt, attributes, input)?)) + } else if lookahead.peek(syn::Token![enum]) { + Ok(ModuleMember::Enum(Enum::parse(namespace, winrt, attributes, input)?)) + } else if lookahead.peek(class) { + Ok(ModuleMember::Class(Class::parse(attributes, input)?)) + } else if lookahead.peek(syn::Token![fn]) { + Ok(ModuleMember::Function(Function::parse(namespace, attributes, input)?)) + } else if lookahead.peek(syn::Token![const]) { + Ok(ModuleMember::Constant(Constant::parse(namespace, attributes, input)?)) + } else { + Err(lookahead.error()) + } + } +} + +impl Class { + fn parse(attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { + input.parse::<class>()?; + let name = input.parse::<syn::Ident>()?.to_string(); + let mut extends = Vec::new(); + let mut base = None; + + if input.peek(syn::Token![:]) { + input.parse::<syn::Token![:]>()?; + while !input.peek(syn::Token![;]) { + if input.peek(class) { + input.parse::<class>()?; + base = Some(input.parse()?); + } else { + extends.push(input.parse()?); + } + _ = input.parse::<syn::Token![,]>(); + } + } + + input.parse::<syn::Token![;]>()?; + Ok(Self { attributes, name, base, extends }) + } +} + +impl Interface { + fn parse(_namespace: &str, winrt: bool, attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { + input.parse::<interface>()?; + let name = input.parse::<syn::Ident>()?.to_string(); + + let mut generics = Vec::new(); + + if input.peek(syn::Token![<]) { + input.parse::<syn::Token![<]>()?; + while input.peek(syn::Ident) { + generics.push(input.parse::<syn::Ident>()?.to_string()); + _ = input.parse::<syn::Token![,]>(); + } + + input.parse::<syn::Token![>]>()?; + } + + let mut extends = Vec::new(); + + if input.peek(syn::Token![:]) { + input.parse::<syn::Token![:]>()?; + while !input.peek(syn::token::Brace) { + extends.push(input.parse()?); + _ = input.parse::<syn::Token![,]>(); + } + } + + let content; + syn::braced!(content in input); + let mut methods = vec![]; + while !content.is_empty() { + methods.push(content.parse()?); + } + Ok(Self { winrt, attributes, generics, extends, name, methods }) + } +} + +impl Struct { + fn parse(_namespace: &str, winrt: bool, attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { + // TODO: need to validate that the struct is valid according to the constraints of the winmd type system. + // Same for the other types. That way we can spit out errors quickly for things like unnamed fields. + let span = input.span(); + let item: syn::ItemStruct = input.parse()?; + let name = item.ident.to_string(); + let mut fields = vec![]; + + let syn::Fields::Named(named) = item.fields else { + return Err(syn::Error::new(item.span(), "unnamed fields not supported")); + }; + + for field in named.named { + fields.push(Field { + span: field.span(), + attributes: field.attrs, + // Simply unwrapping since we already know that it is a named field. + name: field.ident.unwrap().to_string(), + ty: field.ty, + }); + } + + Ok(Self { winrt, name, attributes, span, fields }) + } +} + +impl Enum { + fn parse(_namespace: &str, winrt: bool, attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { + let mut item: syn::ItemEnum = input.parse()?; + item.attrs = attributes; + let name = item.ident.to_string(); + Ok(Self { winrt, name, item }) + } +} + +impl Constant { + fn parse(_namespace: &str, attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { + let mut item: syn::ItemConst = input.parse()?; + item.attrs = attributes; + let name = item.ident.to_string(); + Ok(Self { name, item }) + } +} + +impl Function { + fn parse(_namespace: &str, attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { + let mut item: syn::TraitItemFn = input.parse()?; + item.attrs = attributes; + let name = item.sig.ident.to_string(); + Ok(Self { name, item }) + } +} diff --git a/vendor/windows-bindgen/src/rdl/to_winmd.rs b/vendor/windows-bindgen/src/rdl/to_winmd.rs new file mode 100644 index 000000000..d1dc65b12 --- /dev/null +++ b/vendor/windows-bindgen/src/rdl/to_winmd.rs @@ -0,0 +1,335 @@ +use super::*; +use crate::winmd::{self, writer}; +use crate::{rdl, Result}; + +// TODO: store span in winmd so that errors resolving type references can be traced back to file/line/column +use std::collections::HashMap; +//use syn::spanned::Spanned; + +// TODO: this creates a temporary in-memory winmd used to treat the IDL content uniformly as metadata. +// The winmd_to_winmd does the harder job of validating and producing canonical winmd for public consumption. + +pub fn rdl_to_winmd(file: &rdl::File) -> Result<Vec<u8>> { + // Local-to-qualified type names found in use declaration - e.g. "IStringable" -> "Windows.Foundation.IStringable" + // This is just a convenience for the developer to shorten common references like this but would not support globs or renames. + // Note that none of these are verified to be real until much later when the winmd is validated since we don't + // know what other metadata may be combined + let mut _use_map = HashMap::<String, String>::new(); + + // TODO: read file and populate use_map + + // Types are collected here in two passes - this allows us to figure out whether a local name points to a relative type + // or a type from a use declaration...? + let mut collector = HashMap::<String, HashMap<&str, rdl::ModuleMember>>::new(); + + file.modules.iter().for_each(|module| collect_module(&mut collector, module)); + + // TODO: collect type names into hashmap (phase 1) and just drop clones of the IDL members into the collector + + // TODO: Can we just walk the collector at this point and populate the winmd writer and thus need the read-phase? + // this second walking of the collector is basically the "define" phase + + let mut writer = winmd::Writer::new("temp.winmd"); + + collector.iter().for_each(|(namespace, members)| members.iter().for_each(|(name, member)| write_member(&mut writer, namespace, name, member))); + + Ok(writer.into_stream()) +} + +fn collect_module<'a>(collector: &mut HashMap<String, HashMap<&'a str, rdl::ModuleMember>>, module: &'a rdl::Module) { + module.members.iter().for_each(|member| collect_member(collector, module, member)); +} + +fn collect_member<'a>(collector: &mut HashMap<String, HashMap<&'a str, rdl::ModuleMember>>, module: &'a rdl::Module, member: &'a rdl::ModuleMember) { + match member { + rdl::ModuleMember::Module(module) => collect_module(collector, module), + rdl::ModuleMember::Constant(_) | rdl::ModuleMember::Function(_) => { + collector.entry(module.namespace.to_string()).or_default().entry("Apis").or_insert(member.clone()); + } + _ => { + collector.entry(module.namespace.to_string()).or_default().entry(member.name()).or_insert(member.clone()); + } + } +} + +fn write_member(writer: &mut winmd::Writer, namespace: &str, name: &str, member: &rdl::ModuleMember) { + match member { + rdl::ModuleMember::Interface(member) => write_interface(writer, namespace, name, member), + rdl::ModuleMember::Struct(member) => write_struct(writer, namespace, name, member), + rdl::ModuleMember::Enum(member) => write_enum(writer, namespace, name, member), + rdl::ModuleMember::Class(member) => write_class(writer, namespace, name, member), + rest => unimplemented!("{rest:?}"), + } +} + +fn write_interface(writer: &mut winmd::Writer, namespace: &str, name: &str, member: &rdl::Interface) { + let mut flags = metadata::TypeAttributes::Public | metadata::TypeAttributes::Interface | metadata::TypeAttributes::Abstract; + + if member.winrt { + flags |= metadata::TypeAttributes::WindowsRuntime + } + + writer.tables.TypeDef.push(winmd::TypeDef { + Extends: 0, + FieldList: writer.tables.Field.len() as u32, + MethodList: writer.tables.MethodDef.len() as u32, + Flags: flags.0, + TypeName: writer.strings.insert(name), + TypeNamespace: writer.strings.insert(namespace), + }); + + for (number, generic) in member.generics.iter().enumerate() { + writer.tables.GenericParam.push(writer::GenericParam { + Number: number as u16, + Flags: 0, + Owner: writer::TypeOrMethodDef::TypeDef(writer.tables.TypeDef.len() as u32 - 1).encode(), + Name: writer.strings.insert(generic), + }); + } + + for type_path in &member.extends { + let ty = syn_type_path(namespace, &member.generics, type_path); + + let reference = match &ty { + winmd::Type::TypeRef(type_name) if type_name.generics.is_empty() => writer.insert_type_ref(&type_name.namespace, &type_name.name), + winmd::Type::TypeRef(_) => writer.insert_type_spec(ty), + rest => unimplemented!("{rest:?}"), + }; + + writer.tables.InterfaceImpl.push(writer::InterfaceImpl { Class: writer.tables.TypeDef.len() as u32 - 1, Interface: reference }); + } + + for method in &member.methods { + let signature = syn_signature(namespace, &member.generics, &method.sig); + + let params: Vec<winmd::Type> = signature.params.iter().map(|param| param.ty.clone()).collect(); + + let signature_blob = writer.insert_method_sig(metadata::MethodCallAttributes(0), &signature.return_type, ¶ms); + + let flags = metadata::MethodAttributes::Abstract | metadata::MethodAttributes::HideBySig | metadata::MethodAttributes::HideBySig | metadata::MethodAttributes::NewSlot | metadata::MethodAttributes::Public | metadata::MethodAttributes::Virtual; + + writer.tables.MethodDef.push(winmd::MethodDef { + RVA: 0, + ImplFlags: 0, + Flags: flags.0, + Name: writer.strings.insert(&method.sig.ident.to_string()), + Signature: signature_blob, + ParamList: writer.tables.Param.len() as u32, + }); + + for (sequence, param) in signature.params.iter().enumerate() { + writer.tables.Param.push(winmd::Param { Flags: 0, Sequence: (sequence + 1) as u16, Name: writer.strings.insert(¶m.name) }); + } + } +} + +fn write_struct(writer: &mut winmd::Writer, namespace: &str, name: &str, member: &rdl::Struct) { + let mut flags = metadata::TypeAttributes::Public | metadata::TypeAttributes::Sealed | metadata::TypeAttributes::SequentialLayout; + + if member.winrt { + flags |= metadata::TypeAttributes::WindowsRuntime + } + + let extends = writer.insert_type_ref("System", "ValueType"); + + writer.tables.TypeDef.push(winmd::TypeDef { + Extends: extends, + FieldList: writer.tables.Field.len() as u32, + MethodList: writer.tables.MethodDef.len() as u32, + Flags: flags.0, + TypeName: writer.strings.insert(name), + TypeNamespace: writer.strings.insert(namespace), + }); + + for field in &member.fields { + let flags = metadata::FieldAttributes::Public; + let ty = syn_type(namespace, &[], &field.ty); + let signature = writer.insert_field_sig(&ty); + + writer.tables.Field.push(winmd::Field { Flags: flags.0, Name: writer.strings.insert(&field.name), Signature: signature }); + } +} + +fn write_enum(_writer: &mut winmd::Writer, _namespace: &str, _name: &str, _member: &rdl::Enum) {} + +fn write_class(writer: &mut winmd::Writer, namespace: &str, name: &str, member: &rdl::Class) { + let flags = metadata::TypeAttributes::Public | metadata::TypeAttributes::Sealed | metadata::TypeAttributes::WindowsRuntime; + + let extends = if let Some(base) = &member.base { + match syn_type_path(namespace, &[], base) { + winmd::Type::TypeRef(base) => writer.insert_type_ref(&base.namespace, &base.name), + rest => unimplemented!("{rest:?}"), + } + } else { + writer.insert_type_ref("System", "Object") + }; + + writer.tables.TypeDef.push(winmd::TypeDef { + Extends: extends, + // Even though ECMA-335 says these can be "null", bugs in ILDASM necessitate this to avoid "misreading" the list terminators. + FieldList: writer.tables.Field.len() as u32, + MethodList: writer.tables.MethodDef.len() as u32, + Flags: flags.0, + TypeName: writer.strings.insert(name), + TypeNamespace: writer.strings.insert(namespace), + }); + + for (index, extends) in member.extends.iter().enumerate() { + let ty = syn_type_path(namespace, &[], extends); + + let reference = match &ty { + winmd::Type::TypeRef(type_name) if type_name.generics.is_empty() => writer.insert_type_ref(&type_name.namespace, &type_name.name), + winmd::Type::TypeRef(_) => writer.insert_type_spec(ty), + winmd::Type::IUnknown => writer.insert_type_ref("Windows.Win32.System.Com", "IUnknown"), + winmd::Type::IInspectable => writer.insert_type_ref("Windows.Win32.System.WinRT", "IInspectable"), + rest => unimplemented!("{rest:?}"), + }; + + writer.tables.InterfaceImpl.push(writer::InterfaceImpl { Class: writer.tables.TypeDef.len() as u32 - 1, Interface: reference }); + + if index == 0 { + // TODO: add the DefaultAttribute to the first interface + } + } +} + +fn syn_signature(namespace: &str, generics: &[String], sig: &syn::Signature) -> winmd::Signature { + let params = sig + .inputs + .iter() + .map(|param| match param { + syn::FnArg::Typed(pat_type) => { + let name = match &*pat_type.pat { + syn::Pat::Ident(pat_ident) => pat_ident.ident.to_string(), + rest => unimplemented!("{rest:?}"), + }; + let ty = syn_type(namespace, generics, &pat_type.ty); + winmd::SignatureParam { name, ty } + } + rest => unimplemented!("{rest:?}"), + }) + .collect(); + + let return_type = if let syn::ReturnType::Type(_, ty) = &sig.output { syn_type(namespace, generics, ty) } else { winmd::Type::Void }; + + winmd::Signature { params, return_type, call_flags: 0 } +} + +fn syn_type(namespace: &str, generics: &[String], ty: &syn::Type) -> winmd::Type { + match ty { + syn::Type::Path(ty) => syn_type_path(namespace, generics, ty), + syn::Type::Ptr(ptr) => syn_type_ptr(namespace, ptr), + syn::Type::Array(array) => syn_type_array(namespace, array), + rest => unimplemented!("{rest:?}"), + } +} + +fn syn_type_array(namespace: &str, array: &syn::TypeArray) -> winmd::Type { + let ty = syn_type(namespace, &[], &array.elem); + + if let syn::Expr::Lit(lit) = &array.len { + if let syn::Lit::Int(lit) = &lit.lit { + if let Ok(len) = lit.base10_parse() { + return ty.into_array(len); + } + } + } + + unimplemented!() +} + +fn syn_type_ptr(namespace: &str, ptr: &syn::TypePtr) -> winmd::Type { + let ty = syn_type(namespace, &[], &ptr.elem); + if ptr.mutability.is_some() { + ty.into_mut_ptr() + } else { + ty.into_const_ptr() + } +} + +fn syn_type_path(namespace: &str, generics: &[String], ty: &syn::TypePath) -> winmd::Type { + if ty.qself.is_none() { + return syn_path(namespace, generics, &ty.path); + } + + unimplemented!() +} + +fn syn_path(namespace: &str, generics: &[String], path: &syn::Path) -> winmd::Type { + if let Some(segment) = path.segments.first() { + if path.segments.len() == 1 && segment.arguments.is_empty() { + let name = segment.ident.to_string(); + + if let Some(number) = generics.iter().position(|generic| generic == &name) { + return winmd::Type::GenericParam(number as u16); + } + + match name.as_str() { + "void" => return winmd::Type::Void, + "bool" => return winmd::Type::Bool, + "char" => return winmd::Type::Char, + "i8" => return winmd::Type::I8, + "u8" => return winmd::Type::U8, + "i16" => return winmd::Type::I16, + "u16" => return winmd::Type::U16, + "i32" => return winmd::Type::I32, + "u32" => return winmd::Type::U32, + "i64" => return winmd::Type::I64, + "u64" => return winmd::Type::U64, + "f32" => return winmd::Type::F32, + "f64" => return winmd::Type::F64, + "isize" => return winmd::Type::ISize, + "usize" => return winmd::Type::USize, + "HSTRING" => return winmd::Type::String, + "GUID" => return winmd::Type::GUID, + "IUnknown" => return winmd::Type::IUnknown, + "IInspectable" => return winmd::Type::IInspectable, + "HRESULT" => return winmd::Type::HRESULT, + "PSTR" => return winmd::Type::PSTR, + "PWSTR" => return winmd::Type::PWSTR, + "PCSTR" => return winmd::Type::PCSTR, + "PCWSTR" => return winmd::Type::PCWSTR, + "BSTR" => return winmd::Type::BSTR, + _ => {} + }; + } + } + + // TODO: Here we assume that paths are absolute since there's no way to disambiguate between nested and absolute paths + // The canonicalize function (should maybe) preprocesses the IDL to make this work + + let mut builder = vec![]; + + for segment in &path.segments { + let segment = segment.ident.to_string(); + + if segment == "super" { + if builder.is_empty() { + for segment in namespace.split('.') { + builder.push(segment.to_string()); + } + } + builder.pop(); + } else { + builder.push(segment); + } + } + + // Unwrapping is fine as there should always be at least one segment. + let (name, type_namespace) = builder.split_last().unwrap(); + let type_namespace = if type_namespace.is_empty() { namespace.to_string() } else { type_namespace.join(".") }; + let mut type_generics = vec![]; + + if let Some(segment) = path.segments.last() { + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + for arg in &args.args { + match arg { + syn::GenericArgument::Type(ty) => type_generics.push(syn_type(namespace, generics, ty)), + rest => unimplemented!("{rest:?}"), + } + } + } + } + + winmd::Type::TypeRef(winmd::TypeName { namespace: type_namespace, name: name.to_string(), generics: type_generics }) +} |