1use super::*;
2use crate::winmd::{self, writer};
3use crate::{rdl, Result};
4
5// TODO: store span in winmd so that errors resolving type references can be traced back to file/line/column
6use 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
12pub 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
39fn 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
43fn 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
55fn 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
65fn 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, &params);
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(&param.name) });
122 }
123 }
124}
125
126fn 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
153fn write_enum(_writer: &mut winmd::Writer, _namespace: &str, _name: &str, _member: &rdl::Enum) {}
154
155fn 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
196fn 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
218fn 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
227fn 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
241fn 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
250fn 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
258fn 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