use cpp_type::{CppType, CppTypeIndirection, CppTypeRole, CppTypeBase, CppTypeClassBase};
use cpp_ffi_data::CppFfiType;
use cpp_ffi_data::CppFfiFunctionSignature;
use cpp_ffi_data::{CppFfiFunctionArgument, CppFfiArgumentMeaning};
use cpp_ffi_data::CppMethodWithFfiSignature;
use cpp_data::CppVisibility;
use utils::JoinWithString;
pub use serializable::{CppFunctionArgument, CppMethodKind, CppMethod, CppMethodClassMembership,
CppMethodInheritedFrom};
use cpp_operator::CppOperator;
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ReturnValueAllocationPlace {
Stack,
Heap,
NotApplicable,
}
fn type_to_cpp_code_permissive(type1: &CppType) -> String {
let r = match type1.base {
CppTypeBase::TemplateParameter { ref nested_level, ref index } => {
let mut fake_type = type1.clone();
fake_type.base = CppTypeBase::Class(CppTypeClassBase {
name: format!("T_{}_{}", nested_level, index),
template_arguments: None,
});
fake_type.to_cpp_code(None)
}
CppTypeBase::Class(CppTypeClassBase { ref name, ref template_arguments }) => {
if let &Some(ref template_arguments) = template_arguments {
Ok(format!("{}<{}>",
name,
template_arguments.iter()
.map(|x| type_to_cpp_code_permissive(x))
.join(", ")))
} else {
type1.to_cpp_code(None)
}
}
CppTypeBase::FunctionPointer { .. } => type1.to_cpp_code(Some(&"FN_PTR".to_string())),
_ => type1.to_cpp_code(None),
};
r.unwrap_or_else(|_| "[?]".to_string())
}
impl CppMethodKind {
pub fn is_constructor(&self) -> bool {
match *self {
CppMethodKind::Constructor => true,
_ => false,
}
}
pub fn is_destructor(&self) -> bool {
match *self {
CppMethodKind::Destructor => true,
_ => false,
}
}
#[allow(dead_code)]
pub fn is_regular(&self) -> bool {
match *self {
CppMethodKind::Regular => true,
_ => false,
}
}
}
impl CppMethod {
pub fn argument_types_equal(&self, other: &CppMethod) -> bool {
if self.arguments.len() != other.arguments.len() {
return false;
}
if self.allows_variadic_arguments != other.allows_variadic_arguments {
return false;
}
for i in 0..self.arguments.len() {
if self.arguments.get(i).unwrap().argument_type !=
other.arguments.get(i).unwrap().argument_type {
return false;
}
}
true
}
pub fn needs_allocation_place_variants(&self) -> bool {
if self.is_constructor() || self.is_destructor() {
return true;
}
if self.return_type.needs_allocation_place_variants() {
return true;
}
false
}
pub fn c_signature(&self,
allocation_place: ReturnValueAllocationPlace)
-> Result<CppFfiFunctionSignature, String> {
if self.allows_variadic_arguments {
return Err("Variable arguments are not supported".to_string());
}
let mut r = CppFfiFunctionSignature {
arguments: Vec::new(),
return_type: CppFfiType::void(),
};
if let Some(ref info) = self.class_membership {
if !info.is_static && info.kind != CppMethodKind::Constructor {
r.arguments.push(CppFfiFunctionArgument {
name: "this_ptr".to_string(),
argument_type: CppType {
base: CppTypeBase::Class(info.class_type.clone()),
is_const: info.is_const,
indirection: CppTypeIndirection::Ptr,
}
.to_cpp_ffi_type(CppTypeRole::NotReturnType)
.unwrap(),
meaning: CppFfiArgumentMeaning::This,
});
}
}
for (index, arg) in self.arguments.iter().enumerate() {
match arg.argument_type.to_cpp_ffi_type(CppTypeRole::NotReturnType) {
Ok(c_type) => {
r.arguments.push(CppFfiFunctionArgument {
name: arg.name.clone(),
argument_type: c_type,
meaning: CppFfiArgumentMeaning::Argument(index as i8),
});
}
Err(msg) => {
return Err(format!("Can't convert type to C: {:?}: {}", arg.argument_type, msg));
}
}
}
let real_return_type = if self.is_constructor() {
CppType {
is_const: false,
indirection: CppTypeIndirection::None,
base: CppTypeBase::Class(self.class_membership.as_ref().unwrap().class_type.clone()),
}
} else {
self.return_type.clone()
};
match real_return_type.to_cpp_ffi_type(CppTypeRole::ReturnType) {
Ok(c_type) => {
if real_return_type.needs_allocation_place_variants() {
match allocation_place {
ReturnValueAllocationPlace::Stack => {
r.arguments.push(CppFfiFunctionArgument {
name: "output".to_string(),
argument_type: c_type,
meaning: CppFfiArgumentMeaning::ReturnValue,
});
}
ReturnValueAllocationPlace::Heap => {
r.return_type = c_type;
}
ReturnValueAllocationPlace::NotApplicable => {
panic!("NotApplicable encountered but return value needs allocation_place variants")
}
}
} else {
r.return_type = c_type;
}
}
Err(msg) => {
return Err(format!("Can't convert type to C: {:?}: {}", real_return_type, msg));
}
}
Ok(r)
}
pub fn to_ffi_signatures(&self) -> Result<Vec<CppMethodWithFfiSignature>, String> {
let places = if self.needs_allocation_place_variants() {
vec![ReturnValueAllocationPlace::Heap, ReturnValueAllocationPlace::Stack]
} else {
vec![ReturnValueAllocationPlace::NotApplicable]
};
let mut results = Vec::new();
for place in places {
let c_signature = try!(self.c_signature(place.clone()));
results.push(CppMethodWithFfiSignature {
cpp_method: self.clone(),
allocation_place: place,
c_signature: c_signature,
});
}
Ok(results)
}
pub fn full_name(&self) -> String {
if let Some(ref info) = self.class_membership {
format!("{}::{}",
type_to_cpp_code_permissive(&CppType {
indirection: CppTypeIndirection::None,
is_const: false,
base: CppTypeBase::Class(info.class_type.clone()),
}),
self.name)
} else {
self.name.clone()
}
}
pub fn doc_id(&self) -> String {
if let Some(ref info) = self.class_membership {
format!("{}::{}", info.class_type.name, self.name)
} else {
self.name.clone()
}
}
pub fn short_text(&self) -> String {
let mut s = String::new();
if let Some(ref info) = self.class_membership {
if info.is_virtual {
if info.is_pure_virtual {
s = format!("{} pure virtual", s);
} else {
s = format!("{} virtual", s);
}
}
if info.is_static {
s = format!("{} static", s);
}
if info.visibility == CppVisibility::Protected {
s = format!("{} protected", s);
}
if info.visibility == CppVisibility::Private {
s = format!("{} private", s);
}
if info.is_signal {
s = format!("{} [signal]", s);
}
match info.kind {
CppMethodKind::Constructor => s = format!("{} [constructor]", s),
CppMethodKind::Destructor => s = format!("{} [destructor]", s),
CppMethodKind::Regular => {}
}
}
if self.allows_variadic_arguments {
s = format!("{} [var args]", s);
}
s = format!("{} {}", s, type_to_cpp_code_permissive(&self.return_type));
s = format!("{} {}", s, self.full_name());
s = format!("{}({})",
s,
self.arguments
.iter()
.map(|arg| {
format!("{} {}{}",
type_to_cpp_code_permissive(&arg.argument_type),
arg.name,
if arg.has_default_value {
format!(" = ?")
} else {
String::new()
})
})
.join(", "));
if let Some(ref info) = self.class_membership {
if info.is_const {
s = format!("{} const", s);
}
}
s.trim().to_string()
}
pub fn class_name(&self) -> Option<&String> {
match self.class_membership {
Some(ref info) => Some(&info.class_type.name),
None => None,
}
}
pub fn is_constructor(&self) -> bool {
match self.class_membership {
Some(ref info) => info.kind.is_constructor(),
None => false,
}
}
pub fn is_destructor(&self) -> bool {
match self.class_membership {
Some(ref info) => info.kind.is_destructor(),
None => false,
}
}
pub fn is_operator(&self) -> bool {
self.operator.is_some()
}
pub fn all_involved_types(&self) -> Vec<CppType> {
let mut result: Vec<CppType> = Vec::new();
if let Some(ref class_membership) = self.class_membership {
result.push(CppType {
base: CppTypeBase::Class(class_membership.class_type.clone()),
is_const: class_membership.is_const,
indirection: CppTypeIndirection::Ptr,
});
}
for t in self.arguments.iter().map(|x| x.argument_type.clone()) {
result.push(t);
}
result.push(self.return_type.clone());
if let Some(ref operator) = self.operator {
if let &CppOperator::Conversion(ref cpp_type) = operator {
result.push(cpp_type.clone());
}
}
result
}
}