use crate::metadata::{Method, TypeDefinition};
use crate::metadata::{MethodTable, TypeDefOrRef, TypeDefinitionTable, TypeReferenceTable};
use crate::Assembly;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
#[derive(Debug)]
pub enum BindgenError {
IoError(std::io::Error),
MissingTypeData,
}
fn escape_method(s: &str) -> String {
s.replace(".ctor", "new").replace(".", "_")
}
fn escape_namespace(s: &str) -> String {
s.replace(".", "_")
}
impl From<std::io::Error> for BindgenError {
fn from(err: std::io::Error) -> Self {
Self::IoError(err)
}
}
pub struct BindingGenerator {
assemblies: Vec<Assembly>,
target: File,
namespaces_out: HashMap<String, Vec<u8>>,
}
impl BindingGenerator {
pub fn create<P: AsRef<Path>>(path: P) -> Result<Self, BindgenError> {
println!("path:{}", path.as_ref().display());
let mut target = std::fs::OpenOptions::new()
.write(true)
.read(true)
.create(true)
.open(path)?;
write!(
target,
"// C# bindings generated by wrapped_mono 0.3.2\n#![allow(dead_code)]\n"
);
Ok(Self {
assemblies: Vec::new(),
target,
namespaces_out: HashMap::new(),
})
}
pub fn add_assembly(mut self, asm: Assembly) -> Self {
self.assemblies.push(asm);
self
}
pub fn generate(mut self) -> Result<(), BindgenError> {
if !self.has_corelib() {
self.target.write_all(include_bytes!("corelib_binds.rs"))?;
}
println!("asmc:{}", self.assemblies.len());
for assembly_index in 0..self.assemblies.len() {
let asm = self.assemblies[assembly_index];
let img = asm.get_image();
let tdt = match TypeDefinitionTable::from_image(img) {
Some(tdt) => tdt,
None => continue,
};
let refs = match TypeReferenceTable::from_image(img) {
Some(refs) => refs,
None => TypeReferenceTable::empty(),
};
let asm_name = asm.get_name();
println!("Generating types for assembly {asm_name}");
self.generate_types(tdt, refs, &asm_name)?;
}
for assembly_index in 0..self.assemblies.len() {
let asm = self.assemblies[assembly_index];
let img = asm.get_image();
let tdt = match TypeDefinitionTable::from_image(img) {
Some(tdt) => tdt,
None => continue,
};
let refs = match TypeReferenceTable::from_image(img) {
Some(refs) => refs,
None => TypeReferenceTable::empty(),
};
let asm_name = asm.get_name();
self.generate_methods(tdt, refs, &asm_name)?;
}
for (key, value) in self.namespaces_out {
write!(self.target, "//Implentations for namespace \"{key}\"\n")?;
if !key.is_empty() {
write!(self.target, "mod {key}{{\n")?;
self.target.write_all(&value)?;
write!(self.target, "}}")?;
} else {
self.target.write_all(&value)?;
}
}
Ok(())
}
fn has_corelib(&self) -> bool {
for assembly in &self.assemblies {
if assembly.get_name() == "mscorlib" {
return true;
}
}
false
}
fn generate_method(
&mut self,
tdt: &TypeDefinitionTable,
refs: &TypeReferenceTable,
method: &crate::metadata::Method,
namespace: &str,
type_name: &str,
) -> Result<(), BindgenError> {
let mut out = self.namespaces_out.get_mut(namespace).unwrap();
let name = method.name();
let escaped_name = escape_method(method.name());
let mut param_names = Vec::with_capacity(method.signature().params().len());
for param in method.signature().params() {
let (namespace, name) = if let TypeDefOrRef::TypeDef(index) = param {
let index = *index;
let param = &tdt.defs()[index as usize];
(escape_namespace(param.namespace()), param.name())
} else if let TypeDefOrRef::TypeRef(index) = param {
let index = *index;
let r = match refs.refs().get(index as usize) {
Some(r) => r,
None => return Ok(()),
};
(escape_namespace(r.namespace()), r.name())
} else {
break;
};
if name.contains('<') || name.contains('`') || name.contains('.') {
return Ok(());
}
if namespace.is_empty() {
param_names.push(name.into());
} else {
param_names.push(format!("{namespace}::{name}"));
}
}
write!(out, "impl {type_name}_{escaped_name}_DISPATCH_ARGS for (")?;
if method.signature().flags().has_this() {
if namespace.is_empty() {
param_names.push(name.into());
} else {
param_names.push(format!("{namespace}::{name}"));
}
}
for param_name in ¶m_names {
write!(out, "{param_name},")?;
}
write!(out, "){{")?;
write!(
out,
"\n\ttype ReturnType = System::Object;\n\ttype Args = ("
);
for param_name in ¶m_names {
write!(out, "{param_name},")?;
}
write!(out, ");\n\tfn call(args:Args){{todo!()}}\n}}\n//END\n")?;
Ok(())
}
fn generate_methods(
&mut self,
tdt: TypeDefinitionTable,
refs: TypeReferenceTable,
asm_name: &str,
) -> Result<(), BindgenError> {
for td in tdt.defs() {
let namespace = escape_namespace(td.namespace());
self.create_namespace(&namespace);
let type_name = td.name();
if type_name.contains('<') || type_name.contains('`') {
continue;
}
{
let mut out = self.namespaces_out.get_mut(&namespace).unwrap();
write!(out, "//Implementations of methods for {type_name}\n")?;
}
let mut methods: std::collections::HashSet<String> = HashSet::new();
for method in td.methods() {
let name = method.name();
if name.contains('<')
|| name.contains('>')
|| name.contains('`')
|| name.contains('.')
{
continue;
}
self.generate_method(&tdt, &refs, method, &namespace, &type_name)?;
let mut out = self.namespaces_out.get_mut(&namespace).unwrap();
let escaped_name = escape_method(method.name());
assert!(!escaped_name.contains('.'));
if !methods.contains(&escaped_name) {
write!(out, "trait {type_name}_{escaped_name}_DISPATCH_ARGS{{\n\ttype ReturnType;\n\ttype Args;\n\tfn call(args:Args)->Result<ReturnType,Exception>;\n\t//END\n}}\n");
methods.insert(escaped_name.clone());
}
}
{
let mut out = self.namespaces_out.get_mut(&namespace).unwrap();
write!(out, "//End of Implementations of methods for {type_name}\n")?;
}
}
Ok(())
}
fn generate_types(
&mut self,
tdt: TypeDefinitionTable,
refs: TypeReferenceTable,
asm_name: &str,
) -> Result<(), BindgenError> {
for td in tdt.defs() {
use crate::metadata::TypeDefOrRef::TypeDef;
let name = td.name();
if name.contains('<') || name.contains('`') {
continue;
}
let namespace = td.namespace();
let escaped_namespace = escape_namespace(namespace);
self.create_namespace(&escaped_namespace);
let mut out = self.namespaces_out.get_mut(&escaped_namespace).unwrap();
write!(
out,
"// Bindings to object \"{name}\" in namespace \"{namespace}\".\n"
)?;
write!(out, "struct {name}{{obj:wrapped_mono::Object}}\n");
write!(out,"impl wrapped_mono::InteropClass for {name}{{
fn get_mono_class()->wrapped_mono::Class{{
extern crate lazy_static;
lazy_static::lazy_static!{{
static ref {name}_CLASS:wrapped_mono::Class = {{
let img = Assembly::assembly_loaded(\"{asm_name}\")
.expect(\"Assembly \\\"{asm_name}\\\" is not loaded, could not get \\\"{name}\\\" class!\")
.get_image();
Class::from_name_case(&img, \"{namespace}\", \"{name}\")
.expect(\"Could not get \\\"{name}\\\" class residing in namespace \\\"{namespace}\\\" form assembly \\\"{asm_name}\\\"!\")
}};
}}
*{name}_CLASS
}}
}}\n");
write!(
out,
"impl wrapped_mono::ObjectTrait for {name}{{
fn get_ptr(&self)->*mut wrapped_mono::binds::MonoObject{{
self.object.get_ptr()
}}
unsafe fn from_ptr_unchecked(ptr:*mut wrapped_mono::binds::MonoObject)->Self{{
let object = wrapped_mono::Object::from_ptr_unchecked(ptr);
Self{{object}}
}}
}}\n"
)?;
if !td.extends().is_null() {
let (t_namespace, t_name) = if let TypeDef(index) = td.extends() {
let index = index - 1;
let td = &tdt.defs()[index as usize];
(escape_namespace(td.namespace()), td.name())
} else if let TypeDefOrRef::TypeRef(index) = td.extends() {
let index = index - 1;
let r = &refs.refs()[index as usize];
(escape_namespace(r.namespace()), r.name())
} else {
break;
};
if t_namespace.is_empty() {
write!(out, "impl Into<{t_name}> ")?;
} else {
write!(out, "impl Into<{t_namespace}::{t_name}> ")?;
}
write!(
out,
"for {name}{{
fn into(self)->{t_namespace}::{t_name}{{
//All bindgen objects have identical layout
unsafe{{std::mem::transmute(self)}}
}}
}}"
)?;
}
}
Ok(())
}
fn create_namespace(&mut self, namespace: &str) {
if let Some(out) = self.namespaces_out.get_mut(namespace) {
return;
}
self.namespaces_out.insert(namespace.to_owned(), Vec::new());
}
}