diff --git a/csbindgen/src/emitter.rs b/csbindgen/src/emitter.rs index 32596d7..3689f0a 100644 --- a/csbindgen/src/emitter.rs +++ b/csbindgen/src/emitter.rs @@ -111,6 +111,7 @@ pub fn emit_csharp( } let mut method_list_string = String::new(); + let mut delegate_list = Vec::new(); for item in methods { let mut method_name = &item.method_name; let method_name_temp: String; @@ -119,40 +120,6 @@ pub fn emit_csharp( method_name = &method_name_temp; } - if let Some(x) = &item.return_type { - if let Some(delegate_method) = build_method_delegate_if_required( - x, - options, - aliases, - method_name, - &"return".to_string(), - ) { - method_list_string.push_str( - format!(" [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\n") - .as_str(), - ); - method_list_string - .push_str(format!(" {accessibility} {delegate_method};\n\n").as_str()); - } - } - - for p in item.parameters.iter() { - if let Some(delegate_method) = build_method_delegate_if_required( - &p.rust_type, - options, - aliases, - method_name, - &p.name, - ) { - method_list_string.push_str( - format!(" [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\n") - .as_str(), - ); - method_list_string - .push_str(format!(" {accessibility} {delegate_method};\n\n").as_str()); - } - } - let entry_point = match &item.export_naming { NoMangle => &item.method_name, ExportName(export_name) => export_name, @@ -163,9 +130,14 @@ pub fn emit_csharp( x => format!("{x}{entry_point}"), }; let return_type = match &item.return_type { - Some(x) => { - x.to_csharp_string(options, aliases, false, method_name, &"return".to_string()) - } + Some(x) => x.to_csharp_string( + options, + aliases, + &mut delegate_list, + false, + method_name, + &"return".to_string(), + ), None => "void".to_string(), }; @@ -173,9 +145,14 @@ pub fn emit_csharp( .parameters .iter() .map(|p| { - let mut type_name = - p.rust_type - .to_csharp_string(options, aliases, false, method_name, &p.name); + let mut type_name = p.rust_type.to_csharp_string( + options, + aliases, + &mut delegate_list, + false, + method_name, + &p.name, + ); if type_name == "bool" { type_name = "[MarshalAs(UnmanagedType.U1)] bool".to_string(); } @@ -223,6 +200,7 @@ pub fn emit_csharp( let type_name = field.rust_type.to_csharp_string( options, aliases, + &mut delegate_list, true, &"".to_string(), &"".to_string(), @@ -301,6 +279,7 @@ pub fn emit_csharp( let mut type_name = item.rust_type.to_csharp_string( options, aliases, + &mut delegate_list, false, &"".to_string(), &"".to_string(), @@ -343,6 +322,15 @@ pub fn emit_csharp( } } + let mut delegate_list_string = String::new(); + delegate_list.sort(); + delegate_list.dedup(); + for delegate in delegate_list { + delegate_list_string + .push_str(" [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\n"); + delegate_list_string.push_str(format!(" {accessibility} {delegate};\n\n").as_str()); + } + // use empty string if the generated class is empty. let class_string = if method_list_string.is_empty() && const_string.is_empty() { String::new() @@ -354,6 +342,8 @@ pub fn emit_csharp( {const_string} +{delegate_list_string} + {method_list_string} }}" ) diff --git a/csbindgen/src/type_meta.rs b/csbindgen/src/type_meta.rs index 534d1ff..374bc0f 100644 --- a/csbindgen/src/type_meta.rs +++ b/csbindgen/src/type_meta.rs @@ -1,4 +1,5 @@ use crate::{alias_map::AliasMap, builder::BindgenOptions}; +use std::collections::HashMap; pub fn escape_csharp_name(str: &str) -> String { match str { @@ -225,6 +226,7 @@ impl RustType { &self, options: &BindgenOptions, alias_map: &AliasMap, + delegate_list: &mut Vec, emit_from_struct: bool, method_name: &String, parameter_name: &String, @@ -305,6 +307,7 @@ impl RustType { use_type.to_csharp_string( options, alias_map, + delegate_list, emit_from_struct, method_name, parameter_name, @@ -344,6 +347,7 @@ impl RustType { sb.push_str(&p.rust_type.to_csharp_string( options, alias_map, + delegate_list, emit_from_struct, method_name, parameter_name, @@ -355,6 +359,7 @@ impl RustType { sb.push_str(&x.to_csharp_string( options, alias_map, + delegate_list, emit_from_struct, method_name, parameter_name, @@ -366,6 +371,14 @@ impl RustType { }; sb.push('>'); } else { + build_method_delegate( + self, + options, + alias_map, + delegate_list, + method_name, + parameter_name, + ); sb.push_str(build_method_delegate_name(method_name, parameter_name).as_str()); } } @@ -376,6 +389,7 @@ impl RustType { .to_csharp_string( options, alias_map, + delegate_list, emit_from_struct, method_name, parameter_name, @@ -389,6 +403,7 @@ impl RustType { rust_type: &RustType, options: &BindgenOptions, alias_map: &AliasMap, + delegate_list: &mut Vec, emit_from_struct: bool, method_name: &String, parameter_name: &String, @@ -402,6 +417,7 @@ impl RustType { .to_csharp_string( options, alias_map, + delegate_list, emit_from_struct, method_name, parameter_name, @@ -432,6 +448,7 @@ impl RustType { &use_type, options, alias_map, + delegate_list, emit_from_struct, method_name, parameter_name, @@ -448,6 +465,7 @@ impl RustType { &self, options, alias_map, + delegate_list, emit_from_struct, method_name, parameter_name, @@ -464,69 +482,67 @@ impl RustType { } } -pub fn build_method_delegate_if_required( +pub fn build_method_delegate( me: &RustType, options: &BindgenOptions, alias_map: &AliasMap, + delegate_list: &mut Vec, method_name: &String, parameter_name: &String, -) -> Option { +) { let emit_from_struct = false; match &me.type_kind { TypeKind::Function(parameters, return_type) => { - if emit_from_struct && !options.csharp_use_function_pointer { - None - } else if options.csharp_use_function_pointer { - None - } else { - let return_type_name = match return_type { - Some(x) => x.to_csharp_string( + let return_type_name = match return_type { + Some(x) => x.to_csharp_string( + options, + alias_map, + delegate_list, + emit_from_struct, + &build_method_delegate_name(method_name, parameter_name), + &x.type_name, + ), + None => "void".to_string(), + }; + + let joined_param = parameters + .iter() + .enumerate() + .map(|(index, p)| { + let cs = p.rust_type.to_csharp_string( options, alias_map, + delegate_list, emit_from_struct, - method_name, - parameter_name, - ), - None => "void".to_string(), - }; + &build_method_delegate_name(method_name, parameter_name), + &p.name, + ); + let parameter_name = if p.name == "" { + format!("arg{}", index + 1) + } else { + p.name.clone() + }; - let joined_param = parameters - .iter() - .enumerate() - .map(|(index, p)| { - let cs = p.rust_type.to_csharp_string( - options, - alias_map, - emit_from_struct, - method_name, - parameter_name, - ); - let parameter_name = if p.name == "" { - format!("arg{}", index + 1) - } else { - p.name.clone() - }; - - format!("{} {}", cs, escape_csharp_name(parameter_name.as_str())) - }) - .collect::>() - .join(", "); + format!("{} {}", cs, escape_csharp_name(parameter_name.as_str())) + }) + .collect::>() + .join(", "); - let delegate_name = build_method_delegate_name(method_name, parameter_name); - let delegate_code = - format!("delegate {return_type_name} {delegate_name}({joined_param})"); - Some(delegate_code) - } + let delegate_name = build_method_delegate_name(method_name, parameter_name); + let delegate_code = + format!("delegate {return_type_name} {delegate_name}({joined_param})"); + delegate_list.push(delegate_code); } - TypeKind::Option(inner) => build_method_delegate_if_required( + TypeKind::Option(inner) => build_method_delegate( inner, options, alias_map, + delegate_list, method_name, parameter_name, ), - _ => None, + _ => (), } }