use sha2::{Digest, Sha256};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SwiftEvidenceDigest([u8; 32]);
impl SwiftEvidenceDigest {
#[must_use]
pub fn of(bytes: impl AsRef<[u8]>) -> Self {
Self(Sha256::digest(bytes.as_ref()).into())
}
#[must_use]
pub const fn into_bytes(self) -> [u8; 32] {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SwiftCallableEvidenceLimits {
pub max_mangling_bytes: u64,
pub max_mangling_nodes: u64,
pub max_identifier_bytes: u64,
pub max_context_depth: u64,
pub max_type_ast_depth: u64,
pub max_type_ast_nodes: u64,
}
impl SwiftCallableEvidenceLimits {
pub(super) fn validate(self) -> Result<(), String> {
if self.max_mangling_bytes == 0
|| self.max_mangling_nodes == 0
|| self.max_identifier_bytes == 0
|| self.max_context_depth == 0
|| self.max_type_ast_depth == 0
|| self.max_type_ast_nodes == 0
{
return Err("Swift callable evidence limits must all be nonzero".into());
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SwiftManglingScheme {
StableSwift,
EmbeddedSwift,
LegacySwift,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SwiftManglingGap {
UnsupportedScheme,
UnsupportedNode,
UnsupportedRepresentation,
UnsupportedRequirement,
UnsupportedBuiltin,
ProfileMismatch,
TypeAstDepthExceeded,
TypeAstNodesExceeded,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SwiftTypeDeclarationKind {
Class,
Struct,
Enum,
Protocol,
TypeAlias,
Opaque,
ObjcClass,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SwiftDeclarationPathComponent {
Identifier {
value: String,
},
PrivateContext {
discriminator_sha256: SwiftEvidenceDigest,
},
LocalContext {
discriminator_sha256: SwiftEvidenceDigest,
},
ExtensionContext {
defining_module: String,
extended_declaration_sha256: SwiftEvidenceDigest,
},
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SwiftTypeDeclaration {
pub module: String,
pub declaration_path: Vec<SwiftDeclarationPathComponent>,
pub kind: SwiftTypeDeclarationKind,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SwiftCallableKind {
Function,
InstanceMethod,
StaticMethod,
ClassMethod,
Initializer,
Allocator,
Deinitializer,
SubscriptGet,
SubscriptSet,
PropertyGet,
PropertySet,
PropertyRead,
PropertyModify,
Closure,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SwiftCallableVariantRole {
DirectEntry,
ClassVtableEntry,
ProtocolWitnessEntry,
DispatchThunk,
ReabstractionThunk,
Specialization,
Prespecialization,
AsyncEntry,
AsyncResume,
CoroutineEntry,
CoroutineResume,
DestroyingDeallocator,
DeallocatingDeallocator,
DynamicReplacement,
MetadataAccessor,
WitnessAccessor,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SwiftFunctionRepresentation {
Thin,
Thick,
Method,
WitnessMethod,
CFunction,
Block,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SwiftMetatypeRepresentation {
Thick,
Thin,
Objc,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SwiftTupleElement {
pub label: Option<String>,
pub r#type: SwiftTypeEvidence,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SwiftFormalParameter {
pub label: Option<String>,
pub r#type: SwiftTypeEvidence,
pub variadic: bool,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SwiftTypeEvidence {
Nominal {
declaration: SwiftTypeDeclaration,
arguments: Vec<Self>,
},
GenericParameter {
depth: u64,
index: u64,
},
DependentMember {
base: Box<Self>,
member: String,
protocol: Option<SwiftTypeDeclaration>,
},
Tuple {
elements: Vec<SwiftTupleElement>,
},
Function {
representation: SwiftFunctionRepresentation,
parameters: Vec<SwiftFormalParameter>,
result: Box<Self>,
r#async: bool,
throwing: bool,
},
Metatype {
representation: SwiftMetatypeRepresentation,
instance: Box<Self>,
},
Existential {
protocols: Vec<SwiftTypeDeclaration>,
superclass: Option<Box<Self>>,
class_constraint: bool,
},
Inout {
value: Box<Self>,
},
Owned {
value: Box<Self>,
},
Shared {
value: Box<Self>,
},
Pack {
elements: Vec<Self>,
},
PackExpansion {
pattern: Box<Self>,
},
Builtin {
profile_atom: String,
},
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SwiftFormalTypeEvidence {
pub representation: SwiftFunctionRepresentation,
pub parameters: Vec<SwiftFormalParameter>,
pub result: SwiftTypeEvidence,
pub r#async: bool,
pub throwing: bool,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SwiftGenericRequirementEvidence {
Conformance {
subject: SwiftTypeEvidence,
protocol: SwiftTypeDeclaration,
},
SameType {
left: SwiftTypeEvidence,
right: SwiftTypeEvidence,
},
Superclass {
subject: SwiftTypeEvidence,
superclass: SwiftTypeEvidence,
},
SameShape {
left: SwiftTypeEvidence,
right: SwiftTypeEvidence,
},
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SwiftSpecializationEvidence {
pub substitutions: Vec<SwiftTypeEvidence>,
pub pass_id: Option<String>,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SwiftMangledEntityEvidence {
pub module: String,
pub declaration_path: Vec<SwiftDeclarationPathComponent>,
pub declaration: Option<SwiftTypeDeclaration>,
pub callable_kind: Option<SwiftCallableKind>,
pub base_name: Option<String>,
pub formal_type: Option<SwiftFormalTypeEvidence>,
pub generic_requirements: Vec<SwiftGenericRequirementEvidence>,
pub variant_role: Option<SwiftCallableVariantRole>,
pub specialization: Option<SwiftSpecializationEvidence>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SwiftManglingEvidence {
Supported {
raw: Vec<u8>,
scheme: SwiftManglingScheme,
entity: Box<SwiftMangledEntityEvidence>,
display: String,
},
Unsupported {
raw: Vec<u8>,
reason: SwiftManglingGap,
safe_detail: String,
},
Malformed {
raw: Vec<u8>,
diagnostic: String,
},
}
pub(super) fn validate_entity(
entity: &SwiftMangledEntityEvidence,
limits: &SwiftCallableEvidenceLimits,
) -> Result<(), String> {
limits.validate()?;
if entity.module.is_empty() || entity.module.len() as u64 > limits.max_identifier_bytes {
return Err("Swift mangling module is empty or oversized".into());
}
if entity.declaration_path.len() as u64 > limits.max_context_depth {
return Err("Swift mangling context depth exceeds the selected limit".into());
}
let mut nodes = 0_u64;
if let Some(formal) = &entity.formal_type {
for parameter in &formal.parameters {
validate_type(¶meter.r#type, 1, limits, &mut nodes)?;
}
validate_type(&formal.result, 1, limits, &mut nodes)?;
}
Ok(())
}
fn validate_type(
value: &SwiftTypeEvidence,
depth: u64,
limits: &SwiftCallableEvidenceLimits,
nodes: &mut u64,
) -> Result<(), String> {
*nodes = nodes
.checked_add(1)
.ok_or_else(|| "Swift type AST node count overflowed".to_string())?;
if depth > limits.max_type_ast_depth || *nodes > limits.max_type_ast_nodes {
return Err("Swift type AST structural limit exceeded".into());
}
match value {
SwiftTypeEvidence::Nominal { arguments, .. }
| SwiftTypeEvidence::Pack {
elements: arguments,
} => {
for child in arguments {
validate_type(child, depth + 1, limits, nodes)?;
}
}
SwiftTypeEvidence::DependentMember { base, .. }
| SwiftTypeEvidence::Metatype { instance: base, .. }
| SwiftTypeEvidence::Inout { value: base }
| SwiftTypeEvidence::Owned { value: base }
| SwiftTypeEvidence::Shared { value: base }
| SwiftTypeEvidence::PackExpansion { pattern: base } => {
validate_type(base, depth + 1, limits, nodes)?;
}
SwiftTypeEvidence::Tuple { elements } => {
for element in elements {
validate_type(&element.r#type, depth + 1, limits, nodes)?;
}
}
SwiftTypeEvidence::Function {
parameters, result, ..
} => {
for parameter in parameters {
validate_type(¶meter.r#type, depth + 1, limits, nodes)?;
}
validate_type(result, depth + 1, limits, nodes)?;
}
SwiftTypeEvidence::Existential { superclass, .. } => {
if let Some(superclass) = superclass {
validate_type(superclass, depth + 1, limits, nodes)?;
}
}
SwiftTypeEvidence::GenericParameter { .. } | SwiftTypeEvidence::Builtin { .. } => {}
}
Ok(())
}
#[must_use]
pub fn swift_mangling_scheme(raw: &str) -> Option<(SwiftManglingScheme, &str)> {
let value = raw.strip_prefix('_').unwrap_or(raw);
if value.starts_with("$s") || value.starts_with("$S") {
Some((SwiftManglingScheme::StableSwift, value))
} else if value.starts_with("$e") {
Some((SwiftManglingScheme::EmbeddedSwift, value))
} else if value.starts_with("_T") {
Some((SwiftManglingScheme::LegacySwift, value))
} else {
None
}
}