use crate::backend::dotnet::helpers::*;
use crate::backend::dotnet::*;
trait ConstantReturnValue {
fn get_constant_return_value(&self) -> String;
}
trait MaybeConstantReturnValue {
fn try_get_constant_return_value(&self) -> Option<String>;
}
impl<T> MaybeConstantReturnValue for T
where
T: ConstantReturnValue,
{
fn try_get_constant_return_value(&self) -> Option<String> {
Some(self.get_constant_return_value())
}
}
impl ConstantReturnValue for PrimitiveValue {
fn get_constant_return_value(&self) -> String {
match self {
PrimitiveValue::Bool(x) => x.to_string(),
PrimitiveValue::U8(x) => x.to_string(),
PrimitiveValue::S8(x) => x.to_string(),
PrimitiveValue::U16(x) => x.to_string(),
PrimitiveValue::S16(x) => x.to_string(),
PrimitiveValue::U32(x) => x.to_string(),
PrimitiveValue::S32(x) => x.to_string(),
PrimitiveValue::U64(x) => x.to_string(),
PrimitiveValue::S64(x) => x.to_string(),
PrimitiveValue::Float(x) => x.to_string(),
PrimitiveValue::Double(x) => x.to_string(),
}
}
}
impl ConstantReturnValue for EnumValue {
fn get_constant_return_value(&self) -> String {
format!(
"{}.{}",
self.handle.name.camel_case(),
self.variant.name.camel_case()
)
}
}
impl ConstantReturnValue for DurationValue {
fn get_constant_return_value(&self) -> String {
match self {
DurationValue::Milliseconds(x) => format!("TimeSpan.FromMilliseconds({x})"),
DurationValue::Seconds(x) => format!("TimeSpan.FromSeconds({x})"),
}
}
}
impl ConstantReturnValue for BasicValue {
fn get_constant_return_value(&self) -> String {
match self {
BasicValue::Primitive(x) => x.get_constant_return_value(),
BasicValue::Duration(x) => x.get_constant_return_value(),
BasicValue::Enum(x) => x.get_constant_return_value(),
}
}
}
impl ConstantReturnValue for ZeroParameterStructInitializer {
fn get_constant_return_value(&self) -> String {
match self.initializer.initializer_type {
InitializerType::Normal => format!("new {}()", self.handle.name().camel_case()),
InitializerType::Static => format!(
"{}.{}()",
self.handle.name().camel_case(),
self.initializer.name.camel_case()
),
}
}
}
impl MaybeConstantReturnValue for DefaultCallbackReturnValue {
fn try_get_constant_return_value(&self) -> Option<String> {
match self {
DefaultCallbackReturnValue::Void => None,
DefaultCallbackReturnValue::Basic(x) => x.try_get_constant_return_value(),
DefaultCallbackReturnValue::InitializedStruct(x) => x.try_get_constant_return_value(),
}
}
}
pub(crate) fn generate(
f: &mut dyn Printer,
interface: &InterfaceType<Validated>,
lib: &Library,
framework: TargetFramework,
) -> FormattingResult<()> {
let interface_name = format!("I{}", interface.name().camel_case());
let destroy_func_name = lib.settings.interface.destroy_func_name.clone();
let ctx_variable_name = lib.settings.interface.context_variable_name.clone();
print_license(f, &lib.info.license_description)?;
print_imports(f)?;
f.newline()?;
let is_private = interface
.untyped()
.get_functional_callback()
.map(|cb| cb.functional_transform.enabled())
.unwrap_or(false);
let visibility = if is_private { "internal" } else { "public" };
namespaced(f, &lib.settings.name, |f| {
documentation(f, |f| {
xmldoc_print(f, interface.doc())
})?;
f.writeln(&format!("{visibility} interface {interface_name}"))?;
blocked(f, |f| {
interface.untyped().callbacks.iter().try_for_each(|func| {
documentation(f, |f| {
xmldoc_print(f, &func.doc)?;
f.newline()?;
for arg in &func.arguments {
f.writeln(&format!("<param name=\"{}\">", arg.name.mixed_case()))?;
docstring_print(f, &arg.doc)?;
f.write("</param>")?;
}
if let Some(doc) = &func.return_type.get_doc() {
f.writeln("<returns>")?;
docstring_print(f, doc)?;
f.write("</returns>")?;
}
Ok(())
})?;
f.writeln(&format!(
"{} {}(",
func.return_type.get_dotnet_type(),
func.name.camel_case()
))?;
f.write(
&func
.arguments
.iter()
.map(|arg| {
format!(
"{} {}",
arg.arg_type.get_dotnet_type(),
arg.name.mixed_case()
)
})
.collect::<Vec<String>>()
.join(", "),
)?;
match &func.default_implementation {
None => {
f.write(");")
}
Some(di) => {
if framework.supports_default_interface_methods() {
match di.try_get_constant_return_value() {
None => {
f.write(") {}")
}
Some(value) => {
f.write(") {")?;
indented(f, |f| {
f.writeln(&format!("return {value};"))
})?;
f.writeln("}")
}
}
} else {
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());
f.write(");")
}
}
}
})
})?;
f.newline()?;
if let Some(callback) = interface.untyped().get_functional_callback() {
namespaced(f, "functional", |f| {
generate_functional_helpers(f, interface.untyped(), callback)
})?;
f.newline()?;
}
if let InterfaceType::Future(fi) = interface {
let class_name = fi.interface.name.camel_case();
let value_type = fi.value_type.get_dotnet_type();
let success_method_name = fi
.interface
.settings
.future
.success_callback_method_name
.camel_case();
f.writeln(&format!("internal class {class_name}: {interface_name}"))?;
blocked(f, |f| {
f.writeln(&format!(
"private TaskCompletionSource<{value_type}> tcs = new TaskCompletionSource<{value_type}>();"
))?;
f.newline()?;
f.writeln(&format!(
"internal {class_name}(TaskCompletionSource<{value_type}> tcs)"
))?;
blocked(f, |f| f.writeln("this.tcs = tcs;"))?;
f.newline()?;
f.writeln(&format!(
"void {interface_name}.{success_method_name}({value_type} value)"
))?;
blocked(f, |f| f.writeln("Task.Run(() => tcs.SetResult(value));"))?;
f.newline()?;
let error_method_name = fi
.interface
.settings
.future
.failure_callback_method_name
.camel_case();
f.writeln(&format!(
"void {}.{}({} err)",
interface_name,
error_method_name,
fi.error_type.inner.get_dotnet_type()
))?;
blocked(f, |f| {
f.writeln(&format!(
"Task.Run(() => tcs.SetException(new {}(err)));",
fi.error_type.exception_name.camel_case()
))
})?;
Ok(())
})?;
f.newline()?;
}
f.writeln("[StructLayout(LayoutKind.Sequential)]")?;
f.writeln(&format!("internal struct {interface_name}NativeAdapter"))?;
blocked(f, |f| {
for cb in &interface.untyped().callbacks {
f.writeln("[UnmanagedFunctionPointer(CallingConvention.Cdecl)]")?; f.writeln(&format!(
"private delegate {} {}_delegate(",
cb.return_type.get_native_type(),
cb.name
))?;
f.write(
&cb.arguments
.iter()
.map(|arg| {
format!(
"{} {}",
arg.arg_type.get_native_type(),
arg.name.mixed_case()
)
})
.chain(std::iter::once(format!(
"IntPtr {}",
lib.settings.interface.context_variable_name
)))
.collect::<Vec<String>>()
.join(", "),
)?;
f.write(");")?;
f.writeln(&format!(
"private static {}_delegate {}_static_delegate = {}NativeAdapter.{}_cb;",
cb.name, cb.name, interface_name, cb.name
))?;
}
f.writeln("[UnmanagedFunctionPointer(CallingConvention.Cdecl)]")?; f.writeln(&format!(
"private delegate void {destroy_func_name}_delegate(IntPtr arg);"
))?;
f.writeln(&format!(
"private static {destroy_func_name}_delegate {destroy_func_name}_static_delegate = {interface_name}NativeAdapter.{destroy_func_name}_cb;"
))?;
f.newline()?;
for cb in &interface.untyped().callbacks {
f.writeln(&format!("private {}_delegate {};", cb.name, cb.name))?;
}
f.writeln(&format!(
"private {destroy_func_name}_delegate {destroy_func_name};"
))?;
f.writeln(&format!("public IntPtr {ctx_variable_name};"))?;
f.newline()?;
f.writeln(&format!(
"internal {interface_name}NativeAdapter({interface_name} impl)"
))?;
blocked(f, |f| {
f.writeln("var _handle = GCHandle.Alloc(impl);")?;
f.newline()?;
for cb in &interface.untyped().callbacks {
f.writeln(&format!(
"this.{} = {}NativeAdapter.{}_static_delegate;",
cb.name, interface_name, cb.name
))?;
f.newline()?;
}
f.writeln(&format!(
"this.{destroy_func_name} = {interface_name}NativeAdapter.{destroy_func_name}_static_delegate;"
))?;
f.writeln(&format!(
"this.{ctx_variable_name} = GCHandle.ToIntPtr(_handle);"
))?;
Ok(())
})?;
for cb in &interface.untyped().callbacks {
f.writeln(&format!(
"internal static {} {}_cb(",
cb.return_type.get_native_type(),
cb.name
))?;
f.write(
&cb.arguments
.iter()
.map(|arg| {
format!(
"{} {}",
arg.arg_type.get_native_type(),
arg.name.mixed_case()
)
})
.chain(std::iter::once(format!("IntPtr {ctx_variable_name}")))
.collect::<Vec<String>>()
.join(", "),
)?;
f.write(")")?;
blocked(f, |f| {
f.writeln(&format!(
"var _handle = GCHandle.FromIntPtr({ctx_variable_name});"
))?;
f.writeln(&format!("var _impl = ({interface_name})_handle.Target;"))?;
call_dotnet_function(f, cb, "return ")
})?;
f.newline()?;
}
f.writeln(&format!(
"internal static void {destroy_func_name}_cb(IntPtr arg)"
))?;
blocked(f, |f| {
f.writeln("var _handle = GCHandle.FromIntPtr(arg);")?;
f.writeln("_handle.Free();")
})?;
f.newline()?;
f.newline()?;
f.writeln(&format!(
"internal static {interface_name} FromNative(IntPtr self)"
))?;
blocked(f, |f| {
f.writeln("if (self != IntPtr.Zero)")?;
blocked(f, |f| {
f.writeln("var handle = GCHandle.FromIntPtr(self);")?;
f.writeln(&format!("return handle.Target as {interface_name};"))
})?;
f.writeln("else")?;
blocked(f, |f| f.writeln("return null;"))
})
})
})
}
pub(crate) fn generate_interface_implementation(
f: &mut dyn Printer,
interface: &Handle<Interface<Validated>>,
cb: &CallbackFunction<Validated>,
) -> FormattingResult<()> {
let functor_type = full_functor_type(cb);
f.writeln(&format!(
"internal class Implementation: I{}",
interface.name.camel_case()
))?;
blocked(f, |f| {
f.writeln(&format!("private readonly {functor_type} action;"))?;
f.newline()?;
f.writeln(&format!("internal Implementation({functor_type} action)"))?;
blocked(f, |f| f.writeln("this.action = action;"))?;
f.newline()?;
f.writeln(&format!(
"public {} {}(",
cb.return_type.get_dotnet_type(),
cb.name.camel_case()
))?;
f.write(
&cb.arguments
.iter()
.map(|param| {
format!(
"{} {}",
param.arg_type.get_dotnet_type(),
param.name.mixed_case()
)
})
.collect::<Vec<_>>()
.join(", "),
)?;
f.write(")")?;
blocked(f, |f| {
f.newline()?;
if !cb.return_type.is_none() {
f.write("return ")?;
}
let params = cb
.arguments
.iter()
.map(|param| param.name.mixed_case())
.collect::<Vec<_>>()
.join(", ");
f.write(&format!("this.action.Invoke({params});"))
})
})
}
pub(crate) fn generate_functional_helpers(
f: &mut dyn Printer,
interface: &Handle<Interface<Validated>>,
cb: &CallbackFunction<Validated>,
) -> FormattingResult<()> {
let interface_name = format!("I{}", interface.name.camel_case());
let class_name = interface.name.camel_case();
let functor_type = full_functor_type(cb);
let visibility = if cb.functional_transform.enabled() {
"internal"
} else {
"public"
};
documentation(f, |f| {
f.writeln("<summary>")?;
f.writeln(&format!(
"Provides a method to create an implementation of {interface_name} from a functor"
))?;
f.writeln("</summary>")
})?;
f.writeln(&format!("{visibility} static class {class_name}"))?;
blocked(f, |f| {
f.newline()?;
generate_interface_implementation(f, interface, cb)?;
f.newline()?;
documentation(f, |f| {
f.writeln("<summary>")?;
f.write(&format!(
"Creates an instance of {} which invokes a {}",
interface_name,
base_functor_type(cb)
))?;
f.write("</summary>")?;
f.newline()?;
f.writeln("<param name=\"action\">")?;
f.writeln("Callback to execute")?;
f.writeln("</param>")?;
f.writeln(&format!(
"<return>An implementation of {interface_name}</return>"
))?;
Ok(())
})?;
f.writeln(&format!(
"{visibility} static {interface_name} create({functor_type} action)"
))?;
blocked(f, |f| f.writeln("return new Implementation(action);"))?;
Ok(())
})
}