use crate::ir::{KnownSymbol, LibCall};
use alloc::boxed::Box;
use core::fmt::{self, Write};
use core::str::FromStr;
use cranelift_entity::EntityRef as _;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
use super::entities::UserExternalNameRef;
use super::function::FunctionParameters;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum UserFuncName {
User(UserExternalName),
Testcase(TestcaseName),
}
impl UserFuncName {
pub fn testcase<T: AsRef<[u8]>>(v: T) -> Self {
Self::Testcase(TestcaseName::new(v))
}
pub fn user(namespace: u32, index: u32) -> Self {
Self::User(UserExternalName::new(namespace, index))
}
}
impl Default for UserFuncName {
fn default() -> Self {
UserFuncName::User(UserExternalName::default())
}
}
impl fmt::Display for UserFuncName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UserFuncName::User(user) => user.fmt(f),
UserFuncName::Testcase(testcase) => testcase.fmt(f),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct UserExternalName {
pub namespace: u32,
pub index: u32,
}
impl UserExternalName {
pub fn new(namespace: u32, index: u32) -> Self {
Self { namespace, index }
}
}
impl fmt::Display for UserExternalName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "u{}:{}", self.namespace, self.index)
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct TestcaseName(Box<[u8]>);
impl fmt::Display for TestcaseName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_char('%')?;
f.write_str(std::str::from_utf8(&self.0).unwrap())
}
}
impl fmt::Debug for TestcaseName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self)
}
}
impl TestcaseName {
pub(crate) fn new<T: AsRef<[u8]>>(v: T) -> Self {
Self(v.as_ref().into())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum ExternalName {
User(UserExternalNameRef),
TestCase(TestcaseName),
LibCall(LibCall),
KnownSymbol(KnownSymbol),
}
impl Default for ExternalName {
fn default() -> Self {
Self::User(UserExternalNameRef::new(0))
}
}
impl ExternalName {
pub fn testcase<T: AsRef<[u8]>>(v: T) -> Self {
Self::TestCase(TestcaseName::new(v))
}
pub fn user(func_ref: UserExternalNameRef) -> Self {
Self::User(func_ref)
}
pub fn display<'a>(
&'a self,
params: Option<&'a FunctionParameters>,
) -> DisplayableExternalName<'a> {
DisplayableExternalName { name: self, params }
}
}
pub struct DisplayableExternalName<'a> {
name: &'a ExternalName,
params: Option<&'a FunctionParameters>,
}
impl<'a> fmt::Display for DisplayableExternalName<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.name {
ExternalName::User(func_ref) => {
if let Some(params) = self.params {
let name = ¶ms.user_named_funcs()[*func_ref];
write!(f, "u{}:{}", name.namespace, name.index)
} else {
write!(f, "{}", *func_ref)
}
}
ExternalName::TestCase(testcase) => testcase.fmt(f),
ExternalName::LibCall(lc) => write!(f, "%{}", lc),
ExternalName::KnownSymbol(ks) => write!(f, "%{}", ks),
}
}
}
impl FromStr for ExternalName {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(ks) = s.parse() {
return Ok(Self::KnownSymbol(ks));
}
if let Ok(lc) = s.parse() {
return Ok(Self::LibCall(lc));
}
Ok(Self::testcase(s.as_bytes()))
}
}
#[cfg(test)]
mod tests {
use super::ExternalName;
use crate::ir::{
entities::UserExternalNameRef, function::FunctionParameters, LibCall, UserExternalName,
};
use alloc::string::ToString;
use core::u32;
use cranelift_entity::EntityRef as _;
#[cfg(target_pointer_width = "64")]
#[test]
fn externalname_size() {
assert_eq!(core::mem::size_of::<ExternalName>(), 24);
}
#[test]
fn display_testcase() {
assert_eq!(ExternalName::testcase("").display(None).to_string(), "%");
assert_eq!(ExternalName::testcase("x").display(None).to_string(), "%x");
assert_eq!(
ExternalName::testcase("x_1").display(None).to_string(),
"%x_1"
);
assert_eq!(
ExternalName::testcase("longname12345678")
.display(None)
.to_string(),
"%longname12345678"
);
assert_eq!(
ExternalName::testcase("longname123456789")
.display(None)
.to_string(),
"%longname123456789"
);
}
#[test]
fn display_user() {
assert_eq!(
ExternalName::user(UserExternalNameRef::new(0))
.display(None)
.to_string(),
"userextname0"
);
assert_eq!(
ExternalName::user(UserExternalNameRef::new(1))
.display(None)
.to_string(),
"userextname1"
);
assert_eq!(
ExternalName::user(UserExternalNameRef::new((u32::MAX - 1) as _))
.display(None)
.to_string(),
"userextname4294967294"
);
let mut func_params = FunctionParameters::new();
func_params.ensure_user_func_name(UserExternalName {
namespace: 13,
index: 37,
});
func_params.ensure_user_func_name(UserExternalName {
namespace: 2,
index: 4,
});
assert_eq!(
ExternalName::user(UserExternalNameRef::new(0))
.display(Some(&func_params))
.to_string(),
"u13:37"
);
assert_eq!(
ExternalName::user(UserExternalNameRef::new(1))
.display(Some(&func_params))
.to_string(),
"u2:4"
);
}
#[test]
fn parsing() {
assert_eq!(
"FloorF32".parse(),
Ok(ExternalName::LibCall(LibCall::FloorF32))
);
assert_eq!(
ExternalName::LibCall(LibCall::FloorF32)
.display(None)
.to_string(),
"%FloorF32"
);
}
}