nuidl_lib/codegen/
c.rs

1use std::fmt::Display;
2use std::io;
3use std::io::Write;
4use std::path::Path;
5
6use indoc::indoc;
7use uuid::Uuid;
8
9use crate::codegen::{Ctype, File, Function, Interface, InterfaceDefn, Toplevel};
10
11pub fn write_cguid<W: Write>(mut w: W, f: &File, source: &Path) -> io::Result<()> {
12    writeln!(w, "// Auto-generated by nuidl from {}.", source.display())?;
13    writeln!(w, "#include <nucom/idldefs.h>")?;
14    writeln!(w)?;
15
16    for el in &f.elems {
17        if let Toplevel::Interface(itf) = el {
18            if let Some(InterfaceDefn {
19                uuid: Some(uuid), ..
20            }) = itf.defn
21            {
22                writeln!(w, "const GUID IID_{} = {};", itf.name, GuidRepr(uuid))?;
23            }
24        }
25    }
26
27    Ok(())
28}
29
30pub fn write_header<W: Write>(
31    mut w: W,
32    f: &File,
33    source: &Path,
34    guard_name: &str,
35) -> io::Result<()> {
36    writeln!(
37        w,
38        indoc! {r#"
39            // Auto-generated by nuidl from {}.
40            #ifndef {1}
41            #define {1}
42
43            #include <nucom/idldefs.h>
44        "#},
45        source.display(),
46        guard_name,
47    )?;
48
49    for el in &f.imports {
50        writeln!(w, "#include \"{}\"", el.c_name.display())?;
51    }
52
53    for el in &f.elems {
54        if let Toplevel::Interface(itf) = el {
55            writeln!(w, "typedef struct {0} {0};", itf.name)?;
56        }
57    }
58
59    for el in &f.elems {
60        match el {
61            Toplevel::CppQuote(text) => {
62                writeln!(w, "{}", text)?;
63            }
64            Toplevel::Interface(i) => {
65                print_interface(&mut w, i)?;
66            }
67        }
68    }
69
70    writeln!(
71        w,
72        indoc! {r#"
73            #endif // {}
74        "#},
75        guard_name
76    )?;
77
78    Ok(())
79}
80
81fn write_type_space<W: Write>(t: &Ctype, mut w: W, with_space: bool) -> io::Result<()> {
82    write!(w, "{}", t.typename)?;
83
84    if t.is_const {
85        write!(w, " const")?;
86    }
87
88    let mut next_space = true;
89
90    for ptr in &t.indirection {
91        if next_space {
92            write!(w, " ")?;
93        }
94
95        write!(w, "*")?;
96
97        if ptr.is_const {
98            write!(w, "const")?;
99        }
100
101        next_space = ptr.is_const;
102    }
103
104    if next_space && with_space {
105        write!(w, " ")?;
106    }
107
108    Ok(())
109}
110
111fn write_type<W: Write>(t: &Ctype, w: W) -> io::Result<()> {
112    write_type_space(t, w, true)
113}
114
115struct GuidRepr(Uuid);
116
117impl Display for GuidRepr {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        let (d1, d2, d3, d4) = self.0.as_fields();
120
121        write!(f, "{{0x{:X},", d1)?;
122        write!(f, "0x{:X},", d2)?;
123        write!(f, "0x{:X},{{", d3)?;
124
125        for el in d4 {
126            write!(f, "0x{:X},", el)?;
127        }
128
129        write!(f, "}}}}")?;
130
131        Ok(())
132    }
133}
134
135fn print_interface<W: Write>(w: &mut W, i: &Interface) -> io::Result<()> {
136    if i.defn.is_none() {
137        return Ok(());
138    }
139
140    writeln!(
141        w,
142        indoc! {r#"
143
144            // interface {}
145
146            #if !defined(__cplusplus) || defined(CINTERFACE)
147
148            #if defined(__cplusplus)
149            extern "C" {{
150            #endif
151        "#},
152        i.name
153    )?;
154
155    print_c_interface(&mut *w, i)?;
156
157    writeln!(
158        w,
159        indoc! {r#"
160            #if defined(__cplusplus)
161            }}
162            #endif
163
164            #else
165        "#}
166    )?;
167
168    print_cpp_interface(&mut *w, i)?;
169
170    writeln!(
171        w,
172        indoc! {r#"
173            #endif
174        "#}
175    )?;
176
177    Ok(())
178}
179
180fn print_c_interface<W: Write>(w: &mut W, i: &Interface) -> io::Result<()> {
181    let defn = i.defn.as_ref().unwrap();
182
183    writeln!(w, "struct {}Vtbl\n{{\n    BEGIN_INTERFACE", i.name)?;
184    writeln!(w)?;
185
186    print_c_functions(&mut *w, i, i)?;
187
188    writeln!(w, "    END_INTERFACE\n}};")?;
189    writeln!(
190        w,
191        "struct {0}\n{{\n    struct {0}Vtbl *lpVtbl;\n}};",
192        i.name
193    )?;
194
195    writeln!(w)?;
196
197    if defn.uuid.is_some() {
198        writeln!(w, "extern IID const IID_{};", i.name)?;
199        writeln!(w)?;
200    }
201
202    print_c_call_macros(&mut *w, i, i)?;
203
204    Ok(())
205}
206
207fn print_c_functions<W: Write>(w: &mut W, i: &Interface, cur: &Interface) -> io::Result<()> {
208    let cur_defn = cur.defn.as_ref().unwrap();
209
210    if let Some(base) = &cur_defn.base {
211        print_c_functions(&mut *w, i, base)?;
212    }
213
214    writeln!(w, "    // {}\n", cur.name)?;
215
216    for f in &cur_defn.fns {
217        print_c_function(&mut *w, i, &f)?;
218        writeln!(w)?;
219    }
220
221    Ok(())
222}
223
224fn print_c_function<W: Write>(w: &mut W, i: &Interface, f: &Function) -> io::Result<()> {
225    write!(w, "    ")?;
226    write_type(&f.ret, &mut *w)?;
227    write!(
228        w,
229        "(STDMETHODCALLTYPE *{})(\n        {} *self",
230        f.name, i.name
231    )?;
232
233    for arg in &f.params {
234        write!(w, ",\n        ")?;
235        write_type(&arg.ty, &mut *w)?;
236
237        if let Some(name) = &arg.name {
238            write!(w, "{}", **name)?;
239        }
240    }
241
242    writeln!(w, "\n    );")?;
243    Ok(())
244}
245
246fn print_c_call_macros<W: Write>(w: &mut W, i: &Interface, cur: &Interface) -> io::Result<()> {
247    let cur_defn = cur.defn.as_ref().unwrap();
248
249    if let Some(base) = &cur_defn.base {
250        print_c_call_macros(&mut *w, i, base)?;
251    }
252
253    for f in &cur_defn.fns {
254        print_c_call_macro(&mut *w, i, &f)?;
255    }
256
257    Ok(())
258}
259
260fn print_c_call_macro<W: Write>(w: &mut W, i: &Interface, f: &Function) -> io::Result<()> {
261    write!(w, "#define {}_{}(self", i.name, f.name)?;
262
263    let write_params_block = |w: &mut W| {
264        for (idx, arg) in f.params.iter().enumerate() {
265            write!(w, ", ")?;
266
267            match &arg.name {
268                Some(name) => write!(w, "{}", **name)?,
269                _ => write!(w, "p{}", idx)?,
270            }
271        }
272
273        Ok::<(), io::Error>(())
274    };
275
276    write_params_block(&mut *w)?;
277
278    write!(w, ") (self->lpVtbl->{}(self", f.name)?;
279
280    write_params_block(&mut *w)?;
281
282    writeln!(w, "))")?;
283
284    Ok(())
285}
286
287fn print_cpp_interface<W: Write>(w: &mut W, i: &Interface) -> io::Result<()> {
288    let defn = i.defn.as_ref().unwrap();
289
290    write!(w, "struct {}", i.name)?;
291
292    if let Some(n) = &defn.base {
293        write!(w, " : public {}", n.name)?;
294    };
295
296    writeln!(w, "\n{{\n    BEGIN_INTERFACE")?;
297    writeln!(w)?;
298
299    print_cpp_functions(&mut *w, i)?;
300
301    writeln!(w, "    END_INTERFACE\n}};")?;
302    writeln!(w)?;
303
304    if let Some(guid) = defn.uuid {
305        write!(
306            w,
307            indoc! {r#"
308                extern "C" IID const IID_{};
309
310                namespace nucom {{
311
312                extern "C++" template<>
313                struct GuidOf<{0}>
314                {{
315                    static constexpr GUID guid = {};
316                }};
317
318                }}
319
320            "#},
321            i.name,
322            GuidRepr(guid)
323        )?;
324    }
325
326    Ok(())
327}
328
329fn print_cpp_functions<W: Write>(w: &mut W, i: &Interface) -> io::Result<()> {
330    let InterfaceDefn { base, fns, .. } = i.defn.as_ref().unwrap();
331
332    let mut base = base;
333    let mut needs_space = false;
334
335    while let Some(Interface {
336        name: base_name,
337        defn:
338            Some(InterfaceDefn {
339                fns: base_fns,
340                base: base_base,
341                ..
342            }),
343        ..
344    }) = base.as_deref()
345    {
346        for f in base_fns {
347            if f.params.iter().any(|p| p.poly_type.is_some()) {
348                writeln!(w, "    using {}::{};", base_name, f.name)?;
349                needs_space = true;
350            }
351        }
352
353        base = &base_base;
354    }
355
356    if needs_space {
357        writeln!(w)?;
358    }
359
360    for f in fns {
361        print_cpp_function(&mut *w, i, &f)?;
362        writeln!(w)?;
363    }
364
365    Ok(())
366}
367
368fn print_cpp_function<W: Write>(w: &mut W, _i: &Interface, f: &Function) -> io::Result<()> {
369    write!(w, "    virtual ")?;
370    write_type(&f.ret, &mut *w)?;
371    write!(w, "STDMETHODCALLTYPE {}(", f.name)?;
372
373    for (idx, arg) in f.params.iter().enumerate() {
374        if idx > 0 {
375            write!(w, ",")?;
376        }
377
378        write!(w, "\n        ")?;
379
380        write_type(&arg.ty, &mut *w)?;
381
382        if let Some(name) = &arg.name {
383            write!(w, "{}", **name)?;
384        }
385    }
386
387    if !f.params.is_empty() {
388        write!(w, "\n    ")?;
389    }
390
391    writeln!(w, ") = 0;")?;
392
393    // Write templated wrapper if necessary
394
395    if f.params.iter().any(|p| p.poly_type.is_some()) {
396        writeln!(w)?;
397
398        #[derive(Debug)]
399        struct PolyTypeInfo {
400            value_pos: usize,
401            template_idx: usize,
402        }
403
404        let mut poly_types = Vec::new();
405        let mut type_idx = 1;
406
407        for (idx, param) in f.params.iter().enumerate() {
408            if let Some(_pt) = &param.poly_type {
409                poly_types.push(PolyTypeInfo {
410                    value_pos: idx,
411                    template_idx: type_idx,
412                });
413
414                type_idx += 1;
415            }
416        }
417
418        write!(w, "    template <")?;
419
420        for (idx, pt) in poly_types.iter().enumerate() {
421            if idx > 0 {
422                write!(w, ", ")?;
423            }
424
425            write!(w, "typename T{}", pt.template_idx)?;
426        }
427
428        writeln!(w, ">")?;
429        write!(w, "    inline ")?;
430        write_type(&f.ret, &mut *w)?;
431        write!(w, "{}(", f.name)?;
432
433        let new_params = f
434            .params
435            .iter()
436            .enumerate()
437            .filter(|(_, param)| param.poly_value.is_none());
438
439        let mut first = true;
440
441        for (idx, arg) in new_params {
442            if !first {
443                write!(w, ",")?;
444            }
445
446            first = false;
447
448            write!(w, "\n        ")?;
449
450            if arg.poly_type.is_none() {
451                write_type(&arg.ty, &mut *w)?;
452            } else {
453                let pt = poly_types.iter().find(|pt| pt.value_pos == idx).unwrap();
454
455                let ty = Ctype {
456                    typename: format!("T{}", pt.template_idx),
457                    ..arg.ty.inner().clone()
458                };
459
460                write_type(&ty, &mut *w)?;
461            }
462
463            if let Some(name) = &arg.name {
464                write!(w, "{}", **name)?;
465            } else {
466                write!(w, "unnamed_{}", idx)?;
467            }
468        }
469
470        write!(w, "\n    ")?;
471        writeln!(w, ")")?;
472
473        writeln!(w, "    {{")?;
474        write!(w, "        return this->{}(", f.name)?;
475
476        for (idx, arg) in f.params.iter().enumerate() {
477            if idx > 0 {
478                write!(w, ",")?;
479            }
480
481            write!(w, "\n            ")?;
482
483            if let Some(poly_value) = arg.poly_value {
484                let pt = poly_types
485                    .iter()
486                    .find(|pt| pt.value_pos == poly_value)
487                    .unwrap();
488                write!(w, "&::nucom::guid_of<T{}>", pt.template_idx)?;
489            } else {
490                let needs_cast = arg.poly_type.is_some();
491
492                if needs_cast {
493                    write!(w, "reinterpret_cast<")?;
494                    write_type_space(&arg.ty, &mut *w, false)?;
495                    write!(w, ">(")?;
496                }
497
498                if let Some(name) = &arg.name {
499                    write!(w, "{}", **name)?;
500                } else {
501                    write!(w, "unnamed_{}", idx)?;
502                }
503
504                if needs_cast {
505                    write!(w, ")")?;
506                }
507            }
508        }
509
510        write!(w, "\n        ")?;
511        writeln!(w, ");")?;
512        writeln!(w, "    }}")?;
513    }
514
515    Ok(())
516}