1 | use super::*; |
2 | use crate::winmd::{self, writer}; |
3 | use crate::{rdl, Result}; |
4 | |
5 | // TODO: store span in winmd so that errors resolving type references can be traced back to file/line/column |
6 | use std::collections::HashMap; |
7 | //use syn::spanned::Spanned; |
8 | |
9 | // TODO: this creates a temporary in-memory winmd used to treat the IDL content uniformly as metadata. |
10 | // The winmd_to_winmd does the harder job of validating and producing canonical winmd for public consumption. |
11 | |
12 | pub fn rdl_to_winmd(file: &rdl::File) -> Result<Vec<u8>> { |
13 | // Local-to-qualified type names found in use declaration - e.g. "IStringable" -> "Windows.Foundation.IStringable" |
14 | // This is just a convenience for the developer to shorten common references like this but would not support globs or renames. |
15 | // Note that none of these are verified to be real until much later when the winmd is validated since we don't |
16 | // know what other metadata may be combined |
17 | let mut _use_map = HashMap::<String, String>::new(); |
18 | |
19 | // TODO: read file and populate use_map |
20 | |
21 | // Types are collected here in two passes - this allows us to figure out whether a local name points to a relative type |
22 | // or a type from a use declaration...? |
23 | let mut collector = HashMap::<String, HashMap<&str, rdl::ModuleMember>>::new(); |
24 | |
25 | file.modules.iter().for_each(|module| collect_module(&mut collector, module)); |
26 | |
27 | // TODO: collect type names into hashmap (phase 1) and just drop clones of the IDL members into the collector |
28 | |
29 | // TODO: Can we just walk the collector at this point and populate the winmd writer and thus need the read-phase? |
30 | // this second walking of the collector is basically the "define" phase |
31 | |
32 | let mut writer = winmd::Writer::new("temp.winmd" ); |
33 | |
34 | collector.iter().for_each(|(namespace, members)| members.iter().for_each(|(name, member)| write_member(&mut writer, namespace, name, member))); |
35 | |
36 | Ok(writer.into_stream()) |
37 | } |
38 | |
39 | fn collect_module<'a>(collector: &mut HashMap<String, HashMap<&'a str, rdl::ModuleMember>>, module: &'a rdl::Module) { |
40 | module.members.iter().for_each(|member: &ModuleMember| collect_member(collector, module, member)); |
41 | } |
42 | |
43 | fn collect_member<'a>(collector: &mut HashMap<String, HashMap<&'a str, rdl::ModuleMember>>, module: &'a rdl::Module, member: &'a rdl::ModuleMember) { |
44 | match member { |
45 | rdl::ModuleMember::Module(module: &Module) => collect_module(collector, module), |
46 | rdl::ModuleMember::Constant(_) | rdl::ModuleMember::Function(_) => { |
47 | collector.entry(module.namespace.to_string()).or_default().entry("Apis" ).or_insert(default:member.clone()); |
48 | } |
49 | _ => { |
50 | collector.entry(module.namespace.to_string()).or_default().entry(member.name()).or_insert(default:member.clone()); |
51 | } |
52 | } |
53 | } |
54 | |
55 | fn write_member(writer: &mut winmd::Writer, namespace: &str, name: &str, member: &rdl::ModuleMember) { |
56 | match member { |
57 | rdl::ModuleMember::Interface(member: &Interface) => write_interface(writer, namespace, name, member), |
58 | rdl::ModuleMember::Struct(member: &Struct) => write_struct(writer, namespace, name, member), |
59 | rdl::ModuleMember::Enum(member: &Enum) => write_enum(writer, namespace, name, member), |
60 | rdl::ModuleMember::Class(member: &Class) => write_class(writer, namespace, name, member), |
61 | rest: &ModuleMember => unimplemented!(" {rest:?}" ), |
62 | } |
63 | } |
64 | |
65 | fn write_interface(writer: &mut winmd::Writer, namespace: &str, name: &str, member: &rdl::Interface) { |
66 | let mut flags = metadata::TypeAttributes::Public | metadata::TypeAttributes::Interface | metadata::TypeAttributes::Abstract; |
67 | |
68 | if member.winrt { |
69 | flags |= metadata::TypeAttributes::WindowsRuntime |
70 | } |
71 | |
72 | writer.tables.TypeDef.push(winmd::TypeDef { |
73 | Extends: 0, |
74 | FieldList: writer.tables.Field.len() as u32, |
75 | MethodList: writer.tables.MethodDef.len() as u32, |
76 | Flags: flags.0, |
77 | TypeName: writer.strings.insert(name), |
78 | TypeNamespace: writer.strings.insert(namespace), |
79 | }); |
80 | |
81 | for (number, generic) in member.generics.iter().enumerate() { |
82 | writer.tables.GenericParam.push(writer::GenericParam { |
83 | Number: number as u16, |
84 | Flags: 0, |
85 | Owner: writer::TypeOrMethodDef::TypeDef(writer.tables.TypeDef.len() as u32 - 1).encode(), |
86 | Name: writer.strings.insert(generic), |
87 | }); |
88 | } |
89 | |
90 | for type_path in &member.extends { |
91 | let ty = syn_type_path(namespace, &member.generics, type_path); |
92 | |
93 | let reference = match &ty { |
94 | winmd::Type::TypeRef(type_name) if type_name.generics.is_empty() => writer.insert_type_ref(&type_name.namespace, &type_name.name), |
95 | winmd::Type::TypeRef(_) => writer.insert_type_spec(ty), |
96 | rest => unimplemented!(" {rest:?}" ), |
97 | }; |
98 | |
99 | writer.tables.InterfaceImpl.push(writer::InterfaceImpl { Class: writer.tables.TypeDef.len() as u32 - 1, Interface: reference }); |
100 | } |
101 | |
102 | for method in &member.methods { |
103 | let signature = syn_signature(namespace, &member.generics, &method.sig); |
104 | |
105 | let params: Vec<winmd::Type> = signature.params.iter().map(|param| param.ty.clone()).collect(); |
106 | |
107 | let signature_blob = writer.insert_method_sig(metadata::MethodCallAttributes(0), &signature.return_type, ¶ms); |
108 | |
109 | let flags = metadata::MethodAttributes::Abstract | metadata::MethodAttributes::HideBySig | metadata::MethodAttributes::HideBySig | metadata::MethodAttributes::NewSlot | metadata::MethodAttributes::Public | metadata::MethodAttributes::Virtual; |
110 | |
111 | writer.tables.MethodDef.push(winmd::MethodDef { |
112 | RVA: 0, |
113 | ImplFlags: 0, |
114 | Flags: flags.0, |
115 | Name: writer.strings.insert(&method.sig.ident.to_string()), |
116 | Signature: signature_blob, |
117 | ParamList: writer.tables.Param.len() as u32, |
118 | }); |
119 | |
120 | for (sequence, param) in signature.params.iter().enumerate() { |
121 | writer.tables.Param.push(winmd::Param { Flags: 0, Sequence: (sequence + 1) as u16, Name: writer.strings.insert(¶m.name) }); |
122 | } |
123 | } |
124 | } |
125 | |
126 | fn write_struct(writer: &mut winmd::Writer, namespace: &str, name: &str, member: &rdl::Struct) { |
127 | let mut flags = metadata::TypeAttributes::Public | metadata::TypeAttributes::Sealed | metadata::TypeAttributes::SequentialLayout; |
128 | |
129 | if member.winrt { |
130 | flags |= metadata::TypeAttributes::WindowsRuntime |
131 | } |
132 | |
133 | let extends = writer.insert_type_ref("System" , "ValueType" ); |
134 | |
135 | writer.tables.TypeDef.push(winmd::TypeDef { |
136 | Extends: extends, |
137 | FieldList: writer.tables.Field.len() as u32, |
138 | MethodList: writer.tables.MethodDef.len() as u32, |
139 | Flags: flags.0, |
140 | TypeName: writer.strings.insert(name), |
141 | TypeNamespace: writer.strings.insert(namespace), |
142 | }); |
143 | |
144 | for field in &member.fields { |
145 | let flags = metadata::FieldAttributes::Public; |
146 | let ty = syn_type(namespace, &[], &field.ty); |
147 | let signature = writer.insert_field_sig(&ty); |
148 | |
149 | writer.tables.Field.push(winmd::Field { Flags: flags.0, Name: writer.strings.insert(&field.name), Signature: signature }); |
150 | } |
151 | } |
152 | |
153 | fn write_enum(_writer: &mut winmd::Writer, _namespace: &str, _name: &str, _member: &rdl::Enum) {} |
154 | |
155 | fn write_class(writer: &mut winmd::Writer, namespace: &str, name: &str, member: &rdl::Class) { |
156 | let flags = metadata::TypeAttributes::Public | metadata::TypeAttributes::Sealed | metadata::TypeAttributes::WindowsRuntime; |
157 | |
158 | let extends = if let Some(base) = &member.base { |
159 | match syn_type_path(namespace, &[], base) { |
160 | winmd::Type::TypeRef(base) => writer.insert_type_ref(&base.namespace, &base.name), |
161 | rest => unimplemented!(" {rest:?}" ), |
162 | } |
163 | } else { |
164 | writer.insert_type_ref("System" , "Object" ) |
165 | }; |
166 | |
167 | writer.tables.TypeDef.push(winmd::TypeDef { |
168 | Extends: extends, |
169 | // Even though ECMA-335 says these can be "null", bugs in ILDASM necessitate this to avoid "misreading" the list terminators. |
170 | FieldList: writer.tables.Field.len() as u32, |
171 | MethodList: writer.tables.MethodDef.len() as u32, |
172 | Flags: flags.0, |
173 | TypeName: writer.strings.insert(name), |
174 | TypeNamespace: writer.strings.insert(namespace), |
175 | }); |
176 | |
177 | for (index, extends) in member.extends.iter().enumerate() { |
178 | let ty = syn_type_path(namespace, &[], extends); |
179 | |
180 | let reference = match &ty { |
181 | winmd::Type::TypeRef(type_name) if type_name.generics.is_empty() => writer.insert_type_ref(&type_name.namespace, &type_name.name), |
182 | winmd::Type::TypeRef(_) => writer.insert_type_spec(ty), |
183 | winmd::Type::IUnknown => writer.insert_type_ref("Windows.Win32.System.Com" , "IUnknown" ), |
184 | winmd::Type::IInspectable => writer.insert_type_ref("Windows.Win32.System.WinRT" , "IInspectable" ), |
185 | rest => unimplemented!(" {rest:?}" ), |
186 | }; |
187 | |
188 | writer.tables.InterfaceImpl.push(writer::InterfaceImpl { Class: writer.tables.TypeDef.len() as u32 - 1, Interface: reference }); |
189 | |
190 | if index == 0 { |
191 | // TODO: add the DefaultAttribute to the first interface |
192 | } |
193 | } |
194 | } |
195 | |
196 | fn syn_signature(namespace: &str, generics: &[String], sig: &syn::Signature) -> winmd::Signature { |
197 | let params: Vec = sigimpl Iterator |
198 | .inputs |
199 | .iter() |
200 | .map(|param: &FnArg| match param { |
201 | syn::FnArg::Typed(pat_type: &PatType) => { |
202 | let name: String = match &*pat_type.pat { |
203 | syn::Pat::Ident(pat_ident: &PatIdent) => pat_ident.ident.to_string(), |
204 | rest: &Pat => unimplemented!(" {rest:?}" ), |
205 | }; |
206 | let ty: Type = syn_type(namespace, generics, &pat_type.ty); |
207 | winmd::SignatureParam { name, ty } |
208 | } |
209 | rest: &FnArg => unimplemented!(" {rest:?}" ), |
210 | }) |
211 | .collect(); |
212 | |
213 | let return_type: Type = if let syn::ReturnType::Type(_, ty: &Box) = &sig.output { syn_type(namespace, generics, ty) } else { winmd::Type::Void }; |
214 | |
215 | winmd::Signature { params, return_type, call_flags: 0 } |
216 | } |
217 | |
218 | fn syn_type(namespace: &str, generics: &[String], ty: &syn::Type) -> winmd::Type { |
219 | match ty { |
220 | syn::Type::Path(ty: &TypePath) => syn_type_path(namespace, generics, ty), |
221 | syn::Type::Ptr(ptr: &TypePtr) => syn_type_ptr(namespace, ptr), |
222 | syn::Type::Array(array: &TypeArray) => syn_type_array(namespace, array), |
223 | rest: &Type => unimplemented!(" {rest:?}" ), |
224 | } |
225 | } |
226 | |
227 | fn syn_type_array(namespace: &str, array: &syn::TypeArray) -> winmd::Type { |
228 | let ty: Type = syn_type(namespace, &[], &array.elem); |
229 | |
230 | if let syn::Expr::Lit(lit: &ExprLit) = &array.len { |
231 | if let syn::Lit::Int(lit: &LitInt) = &lit.lit { |
232 | if let Ok(len: usize) = lit.base10_parse() { |
233 | return ty.into_array(len); |
234 | } |
235 | } |
236 | } |
237 | |
238 | unimplemented!() |
239 | } |
240 | |
241 | fn syn_type_ptr(namespace: &str, ptr: &syn::TypePtr) -> winmd::Type { |
242 | let ty: Type = syn_type(namespace, &[], &ptr.elem); |
243 | if ptr.mutability.is_some() { |
244 | ty.into_mut_ptr() |
245 | } else { |
246 | ty.into_const_ptr() |
247 | } |
248 | } |
249 | |
250 | fn syn_type_path(namespace: &str, generics: &[String], ty: &syn::TypePath) -> winmd::Type { |
251 | if ty.qself.is_none() { |
252 | return syn_path(namespace, generics, &ty.path); |
253 | } |
254 | |
255 | unimplemented!() |
256 | } |
257 | |
258 | fn syn_path(namespace: &str, generics: &[String], path: &syn::Path) -> winmd::Type { |
259 | if let Some(segment) = path.segments.first() { |
260 | if path.segments.len() == 1 && segment.arguments.is_empty() { |
261 | let name = segment.ident.to_string(); |
262 | |
263 | if let Some(number) = generics.iter().position(|generic| generic == &name) { |
264 | return winmd::Type::GenericParam(number as u16); |
265 | } |
266 | |
267 | match name.as_str() { |
268 | "void" => return winmd::Type::Void, |
269 | "bool" => return winmd::Type::Bool, |
270 | "char" => return winmd::Type::Char, |
271 | "i8" => return winmd::Type::I8, |
272 | "u8" => return winmd::Type::U8, |
273 | "i16" => return winmd::Type::I16, |
274 | "u16" => return winmd::Type::U16, |
275 | "i32" => return winmd::Type::I32, |
276 | "u32" => return winmd::Type::U32, |
277 | "i64" => return winmd::Type::I64, |
278 | "u64" => return winmd::Type::U64, |
279 | "f32" => return winmd::Type::F32, |
280 | "f64" => return winmd::Type::F64, |
281 | "isize" => return winmd::Type::ISize, |
282 | "usize" => return winmd::Type::USize, |
283 | "HSTRING" => return winmd::Type::String, |
284 | "GUID" => return winmd::Type::GUID, |
285 | "IUnknown" => return winmd::Type::IUnknown, |
286 | "IInspectable" => return winmd::Type::IInspectable, |
287 | "HRESULT" => return winmd::Type::HRESULT, |
288 | "PSTR" => return winmd::Type::PSTR, |
289 | "PWSTR" => return winmd::Type::PWSTR, |
290 | "PCSTR" => return winmd::Type::PCSTR, |
291 | "PCWSTR" => return winmd::Type::PCWSTR, |
292 | "BSTR" => return winmd::Type::BSTR, |
293 | _ => {} |
294 | }; |
295 | } |
296 | } |
297 | |
298 | // TODO: Here we assume that paths are absolute since there's no way to disambiguate between nested and absolute paths |
299 | // The canonicalize function (should maybe) preprocesses the IDL to make this work |
300 | |
301 | let mut builder = vec![]; |
302 | |
303 | for segment in &path.segments { |
304 | let segment = segment.ident.to_string(); |
305 | |
306 | if segment == "super" { |
307 | if builder.is_empty() { |
308 | for segment in namespace.split('.' ) { |
309 | builder.push(segment.to_string()); |
310 | } |
311 | } |
312 | builder.pop(); |
313 | } else { |
314 | builder.push(segment); |
315 | } |
316 | } |
317 | |
318 | // Unwrapping is fine as there should always be at least one segment. |
319 | let (name, type_namespace) = builder.split_last().unwrap(); |
320 | let type_namespace = if type_namespace.is_empty() { namespace.to_string() } else { type_namespace.join("." ) }; |
321 | let mut type_generics = vec![]; |
322 | |
323 | if let Some(segment) = path.segments.last() { |
324 | if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { |
325 | for arg in &args.args { |
326 | match arg { |
327 | syn::GenericArgument::Type(ty) => type_generics.push(syn_type(namespace, generics, ty)), |
328 | rest => unimplemented!(" {rest:?}" ), |
329 | } |
330 | } |
331 | } |
332 | } |
333 | |
334 | winmd::Type::TypeRef(winmd::TypeName { namespace: type_namespace, name: name.to_string(), generics: type_generics }) |
335 | } |
336 | |