use crate::cpp_data::CppPath;
use crate::cpp_ffi_data::{CppFfiType, CppTypeConversionToFfi};
use ritual_common::errors::{bail, Result, ResultExt};
use serde_derive::{Deserialize, Serialize};
use std::hash::Hash;
use std::hash::Hasher;
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub enum CppPointerLikeTypeKind {
Pointer,
Reference,
RValueReference,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub enum CppBuiltInNumericType {
Bool,
Char,
SChar,
UChar,
WChar,
Char16,
Char32,
Short,
UShort,
Int,
UInt,
Long,
ULong,
LongLong,
ULongLong,
Int128,
UInt128,
Float,
Double,
LongDouble,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
#[allow(dead_code)]
pub enum CppSpecificNumericTypeKind {
Integer { is_signed: bool },
FloatingPoint,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub struct CppFunctionPointerType {
pub return_type: Box<CppType>,
pub arguments: Vec<CppType>,
pub allows_variadic_arguments: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CppSpecificNumericType {
pub path: CppPath,
pub bits: usize,
pub kind: CppSpecificNumericTypeKind,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub enum CppType {
Void,
BuiltInNumeric(CppBuiltInNumericType),
SpecificNumeric(CppSpecificNumericType),
PointerSizedInteger { path: CppPath, is_signed: bool },
Enum {
path: CppPath,
},
Class(CppPath),
TemplateParameter {
nested_level: usize,
index: usize,
name: String,
},
FunctionPointer(CppFunctionPointerType),
PointerLike {
kind: CppPointerLikeTypeKind,
is_const: bool,
target: Box<CppType>,
},
}
impl CppBuiltInNumericType {
pub fn to_cpp_code(&self) -> &'static str {
use self::CppBuiltInNumericType::*;
match *self {
Bool => "bool",
Char => "char",
SChar => "signed char",
UChar => "unsigned char",
WChar => "wchar_t",
Char16 => "char16_t",
Char32 => "char32_t",
Short => "short",
UShort => "unsigned short",
Int => "int",
UInt => "unsigned int",
Long => "long",
ULong => "unsigned long",
LongLong => "long long",
ULongLong => "unsigned long long",
Int128 => "__int128_t",
UInt128 => "__uint128_t",
Float => "float",
Double => "double",
LongDouble => "long double",
}
}
pub fn is_float(&self) -> bool {
use self::CppBuiltInNumericType::*;
match *self {
Float | Double | LongDouble => true,
_ => false,
}
}
pub fn is_signed_integer(&self) -> bool {
use self::CppBuiltInNumericType::*;
match *self {
SChar | Short | Int | Long | LongLong | Int128 => true,
_ => false,
}
}
pub fn is_unsigned_integer(&self) -> bool {
use self::CppBuiltInNumericType::*;
match *self {
UChar | Char16 | Char32 | UShort | UInt | ULong | ULongLong | UInt128 => true,
_ => false,
}
}
pub fn is_integer_with_undefined_signedness(&self) -> bool {
use self::CppBuiltInNumericType::*;
match *self {
Char | WChar => true,
_ => false,
}
}
pub fn all() -> &'static [CppBuiltInNumericType] {
use self::CppBuiltInNumericType::*;
&[
Bool, Char, SChar, UChar, WChar, Char16, Char32, Short, UShort, Int, UInt, Long, ULong,
LongLong, ULongLong, Int128, UInt128, Float, Double, LongDouble,
]
}
}
impl CppPath {
pub fn instantiate(
&self,
nested_level1: usize,
template_arguments1: &[CppType],
) -> Result<CppPath> {
let mut new_path = self.clone();
for path_item in &mut new_path.items {
if let Some(ref mut template_arguments) = path_item.template_arguments {
for arg in template_arguments {
*arg = arg.instantiate(nested_level1, template_arguments1)?;
}
}
}
Ok(new_path)
}
}
impl CppType {
pub fn new_pointer(is_const: bool, target: CppType) -> Self {
CppType::PointerLike {
kind: CppPointerLikeTypeKind::Pointer,
is_const,
target: Box::new(target),
}
}
pub fn new_reference(is_const: bool, target: CppType) -> Self {
CppType::PointerLike {
kind: CppPointerLikeTypeKind::Reference,
is_const,
target: Box::new(target),
}
}
#[allow(dead_code)]
pub fn is_void(&self) -> bool {
match *self {
CppType::Void => true,
_ => false,
}
}
pub fn is_class(&self) -> bool {
match *self {
CppType::Class(..) => true,
_ => false,
}
}
pub fn is_template_parameter(&self) -> bool {
match *self {
CppType::TemplateParameter { .. } => true,
_ => false,
}
}
pub fn is_function_pointer(&self) -> bool {
match *self {
CppType::FunctionPointer(..) => true,
_ => false,
}
}
pub fn is_or_contains_template_parameter(&self) -> bool {
match *self {
CppType::TemplateParameter { .. } => true,
CppType::PointerLike { ref target, .. } => target.is_or_contains_template_parameter(),
CppType::FunctionPointer(ref type1) => {
type1.return_type.is_or_contains_template_parameter()
|| type1
.arguments
.iter()
.any(|arg| arg.is_or_contains_template_parameter())
}
CppType::Class(ref path) => path.items.iter().any(|item| {
if let Some(ref template_arguments) = item.template_arguments {
template_arguments
.iter()
.any(|arg| arg.is_or_contains_template_parameter())
} else {
false
}
}),
_ => false,
}
}
pub fn to_cpp_code(&self, function_pointer_inner_text: Option<&str>) -> Result<String> {
if !self.is_function_pointer() && function_pointer_inner_text.is_some() {
bail!("unexpected function_pointer_inner_text");
}
match *self {
CppType::Void => Ok("void".to_string()),
CppType::BuiltInNumeric(ref t) => Ok(t.to_cpp_code().to_string()),
CppType::Enum { ref path }
| CppType::SpecificNumeric(CppSpecificNumericType { ref path, .. })
| CppType::PointerSizedInteger { ref path, .. } => path.to_cpp_code(),
CppType::Class(ref path) => path.to_cpp_code(),
CppType::TemplateParameter { .. } => {
bail!("template parameters are not allowed in C++ code generator");
}
CppType::FunctionPointer(CppFunctionPointerType {
ref return_type,
ref arguments,
ref allows_variadic_arguments,
}) => {
if *allows_variadic_arguments {
bail!("function pointers with variadic arguments are not supported");
}
let mut arg_texts = Vec::new();
for arg in arguments {
arg_texts.push(arg.to_cpp_code(None)?);
}
if let Some(function_pointer_inner_text) = function_pointer_inner_text {
Ok(format!(
"{} (*{})({})",
return_type.as_ref().to_cpp_code(None)?,
function_pointer_inner_text,
arg_texts.join(", ")
))
} else {
bail!("function_pointer_inner_text argument is missing");
}
}
CppType::PointerLike {
ref kind,
ref is_const,
ref target,
} => Ok(format!(
"{}{}{}",
if *is_const { "const " } else { "" },
target.to_cpp_code(function_pointer_inner_text)?,
match *kind {
CppPointerLikeTypeKind::Pointer => "*",
CppPointerLikeTypeKind::Reference => "&",
CppPointerLikeTypeKind::RValueReference => "&&",
}
)),
}
}
pub fn to_cpp_pseudo_code(&self) -> String {
match *self {
CppType::TemplateParameter { ref name, .. } => {
return name.to_string(); }
CppType::Class(ref base) => return base.to_cpp_pseudo_code(),
CppType::FunctionPointer(..) => {
return self
.to_cpp_code(Some(&"FN_PTR".to_string()))
.unwrap_or_else(|_| "[?]".to_string())
}
_ => {}
};
self.to_cpp_code(None).unwrap_or_else(|_| "[?]".to_string())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CppTypeRole {
ReturnType,
NotReturnType,
}
pub fn is_qflags(path: &CppPath) -> bool {
path.items.len() == 1 && &path.items[0].name == "QFlags"
}
impl CppType {
fn contains_reference(&self) -> bool {
if let CppType::PointerLike {
ref kind,
ref target,
..
} = *self
{
match *kind {
CppPointerLikeTypeKind::Pointer => target.contains_reference(),
CppPointerLikeTypeKind::Reference | CppPointerLikeTypeKind::RValueReference => true,
}
} else {
false
}
}
#[allow(clippy::collapsible_if)]
pub fn to_cpp_ffi_type(&self, role: CppTypeRole) -> Result<CppFfiType> {
let inner = || -> Result<CppFfiType> {
if self.is_or_contains_template_parameter() {
bail!("template parameters cannot be expressed in FFI");
}
match self {
CppType::FunctionPointer(CppFunctionPointerType {
ref return_type,
ref arguments,
ref allows_variadic_arguments,
}) => {
if *allows_variadic_arguments {
bail!("function pointers with variadic arguments are not supported");
}
let mut all_types: Vec<&CppType> = arguments.iter().collect();
all_types.push(return_type.as_ref());
for arg in all_types {
match *arg {
CppType::FunctionPointer(..) => {
bail!(
"function pointers containing nested function pointers are \
not supported"
);
}
CppType::Class(..) => {
bail!(
"Function pointers containing classes by value are not \
supported"
);
}
_ => {}
}
if arg.contains_reference() {
bail!("Function pointers containing references are not supported");
}
}
return Ok(CppFfiType {
ffi_type: self.clone(),
conversion: CppTypeConversionToFfi::NoChange,
original_type: self.clone(),
});
}
CppType::Class(ref path) => {
if is_qflags(&path) {
return Ok(CppFfiType {
ffi_type: CppType::BuiltInNumeric(CppBuiltInNumericType::UInt),
conversion: CppTypeConversionToFfi::QFlagsToUInt,
original_type: self.clone(),
});
} else {
return Ok(CppFfiType {
ffi_type: CppType::PointerLike {
is_const: role != CppTypeRole::ReturnType,
kind: CppPointerLikeTypeKind::Pointer,
target: Box::new(self.clone()),
},
conversion: CppTypeConversionToFfi::ValueToPointer,
original_type: self.clone(),
});
}
}
CppType::PointerLike {
ref kind,
ref is_const,
ref target,
} => {
match *kind {
CppPointerLikeTypeKind::Pointer => {}
CppPointerLikeTypeKind::Reference => {
if *is_const {
if let CppType::Class(ref path) = **target {
if is_qflags(path) {
return Ok(CppFfiType {
ffi_type: CppType::BuiltInNumeric(
CppBuiltInNumericType::UInt,
),
conversion: CppTypeConversionToFfi::QFlagsToUInt,
original_type: self.clone(),
});
}
}
}
return Ok(CppFfiType {
ffi_type: CppType::PointerLike {
is_const: *is_const,
kind: CppPointerLikeTypeKind::Pointer,
target: target.clone(),
},
conversion: CppTypeConversionToFfi::ReferenceToPointer,
original_type: self.clone(),
});
}
CppPointerLikeTypeKind::RValueReference => {
bail!("rvalue references are not supported");
}
}
}
_ => {}
}
Ok(CppFfiType {
ffi_type: self.clone(),
conversion: CppTypeConversionToFfi::NoChange,
original_type: self.clone(),
})
};
Ok(inner().with_context(|_| format!("Can't express type to FFI: {:?}", self))?)
}
#[allow(clippy::if_not_else)]
pub fn instantiate(
&self,
nested_level1: usize,
template_arguments1: &[CppType],
) -> Result<CppType> {
match self {
CppType::TemplateParameter {
nested_level,
index,
..
} => {
if *nested_level == nested_level1 {
if *index >= template_arguments1.len() {
bail!("not enough template arguments");
}
Ok(template_arguments1[*index].clone())
} else {
Ok(self.clone())
}
}
CppType::Class(ref type1) => Ok(CppType::Class(
type1.instantiate(nested_level1, template_arguments1)?,
)),
CppType::PointerLike {
ref kind,
ref is_const,
ref target,
} => Ok(CppType::PointerLike {
kind: kind.clone(),
is_const: *is_const,
target: Box::new(target.instantiate(nested_level1, template_arguments1)?),
}),
_ => Ok(self.clone()),
}
}
}
impl PartialEq for CppSpecificNumericType {
fn eq(&self, other: &CppSpecificNumericType) -> bool {
self.bits == other.bits && self.kind == other.kind
}
}
impl Eq for CppSpecificNumericType {}
impl Hash for CppSpecificNumericType {
fn hash<H: Hasher>(&self, state: &mut H) {
self.bits.hash(state);
self.kind.hash(state);
}
}