oo_bindgen/backend/dotnet/
interface.rs1use 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 xmldoc_print(f, interface.doc())
117 })?;
118
119 f.writeln(&format!("{visibility} interface {interface_name}"))?;
120 blocked(f, |f| {
121 interface.untyped().callbacks.iter().try_for_each(|func| {
123 documentation(f, |f| {
125 xmldoc_print(f, &func.doc)?;
127 f.newline()?;
128
129 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 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 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 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 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 f.writeln("[StructLayout(LayoutKind.Sequential)]")?;
257 f.writeln(&format!("internal struct {interface_name}NativeAdapter"))?;
258 blocked(f, |f| {
259 for cb in &interface.untyped().callbacks {
261 f.writeln("[UnmanagedFunctionPointer(CallingConvention.Cdecl)]")?; 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)]")?; 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 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 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 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 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 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 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 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 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}