use super::super::super::ast::{
CSharpArgumentList, CSharpClassName, CSharpComment, CSharpExpression, CSharpIdentity,
CSharpLocalName, CSharpMethodName, CSharpParamName, CSharpParameter, CSharpParameterList,
CSharpPropertyName, CSharpType, CSharpTypeReference,
};
use super::super::CFunctionName;
use super::callable_plan::CSharpCallablePlan;
use super::param::{native_call_arg_list, native_param_list};
use super::{CSharpAsyncCallPlan, CSharpParamPlan, CSharpReturnKind, CSharpWireWriterPlan};
#[derive(Debug, Clone)]
pub struct CSharpMethodPlan {
pub summary_doc: Option<CSharpComment>,
pub name: CSharpMethodName,
pub native_method_name: CSharpMethodName,
pub ffi_name: CFunctionName,
pub async_call: Option<CSharpAsyncCallPlan>,
pub receiver: CSharpReceiver,
pub params: Vec<CSharpParamPlan>,
pub return_type: CSharpType,
pub return_kind: CSharpReturnKind,
pub wire_writers: Vec<CSharpWireWriterPlan>,
pub owner_is_blittable: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CSharpReceiver {
Static,
InstanceExtension,
InstanceNative,
ClassInstance,
}
impl CSharpReceiver {
pub fn is_static(&self) -> bool {
matches!(self, Self::Static)
}
pub fn is_instance_extension(&self) -> bool {
matches!(self, Self::InstanceExtension)
}
pub fn is_class_instance(&self) -> bool {
matches!(self, Self::ClassInstance)
}
}
impl CSharpMethodPlan {
pub fn has_pinned_params(&self) -> bool {
self.params.iter().any(CSharpParamPlan::is_pinned)
}
pub fn native_param_list(
&self,
owner_class_name: &CSharpClassName,
owner_is_blittable: bool,
) -> CSharpParameterList {
let mut list = CSharpParameterList::empty();
match self.receiver {
CSharpReceiver::Static => {}
CSharpReceiver::InstanceExtension => {
list.push(self_param(CSharpType::CStyleEnum(
CSharpTypeReference::Plain(owner_class_name.clone()),
)));
}
CSharpReceiver::InstanceNative if owner_is_blittable => {
list.push(self_param(CSharpType::Record(CSharpTypeReference::Plain(
owner_class_name.clone(),
))));
}
CSharpReceiver::InstanceNative => {
list.push(CSharpParameter::bare(
CSharpType::Array(Box::new(CSharpType::Byte)),
CSharpParamName::new("self"),
));
list.push(CSharpParameter::bare(
CSharpType::UIntPtr,
CSharpParamName::new("selfLen"),
));
}
CSharpReceiver::ClassInstance => {
list.push(self_param(CSharpType::IntPtr));
}
}
list.extend(native_param_list(&self.params));
list
}
}
fn self_param(csharp_type: CSharpType) -> CSharpParameter {
CSharpParameter::bare(csharp_type, CSharpParamName::new("self"))
}
impl CSharpMethodPlan {
pub fn full_native_call_args(&self) -> CSharpArgumentList {
let mut list = CSharpArgumentList::empty();
match self.receiver {
CSharpReceiver::Static => {}
CSharpReceiver::InstanceExtension => {
list.push(local_ident("self"));
}
CSharpReceiver::InstanceNative if self.owner_is_blittable => {
list.push(CSharpExpression::Identity(CSharpIdentity::This));
}
CSharpReceiver::InstanceNative => {
let buf = local_ident("_selfBytes");
list.push(buf.clone());
list.push(uintptr_length_member(buf));
}
CSharpReceiver::ClassInstance => {
list.push(local_ident("_handle"));
}
}
list.extend(native_call_arg_list(&self.params));
list
}
pub fn full_native_call_args_async(&self) -> CSharpArgumentList {
let mut list = CSharpArgumentList::empty();
match self.receiver {
CSharpReceiver::Static => {}
CSharpReceiver::InstanceExtension => {
list.push(local_ident("self"));
}
CSharpReceiver::InstanceNative if self.owner_is_blittable => {
list.push(local_ident("_self"));
}
CSharpReceiver::InstanceNative => {
let buf = local_ident("_selfBytes");
list.push(buf.clone());
list.push(uintptr_length_member(buf));
}
CSharpReceiver::ClassInstance => {
list.push(CSharpExpression::Identity(CSharpIdentity::Local(
CSharpLocalName::new("_handle"),
)));
}
}
list.extend(native_call_arg_list(&self.params));
list
}
}
impl CSharpCallablePlan for CSharpMethodPlan {
fn async_call(&self) -> Option<&CSharpAsyncCallPlan> {
self.async_call.as_ref()
}
fn return_type(&self) -> &CSharpType {
&self.return_type
}
fn return_kind(&self) -> &CSharpReturnKind {
&self.return_kind
}
}
fn local_ident(name: &str) -> CSharpExpression {
CSharpExpression::Identity(CSharpIdentity::Local(CSharpLocalName::new(name)))
}
fn uintptr_length_member(receiver: CSharpExpression) -> CSharpExpression {
CSharpExpression::Cast {
target: CSharpType::UIntPtr,
inner: Box::new(CSharpExpression::MemberAccess {
receiver: Box::new(receiver),
name: CSharpPropertyName::from_source("length"),
}),
}
}
#[cfg(test)]
mod tests {
use super::super::super::super::ast::{
CSharpClassName, CSharpMethodName, CSharpParamName, CSharpType,
};
use super::super::CSharpParamKind;
use super::*;
fn method(receiver: CSharpReceiver) -> CSharpMethodPlan {
method_with_owner(receiver, false)
}
fn method_with_owner(receiver: CSharpReceiver, owner_is_blittable: bool) -> CSharpMethodPlan {
CSharpMethodPlan {
summary_doc: None,
name: CSharpMethodName::from_source("test"),
native_method_name: CSharpMethodName::from_source("OwnerTest"),
ffi_name: CFunctionName::new("boltffi_test".to_string()),
async_call: None,
receiver,
params: vec![CSharpParamPlan {
name: CSharpParamName::from_source("count"),
csharp_type: CSharpType::Int,
kind: CSharpParamKind::Direct,
}],
return_type: CSharpType::Void,
return_kind: CSharpReturnKind::Void,
wire_writers: vec![],
owner_is_blittable,
}
}
#[test]
fn native_param_list_static_has_no_self() {
let m = method(CSharpReceiver::Static);
let owner = CSharpClassName::from_source("shape");
assert_eq!(m.native_param_list(&owner, false).to_string(), "int count",);
}
#[test]
fn native_param_list_instance_extension_prepends_enum_self() {
let m = method(CSharpReceiver::InstanceExtension);
let owner = CSharpClassName::from_source("direction");
assert_eq!(
m.native_param_list(&owner, false).to_string(),
"Direction self, int count",
);
}
#[test]
fn native_param_list_instance_native_blittable_prepends_record_self() {
let m = method(CSharpReceiver::InstanceNative);
let owner = CSharpClassName::from_source("point");
assert_eq!(
m.native_param_list(&owner, true).to_string(),
"Point self, int count",
);
}
#[test]
fn native_param_list_instance_native_wire_encoded_prepends_byte_buffer_self() {
let m = method(CSharpReceiver::InstanceNative);
let owner = CSharpClassName::from_source("shape");
assert_eq!(
m.native_param_list(&owner, false).to_string(),
"byte[] self, UIntPtr selfLen, int count",
);
}
#[test]
fn full_native_call_args_instance_native_blittable_passes_this() {
let m = method_with_owner(CSharpReceiver::InstanceNative, true);
assert_eq!(m.full_native_call_args().to_string(), "this, count",);
}
#[test]
fn full_native_call_args_instance_native_wire_passes_selfbytes_pair() {
let m = method_with_owner(CSharpReceiver::InstanceNative, false);
assert_eq!(
m.full_native_call_args().to_string(),
"_selfBytes, (UIntPtr)_selfBytes.Length, count",
);
}
}