Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: export safe handle. #1

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions csbindgen/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::type_meta::RustType;
use std::path::PathBuf;
use std::{
error::Error,
Expand Down Expand Up @@ -32,6 +33,8 @@ pub struct BindgenOptions {
pub csharp_use_nint_types: bool,
pub csharp_imported_namespaces: Vec<String>,
pub csharp_generate_const_filter: fn(const_name: &str) -> bool,
pub csharp_gen_safe_handle: Vec<String>,
pub csharp_type_redirect: fn(rust_type: &RustType) -> RustType,
}

impl Default for Builder {
Expand All @@ -57,6 +60,8 @@ impl Default for Builder {
csharp_use_nint_types: true,
csharp_imported_namespaces: vec![],
csharp_generate_const_filter: |_| false,
csharp_gen_safe_handle: vec![],
csharp_type_redirect: |x| x.clone(),
},
}
}
Expand All @@ -69,7 +74,9 @@ impl Builder {

/// Add an input .rs file(such as generated from bindgen) to generate binding.
pub fn input_bindgen_file<T: AsRef<Path>>(mut self, input_bindgen_file: T) -> Builder {
self.options.input_bindgen_files.push(input_bindgen_file.as_ref().to_path_buf());
self.options
.input_bindgen_files
.push(input_bindgen_file.as_ref().to_path_buf());
self
}

Expand All @@ -87,8 +94,6 @@ impl Builder {
self
}



/// add original extern call type prefix to rust wrapper,
/// `return {rust_method_type_path}::foo()`
pub fn rust_method_type_path<T: Into<String>>(mut self, rust_method_type_path: T) -> Builder {
Expand All @@ -110,6 +115,20 @@ impl Builder {
self
}

/// configure C# generate SafeHandle,
/// "using {csharp_namespace};"
pub fn csharp_gen_safe_handle<T: Into<String>>(mut self, class_name: T) -> Builder {
self.options.csharp_gen_safe_handle.push(class_name.into());
self
}

/// configure C# type redirect,
/// `[DllImport({csharp_dll_name})]`
pub fn csharp_type_redirect(mut self, func: fn(rust_type: &RustType) -> RustType) -> Builder {
self.options.csharp_type_redirect = func;
self
}

/// configure C# file namespace(default is `CsBindgen`),
/// "namespace {csharp_namespace}"
pub fn csharp_namespace<T: Into<String>>(mut self, csharp_namespace: T) -> Builder {
Expand Down Expand Up @@ -197,12 +216,19 @@ impl Builder {
/// configure C# generate const, default is false
/// equivalent to csharp_generate_const_filter(|_| csharp_generate_const)
#[deprecated(note = "User csharp_generate_const_filter instead")]
pub fn csharp_generate_const(mut self, csharp_generate_const: bool) -> Builder {
self.csharp_generate_const_filter(if csharp_generate_const { |_| true } else { |_| false })
pub fn csharp_generate_const(self, csharp_generate_const: bool) -> Builder {
self.csharp_generate_const_filter(if csharp_generate_const {
|_| true
} else {
|_| false
})
}

/// configure C# generate const filter, default `|_| false`
pub fn csharp_generate_const_filter(mut self, csharp_generate_const_filter: fn(const_name: &str) -> bool) -> Builder {
pub fn csharp_generate_const_filter(
mut self,
csharp_generate_const_filter: fn(const_name: &str) -> bool,
) -> Builder {
self.options.csharp_generate_const_filter = csharp_generate_const_filter;
self
}
Expand Down
200 changes: 195 additions & 5 deletions csbindgen/src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,178 @@ use ::std::os::raw::*;
result
}

// convert struct* to SafeHandle
pub fn emit_type_redirect(options: &BindgenOptions, rust_type: &RustType) -> RustType {
if let TypeKind::Pointer(_, inner) = &rust_type.type_kind {
if options
.csharp_gen_safe_handle
.iter()
.any(|name| name.to_string() == inner.type_name)
{
return RustType {
type_name: format!("{}Handle", inner.type_name),
type_kind: TypeKind::Normal,
};
}
}

(options.csharp_type_redirect)(&rust_type)
}

// convert struct* to System.IntPtr
pub fn emit_type_to_intptr(options: &BindgenOptions, rust_type: &RustType) -> RustType {
if let TypeKind::Pointer(_, inner) = &rust_type.type_kind {
if options
.csharp_gen_safe_handle
.iter()
.any(|name| name.to_string() == inner.type_name)
{
return RustType {
type_name: "System.IntPtr".to_string(),
type_kind: TypeKind::Normal,
};
}
}

(options.csharp_type_redirect)(&rust_type)
}

pub fn emit_csharp_safe_handle(
methods: &Vec<ExternMethod>,
aliases: &AliasMap,
options: &BindgenOptions,
) -> String {
let mut structs_string = String::new();

let class_name = &options.csharp_class_name;
let method_prefix = &options.csharp_method_prefix;

for struct_name in &options.csharp_gen_safe_handle {
let struct_type = emit_type_redirect(
options,
&RustType {
type_name: struct_name.to_string(),
type_kind: TypeKind::Pointer(
PointerType::Box,
Box::new(RustType {
type_name: struct_name.to_string(),
type_kind: TypeKind::Normal,
}),
),
},
);

let convert_type = emit_type_redirect(options, &struct_type);

//let name = (options.csharp_type_rename)(escape_name(&item.struct_name));
let name = escape_name(&convert_type.type_name);

structs_string.push_str_ln(
format!(
"
public partial class {name} : SafeHandle {{
public {name}(System.IntPtr ptr) : base(ptr, true) {{ }}
public {name}() : base(IntPtr.Zero, true) {{ }}
public override bool IsInvalid {{ get {{ return this.handle == IntPtr.Zero; }} }}"
)
.as_str(),
);

// generate methods
for item in methods {
let mut method_name = &item.method_name;
let method_name_temp: String;
if method_prefix.is_empty() {
method_name_temp = escape_name(method_name);
method_name = &method_name_temp;
}

if item.parameters.len() < 1 {
continue;
}

let first_parameter = &item.parameters[0];
let first_parameter_type_name =
emit_type_redirect(options, &first_parameter.rust_type).type_name;

if first_parameter_type_name != name {
continue;
}

let return_type = match &item.return_type {
Some(x) => {
let redirect_type = emit_type_redirect(options, &x);

redirect_type.to_csharp_string(
options,
aliases,
false,
method_name,
&"return".to_string(),
)
}
None => "void".to_string(),
};

let mut parameters_str = String::new();
let mut call_parameters_str = String::from("this.handle");

for idx in 1..item.parameters.len() {
let p = &item.parameters[idx];
let redirect_type = emit_type_redirect(options, &p.rust_type);

let type_name =
redirect_type.to_csharp_string(options, aliases, false, method_name, &p.name);

if idx != 1 {
parameters_str += ", ";
}

// delegate function
if let Some(_) = build_method_delegate_if_required(
&p.rust_type,
options,
aliases,
method_name,
&p.name,
) {
parameters_str += format!("{class_name}.").as_str();
}

parameters_str +=
format!("{} {}", type_name, escape_name(p.name.as_str())).as_str();

call_parameters_str += ", ";
call_parameters_str += escape_name(p.name.as_str()).as_str();
}

structs_string.push_str_ln(
format!(
" public unsafe {return_type} {method_prefix}{method_name}({parameters_str})"
)
.as_str(),
);

structs_string.push_str(" {\n ");
if let Some(_) = &item.return_type {
structs_string.push_str("return ");
}

structs_string.push_str_ln(
format!("{class_name}.{method_name}({call_parameters_str});").as_str(),
);

structs_string.push_str_ln(" }");

structs_string.push('\n');
}

structs_string.push_str_ln(" }");
}

structs_string
}

pub fn emit_csharp(
methods: &Vec<ExternMethod>,
aliases: &AliasMap,
Expand Down Expand Up @@ -158,18 +330,32 @@ pub fn emit_csharp(
};
let return_type = match &item.return_type {
Some(x) => {
x.to_csharp_string(options, aliases, false, method_name, &"return".to_string())
let redirect_type = emit_type_redirect(options, &x);
redirect_type.to_csharp_string(
options,
aliases,
false,
method_name,
&"return".to_string(),
)
}
None => "void".to_string(),
};

let mut parameter_count = 0;
let parameters = item
.parameters
.iter()
.map(|p| {
let redirect_type = if parameter_count == 0 {
emit_type_to_intptr(options, &p.rust_type)
} else {
emit_type_redirect(options, &p.rust_type)
};

parameter_count += 1;
let mut type_name =
p.rust_type
.to_csharp_string(options, aliases, false, method_name, &p.name);
redirect_type.to_csharp_string(options, aliases, false, method_name, &p.name);
if type_name == "bool" {
type_name = "[MarshalAs(UnmanagedType.U1)] bool".to_string();
}
Expand Down Expand Up @@ -215,7 +401,8 @@ pub fn emit_csharp(
structs_string.push_str_ln(" [FieldOffset(0)]");
}

let type_name = field.rust_type.to_csharp_string(
let redirect_type = (options.csharp_type_redirect)(&field.rust_type);
let type_name = redirect_type.to_csharp_string(
options,
aliases,
true,
Expand Down Expand Up @@ -268,6 +455,8 @@ pub fn emit_csharp(
structs_string.push('\n');
}

let class_helper_string = emit_csharp_safe_handle(methods, aliases, options);

let mut enum_string = String::new();
for item in enums {
let repr = match &item.repr {
Expand Down Expand Up @@ -321,7 +510,7 @@ pub fn emit_csharp(
} else {
let value = if type_name == "float" {
format!("{}f", item.value)
}else {
} else {
item.value.to_string()
};

Expand Down Expand Up @@ -367,6 +556,7 @@ namespace {namespace}

{structs_string}
{enum_string}
{class_helper_string}
}}
"
);
Expand Down
5 changes: 2 additions & 3 deletions csbindgen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod builder;
mod emitter;
mod field_map;
mod parser;
mod type_meta;
pub mod type_meta;
mod util;

use alias_map::AliasMap;
Expand Down Expand Up @@ -49,8 +49,7 @@ pub(crate) fn generate(
collect_struct(&file_ast, &mut structs);
collect_enum(&file_ast, &mut enums);

collect_const(&file_ast, &mut consts,options.csharp_generate_const_filter);

collect_const(&file_ast, &mut consts, options.csharp_generate_const_filter);
}

// collect using_types
Expand Down
Loading