oo_bindgen/backend/dotnet/
interface.rs

1use crate::backend::dotnet::helpers::*;
2use crate::backend::dotnet::*;
3
4trait ConstantReturnValue {
5    fn get_constant_return_value(&self) -> String;
6}
7
8trait MaybeConstantReturnValue {
9    fn try_get_constant_return_value(&self) -> Option<String>;
10}
11
12impl<T> MaybeConstantReturnValue for T
13where
14    T: ConstantReturnValue,
15{
16    fn try_get_constant_return_value(&self) -> Option<String> {
17        Some(self.get_constant_return_value())
18    }
19}
20
21impl ConstantReturnValue for PrimitiveValue {
22    fn get_constant_return_value(&self) -> String {
23        match self {
24            PrimitiveValue::Bool(x) => x.to_string(),
25            PrimitiveValue::U8(x) => x.to_string(),
26            PrimitiveValue::S8(x) => x.to_string(),
27            PrimitiveValue::U16(x) => x.to_string(),
28            PrimitiveValue::S16(x) => x.to_string(),
29            PrimitiveValue::U32(x) => x.to_string(),
30            PrimitiveValue::S32(x) => x.to_string(),
31            PrimitiveValue::U64(x) => x.to_string(),
32            PrimitiveValue::S64(x) => x.to_string(),
33            PrimitiveValue::Float(x) => x.to_string(),
34            PrimitiveValue::Double(x) => x.to_string(),
35        }
36    }
37}
38
39impl ConstantReturnValue for EnumValue {
40    fn get_constant_return_value(&self) -> String {
41        format!(
42            "{}.{}",
43            self.handle.name.camel_case(),
44            self.variant.name.camel_case()
45        )
46    }
47}
48
49impl ConstantReturnValue for DurationValue {
50    fn get_constant_return_value(&self) -> String {
51        match self {
52            DurationValue::Milliseconds(x) => format!("TimeSpan.FromMilliseconds({x})"),
53            DurationValue::Seconds(x) => format!("TimeSpan.FromSeconds({x})"),
54        }
55    }
56}
57
58impl ConstantReturnValue for BasicValue {
59    fn get_constant_return_value(&self) -> String {
60        match self {
61            BasicValue::Primitive(x) => x.get_constant_return_value(),
62            BasicValue::Duration(x) => x.get_constant_return_value(),
63            BasicValue::Enum(x) => x.get_constant_return_value(),
64        }
65    }
66}
67
68impl ConstantReturnValue for ZeroParameterStructInitializer {
69    fn get_constant_return_value(&self) -> String {
70        match self.initializer.initializer_type {
71            InitializerType::Normal => format!("new {}()", self.handle.name().camel_case()),
72            InitializerType::Static => format!(
73                "{}.{}()",
74                self.handle.name().camel_case(),
75                self.initializer.name.camel_case()
76            ),
77        }
78    }
79}
80
81impl MaybeConstantReturnValue for DefaultCallbackReturnValue {
82    fn try_get_constant_return_value(&self) -> Option<String> {
83        match self {
84            DefaultCallbackReturnValue::Void => None,
85            DefaultCallbackReturnValue::Basic(x) => x.try_get_constant_return_value(),
86            DefaultCallbackReturnValue::InitializedStruct(x) => x.try_get_constant_return_value(),
87        }
88    }
89}
90
91pub(crate) fn generate(
92    f: &mut dyn Printer,
93    interface: &InterfaceType<Validated>,
94    lib: &Library,
95    framework: TargetFramework,
96) -> FormattingResult<()> {
97    let interface_name = format!("I{}", interface.name().camel_case());
98
99    let destroy_func_name = lib.settings.interface.destroy_func_name.clone();
100    let ctx_variable_name = lib.settings.interface.context_variable_name.clone();
101
102    print_license(f, &lib.info.license_description)?;
103    print_imports(f)?;
104    f.newline()?;
105
106    let is_private = interface
107        .untyped()
108        .get_functional_callback()
109        .map(|cb| cb.functional_transform.enabled())
110        .unwrap_or(false);
111    let visibility = if is_private { "internal" } else { "public" };
112
113    namespaced(f, &lib.settings.name, |f| {
114        documentation(f, |f| {
115            // Print top-level documentation
116            xmldoc_print(f, interface.doc())
117        })?;
118
119        f.writeln(&format!("{visibility} interface {interface_name}"))?;
120        blocked(f, |f| {
121            // Write each required method
122            interface.untyped().callbacks.iter().try_for_each(|func| {
123                // Documentation
124                documentation(f, |f| {
125                    // Print top-level documentation
126                    xmldoc_print(f, &func.doc)?;
127                    f.newline()?;
128
129                    // Print each parameter value
130                    for arg in &func.arguments {
131                        f.writeln(&format!("<param name=\"{}\">", arg.name.mixed_case()))?;
132                        docstring_print(f, &arg.doc)?;
133                        f.write("</param>")?;
134                    }
135
136                    // Print return value
137                    if let Some(doc) = &func.return_type.get_doc() {
138                        f.writeln("<returns>")?;
139                        docstring_print(f, doc)?;
140                        f.write("</returns>")?;
141                    }
142
143                    Ok(())
144                })?;
145
146                // Callback signature
147                f.writeln(&format!(
148                    "{} {}(",
149                    func.return_type.get_dotnet_type(),
150                    func.name.camel_case()
151                ))?;
152                f.write(
153                    &func
154                        .arguments
155                        .iter()
156                        .map(|arg| {
157                            format!(
158                                "{} {}",
159                                arg.arg_type.get_dotnet_type(),
160                                arg.name.mixed_case()
161                            )
162                        })
163                        .collect::<Vec<String>>()
164                        .join(", "),
165                )?;
166                match &func.default_implementation {
167                    None => {
168                        f.write(");")
169                    }
170                    Some(di) => {
171                        if framework.supports_default_interface_methods() {
172                            match di.try_get_constant_return_value() {
173                                None => {
174                                    f.write(") {}")
175                                }
176                                Some(value) => {
177                                    f.write(") {")?;
178                                    indented(f, |f| {
179                                        f.writeln(&format!("return {value};"))
180                                    })?;
181                                    f.writeln("}")
182                                }
183                            }
184                        } else {
185                            tracing::warn!("Method {}::{} has a default implementation defined, but it cannot be supported in C# 7.3", interface.name().camel_case(), func.name.camel_case());
186                            f.write(");")
187                        }
188                    }
189                }
190            })
191        })?;
192
193        f.newline()?;
194
195        // Write the Action<>/Func<> based implementation if it's a functional interface
196        if let Some(callback) = interface.untyped().get_functional_callback() {
197            namespaced(f, "functional", |f| {
198                generate_functional_helpers(f, interface.untyped(), callback)
199            })?;
200            f.newline()?;
201        }
202
203        // write a Task-based implementation if it's a future interface
204        if let InterfaceType::Future(fi) = interface {
205            let class_name = fi.interface.name.camel_case();
206            let value_type = fi.value_type.get_dotnet_type();
207            let success_method_name = fi
208                .interface
209                .settings
210                .future
211                .success_callback_method_name
212                .camel_case();
213
214            f.writeln(&format!("internal class {class_name}: {interface_name}"))?;
215            blocked(f, |f| {
216                f.writeln(&format!(
217                    "private TaskCompletionSource<{value_type}> tcs = new TaskCompletionSource<{value_type}>();"
218                ))?;
219                f.newline()?;
220                f.writeln(&format!(
221                    "internal {class_name}(TaskCompletionSource<{value_type}> tcs)"
222                ))?;
223                blocked(f, |f| f.writeln("this.tcs = tcs;"))?;
224                f.newline()?;
225                f.writeln(&format!(
226                    "void {interface_name}.{success_method_name}({value_type} value)"
227                ))?;
228                blocked(f, |f| f.writeln("Task.Run(() => tcs.SetResult(value));"))?;
229                f.newline()?;
230
231                let error_method_name = fi
232                    .interface
233                    .settings
234                    .future
235                    .failure_callback_method_name
236                    .camel_case();
237                f.writeln(&format!(
238                    "void {}.{}({} err)",
239                    interface_name,
240                    error_method_name,
241                    fi.error_type.inner.get_dotnet_type()
242                ))?;
243                blocked(f, |f| {
244                    f.writeln(&format!(
245                        "Task.Run(() => tcs.SetException(new {}(err)));",
246                        fi.error_type.exception_name.camel_case()
247                    ))
248                })?;
249
250                Ok(())
251            })?;
252            f.newline()?;
253        }
254
255        // Create the native adapter
256        f.writeln("[StructLayout(LayoutKind.Sequential)]")?;
257        f.writeln(&format!("internal struct {interface_name}NativeAdapter"))?;
258        blocked(f, |f| {
259            // Define each delegate type
260            for cb in &interface.untyped().callbacks {
261                f.writeln("[UnmanagedFunctionPointer(CallingConvention.Cdecl)]")?; // C calling convetion
262                f.writeln(&format!(
263                    "private delegate {} {}_delegate(",
264                    cb.return_type.get_native_type(),
265                    cb.name
266                ))?;
267                f.write(
268                    &cb.arguments
269                        .iter()
270                        .map(|arg| {
271                            format!(
272                                "{} {}",
273                                arg.arg_type.get_native_type(),
274                                arg.name.mixed_case()
275                            )
276                        })
277                        .chain(std::iter::once(format!(
278                            "IntPtr {}",
279                            lib.settings.interface.context_variable_name
280                        )))
281                        .collect::<Vec<String>>()
282                        .join(", "),
283                )?;
284                f.write(");")?;
285                f.writeln(&format!(
286                    "private static {}_delegate {}_static_delegate = {}NativeAdapter.{}_cb;",
287                    cb.name, cb.name, interface_name, cb.name
288                ))?;
289            }
290
291            f.writeln("[UnmanagedFunctionPointer(CallingConvention.Cdecl)]")?; // C calling convetion
292            f.writeln(&format!(
293                "private delegate void {destroy_func_name}_delegate(IntPtr arg);"
294            ))?;
295
296            f.writeln(&format!(
297                "private static {destroy_func_name}_delegate {destroy_func_name}_static_delegate = {interface_name}NativeAdapter.{destroy_func_name}_cb;"
298            ))?;
299
300            f.newline()?;
301
302            // Define each structure element that will be marshalled
303            for cb in &interface.untyped().callbacks {
304                f.writeln(&format!("private {}_delegate {};", cb.name, cb.name))?;
305            }
306
307            f.writeln(&format!(
308                "private {destroy_func_name}_delegate {destroy_func_name};"
309            ))?;
310            f.writeln(&format!("public IntPtr {ctx_variable_name};"))?;
311
312            f.newline()?;
313
314            // Define the constructor
315            f.writeln(&format!(
316                "internal {interface_name}NativeAdapter({interface_name} impl)"
317            ))?;
318            blocked(f, |f| {
319                f.writeln("var _handle = GCHandle.Alloc(impl);")?;
320                f.newline()?;
321
322                for cb in &interface.untyped().callbacks {
323                    f.writeln(&format!(
324                        "this.{} = {}NativeAdapter.{}_static_delegate;",
325                        cb.name, interface_name, cb.name
326                    ))?;
327
328                    f.newline()?;
329                }
330
331                f.writeln(&format!(
332                    "this.{destroy_func_name} = {interface_name}NativeAdapter.{destroy_func_name}_static_delegate;"
333                ))?;
334
335                f.writeln(&format!(
336                    "this.{ctx_variable_name} = GCHandle.ToIntPtr(_handle);"
337                ))?;
338                Ok(())
339            })?;
340
341            // Define each delegate function
342            for cb in &interface.untyped().callbacks {
343                f.writeln(&format!(
344                    "internal static {} {}_cb(",
345                    cb.return_type.get_native_type(),
346                    cb.name
347                ))?;
348                f.write(
349                    &cb.arguments
350                        .iter()
351                        .map(|arg| {
352                            format!(
353                                "{} {}",
354                                arg.arg_type.get_native_type(),
355                                arg.name.mixed_case()
356                            )
357                        })
358                        .chain(std::iter::once(format!("IntPtr {ctx_variable_name}")))
359                        .collect::<Vec<String>>()
360                        .join(", "),
361                )?;
362                f.write(")")?;
363
364                blocked(f, |f| {
365                    f.writeln(&format!(
366                        "var _handle = GCHandle.FromIntPtr({ctx_variable_name});"
367                    ))?;
368                    f.writeln(&format!("var _impl = ({interface_name})_handle.Target;"))?;
369                    call_dotnet_function(f, cb, "return ")
370                })?;
371
372                f.newline()?;
373            }
374
375            // destroy delegate
376            f.writeln(&format!(
377                "internal static void {destroy_func_name}_cb(IntPtr arg)"
378            ))?;
379
380            blocked(f, |f| {
381                f.writeln("var _handle = GCHandle.FromIntPtr(arg);")?;
382                f.writeln("_handle.Free();")
383            })?;
384
385            f.newline()?;
386
387            f.newline()?;
388
389            // Write the conversion routine
390            f.writeln(&format!(
391                "internal static {interface_name} FromNative(IntPtr self)"
392            ))?;
393            blocked(f, |f| {
394                f.writeln("if (self != IntPtr.Zero)")?;
395                blocked(f, |f| {
396                    f.writeln("var handle = GCHandle.FromIntPtr(self);")?;
397                    f.writeln(&format!("return handle.Target as {interface_name};"))
398                })?;
399                f.writeln("else")?;
400                blocked(f, |f| f.writeln("return null;"))
401            })
402        })
403    })
404}
405
406pub(crate) fn generate_interface_implementation(
407    f: &mut dyn Printer,
408    interface: &Handle<Interface<Validated>>,
409    cb: &CallbackFunction<Validated>,
410) -> FormattingResult<()> {
411    let functor_type = full_functor_type(cb);
412
413    f.writeln(&format!(
414        "internal class Implementation: I{}",
415        interface.name.camel_case()
416    ))?;
417    blocked(f, |f| {
418        f.writeln(&format!("private readonly {functor_type} action;"))?;
419        f.newline()?;
420
421        // constructor
422        f.writeln(&format!("internal Implementation({functor_type} action)"))?;
423        blocked(f, |f| f.writeln("this.action = action;"))?;
424
425        f.newline()?;
426
427        f.writeln(&format!(
428            "public {} {}(",
429            cb.return_type.get_dotnet_type(),
430            cb.name.camel_case()
431        ))?;
432        f.write(
433            &cb.arguments
434                .iter()
435                .map(|param| {
436                    format!(
437                        "{} {}",
438                        param.arg_type.get_dotnet_type(),
439                        param.name.mixed_case()
440                    )
441                })
442                .collect::<Vec<_>>()
443                .join(", "),
444        )?;
445        f.write(")")?;
446        blocked(f, |f| {
447            f.newline()?;
448
449            if !cb.return_type.is_none() {
450                f.write("return ")?;
451            }
452
453            let params = cb
454                .arguments
455                .iter()
456                .map(|param| param.name.mixed_case())
457                .collect::<Vec<_>>()
458                .join(", ");
459
460            f.write(&format!("this.action.Invoke({params});"))
461        })
462    })
463}
464
465pub(crate) fn generate_functional_helpers(
466    f: &mut dyn Printer,
467    interface: &Handle<Interface<Validated>>,
468    cb: &CallbackFunction<Validated>,
469) -> FormattingResult<()> {
470    let interface_name = format!("I{}", interface.name.camel_case());
471    let class_name = interface.name.camel_case();
472    let functor_type = full_functor_type(cb);
473
474    let visibility = if cb.functional_transform.enabled() {
475        "internal"
476    } else {
477        "public"
478    };
479
480    documentation(f, |f| {
481        f.writeln("<summary>")?;
482        f.writeln(&format!(
483            "Provides a method to create an implementation of {interface_name} from a functor"
484        ))?;
485        f.writeln("</summary>")
486    })?;
487    f.writeln(&format!("{visibility} static class {class_name}"))?;
488    blocked(f, |f| {
489        f.newline()?;
490        // write the private implementation class
491        generate_interface_implementation(f, interface, cb)?;
492        f.newline()?;
493
494        documentation(f, |f| {
495            f.writeln("<summary>")?;
496            f.write(&format!(
497                "Creates an instance of {} which invokes a {}",
498                interface_name,
499                base_functor_type(cb)
500            ))?;
501            f.write("</summary>")?;
502            f.newline()?;
503            f.writeln("<param name=\"action\">")?;
504            f.writeln("Callback to execute")?;
505            f.writeln("</param>")?;
506            f.writeln(&format!(
507                "<return>An implementation of {interface_name}</return>"
508            ))?;
509            Ok(())
510        })?;
511        // write the factory function
512        f.writeln(&format!(
513            "{visibility} static {interface_name} create({functor_type} action)"
514        ))?;
515        blocked(f, |f| f.writeln("return new Implementation(action);"))?;
516
517        Ok(())
518    })
519}