use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use crate::clang::name_mangling::{mangle_simple_function, MangledName, Mangler};
use crate::x86::{
X86CallingConvention, X86FrameLowering, X86RegisterInfo, X86_64_REG_COUNT,
X86_RED_ZONE_SIZE_64, X86_STACK_ALIGNMENT_64,
};
pub const AMD64_INT_ARG_REGS: &[&str] = &["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
pub const AMD64_SSE_ARG_REGS: &[&str] = &[
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
];
pub const WIN64_INT_ARG_REGS: &[&str] = &["rcx", "rdx", "r8", "r9"];
pub const WIN64_SSE_ARG_REGS: &[&str] = &["xmm0", "xmm1", "xmm2", "xmm3"];
pub const X86_LP64_LONG_SIZE: u32 = 8;
pub const X86_LP64_PTR_SIZE: u32 = 8;
pub const X86_ILP32_LONG_SIZE: u32 = 4;
pub const X86_ILP32_PTR_SIZE: u32 = 4;
pub const X86_LLP64_LONG_SIZE: u32 = 4;
pub const X86_LLP64_PTR_SIZE: u32 = 8;
#[derive(Debug, Clone)]
pub struct X86Interop {
pub arch: String,
pub abi: X86InteropABI,
pub is_64bit: bool,
pub cxx_interops: Vec<X86CXXInterop>,
pub rust_interops: Vec<X86RustInterop>,
pub python_interops: Vec<X86PythonInterop>,
pub go_interops: Vec<X86GoInterop>,
pub java_interops: Vec<X86JavaInterop>,
pub dotnet_interops: Vec<X86DotNetInterop>,
pub wasm_interops: Vec<X86WASMInterop>,
pub linker_flags: Vec<String>,
pub compile_flags: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum X86InteropABI {
SystemV,
MicrosoftX64,
Cdecl,
Stdcall,
Fastcall,
Thiscall,
Vectorcall,
Custom(String),
}
impl fmt::Display for X86InteropABI {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SystemV => write!(f, "sysv"),
Self::MicrosoftX64 => write!(f, "msx64"),
Self::Cdecl => write!(f, "cdecl"),
Self::Stdcall => write!(f, "stdcall"),
Self::Fastcall => write!(f, "fastcall"),
Self::Thiscall => write!(f, "thiscall"),
Self::Vectorcall => write!(f, "vectorcall"),
Self::Custom(s) => write!(f, "custom({})", s),
}
}
}
impl Default for X86Interop {
fn default() -> Self {
Self {
arch: "x86_64".into(),
abi: X86InteropABI::SystemV,
is_64bit: true,
cxx_interops: Vec::new(),
rust_interops: Vec::new(),
python_interops: Vec::new(),
go_interops: Vec::new(),
java_interops: Vec::new(),
dotnet_interops: Vec::new(),
wasm_interops: Vec::new(),
linker_flags: Vec::new(),
compile_flags: Vec::new(),
}
}
}
impl X86Interop {
pub fn new(arch: &str, abi: X86InteropABI) -> Self {
let is_64bit = matches!(arch, "x86_64" | "amd64" | "x64");
Self {
arch: arch.to_string(),
abi,
is_64bit,
..Default::default()
}
}
pub fn new_linux_x86_64() -> Self {
Self::new("x86_64", X86InteropABI::SystemV)
}
pub fn new_windows_x64() -> Self {
Self::new("x86_64", X86InteropABI::MicrosoftX64)
}
pub fn new_linux_i386() -> Self {
Self::new("i386", X86InteropABI::Cdecl)
}
pub fn new_windows_i386() -> Self {
Self::new("i386", X86InteropABI::Stdcall)
}
pub fn add_cxx_interop(&mut self, interop: X86CXXInterop) -> &mut Self {
self.cxx_interops.push(interop);
self
}
pub fn add_rust_interop(&mut self, interop: X86RustInterop) -> &mut Self {
self.rust_interops.push(interop);
self
}
pub fn add_python_interop(&mut self, interop: X86PythonInterop) -> &mut Self {
self.python_interops.push(interop);
self
}
pub fn add_go_interop(&mut self, interop: X86GoInterop) -> &mut Self {
self.go_interops.push(interop);
self
}
pub fn add_java_interop(&mut self, interop: X86JavaInterop) -> &mut Self {
self.java_interops.push(interop);
self
}
pub fn add_dotnet_interop(&mut self, interop: X86DotNetInterop) -> &mut Self {
self.dotnet_interops.push(interop);
self
}
pub fn add_wasm_interop(&mut self, interop: X86WASMInterop) -> &mut Self {
self.wasm_interops.push(interop);
self
}
pub fn add_linker_flag(&mut self, flag: &str) -> &mut Self {
self.linker_flags.push(flag.to_string());
self
}
pub fn add_compile_flag(&mut self, flag: &str) -> &mut Self {
self.compile_flags.push(flag.to_string());
self
}
pub fn total_interop_count(&self) -> usize {
self.cxx_interops.len()
+ self.rust_interops.len()
+ self.python_interops.len()
+ self.go_interops.len()
+ self.java_interops.len()
+ self.dotnet_interops.len()
+ self.wasm_interops.len()
}
pub fn pointer_size(&self) -> u32 {
if self.is_64bit {
match self.abi {
X86InteropABI::MicrosoftX64 => X86_LLP64_PTR_SIZE,
_ => X86_LP64_PTR_SIZE,
}
} else {
X86_ILP32_PTR_SIZE
}
}
pub fn long_size(&self) -> u32 {
if self.is_64bit {
match self.abi {
X86InteropABI::MicrosoftX64 => X86_LLP64_LONG_SIZE,
_ => X86_LP64_LONG_SIZE,
}
} else {
X86_ILP32_LONG_SIZE
}
}
pub fn type_align(&self, c_type: &X86CType) -> u32 {
if self.is_64bit {
c_type.alignment_x86_64()
} else {
c_type.alignment_i386()
}
}
pub fn type_size(&self, c_type: &X86CType) -> u32 {
if self.is_64bit {
c_type.size_x86_64(self.abi.clone())
} else {
c_type.size_i386()
}
}
pub fn generate_build_script(&self) -> String {
let mut out = String::new();
out.push_str("# Auto-generated X86 interop build script\n");
out.push_str(&format!("# ARCH={} ABI={}\n\n", self.arch, self.abi));
if !self.cxx_interops.is_empty() {
out.push_str("# ── C++ Interop Targets ──\n");
for (i, cxx) in self.cxx_interops.iter().enumerate() {
out.push_str(&format!("cxx_target_{}: {}\n", i, cxx.build_rule()));
}
out.push('\n');
}
if !self.rust_interops.is_empty() {
out.push_str("# ── Rust FFI Targets ──\n");
for (i, rust) in self.rust_interops.iter().enumerate() {
out.push_str(&format!("rust_target_{}: {}\n", i, rust.build_rule()));
}
out.push('\n');
}
if !self.python_interops.is_empty() {
out.push_str("# ── Python Extension Targets ──\n");
for (i, py) in self.python_interops.iter().enumerate() {
out.push_str(&format!("py_target_{}: {}\n", i, py.build_rule()));
}
out.push('\n');
}
if !self.go_interops.is_empty() {
out.push_str("# ── Go cgo Targets ──\n");
for (i, go) in self.go_interops.iter().enumerate() {
out.push_str(&format!("go_target_{}: {}\n", i, go.build_rule()));
}
out.push('\n');
}
if !self.wasm_interops.is_empty() {
out.push_str("# ── WASM Targets ──\n");
for (i, wm) in self.wasm_interops.iter().enumerate() {
out.push_str(&format!("wasm_target_{}: {}\n", i, wm.build_rule()));
}
out.push('\n');
}
out
}
pub fn generate_ffi_header(&self) -> String {
let mut out = String::new();
out.push_str(&format!(
"/* Auto-generated X86 FFI header — arch={}, abi={} */\n",
self.arch, self.abi
));
out.push_str("#ifndef X86_INTEROP_FFI_H\n");
out.push_str("#define X86_INTEROP_FFI_H\n\n");
out.push_str("#ifdef __cplusplus\n");
out.push_str("extern \"C\" {\n");
out.push_str("#endif\n\n");
for cxx in &self.cxx_interops {
out.push_str(&cxx.to_c_declaration());
out.push('\n');
}
for rust in &self.rust_interops {
out.push_str(&rust.to_c_declaration());
out.push('\n');
}
out.push_str("#ifdef __cplusplus\n");
out.push_str("}\n");
out.push_str("#endif\n\n");
out.push_str("#endif /* X86_INTEROP_FFI_H */\n");
out
}
pub fn generate_cxx_wrapper_source(&self) -> String {
let mut out = String::new();
out.push_str(&format!(
"// Auto-generated C++ → C wrapper source — arch={}, abi={}\n",
self.arch, self.abi
));
out.push_str("#include \"x86_interop_ffi.h\"\n\n");
for cxx in &self.cxx_interops {
out.push_str(&cxx.to_c_wrapper_impl());
out.push('\n');
}
out
}
pub fn generate_all_bindings(&self) -> String {
let mut out = String::new();
out.push_str(&format!(
"// ═══ Auto-generated X86 Interop Bindings (arch={}, abi={}) ═══\n\n",
self.arch, self.abi
));
out.push_str("// ── C Header ──\n");
out.push_str(&self.generate_ffi_header());
out.push_str("\n\n");
if !self.cxx_interops.is_empty() {
out.push_str("// ── C++ → C Wrappers ──\n");
out.push_str(&self.generate_cxx_wrapper_source());
out.push_str("\n\n");
}
if !self.rust_interops.is_empty() {
out.push_str("// ── Rust FFI Bindings ──\n");
for rust in &self.rust_interops {
out.push_str(&rust.to_rust_binding());
out.push('\n');
}
out.push_str("\n\n");
}
if !self.python_interops.is_empty() {
out.push_str("// ── Python Bindings ──\n");
for py in &self.python_interops {
out.push_str(&py.to_python_binding());
out.push('\n');
}
out.push_str("\n\n");
}
if !self.go_interops.is_empty() {
out.push_str("// ── Go Bindings ──\n");
for go in &self.go_interops {
out.push_str(&go.to_go_binding());
out.push('\n');
}
out.push_str("\n\n");
}
if !self.java_interops.is_empty() {
out.push_str("// ── Java JNI Bindings ──\n");
for java in &self.java_interops {
out.push_str(&java.to_java_binding());
out.push('\n');
}
out.push_str("\n\n");
}
if !self.dotnet_interops.is_empty() {
out.push_str("// ── .NET / C# P/Invoke Bindings ──\n");
for dn in &self.dotnet_interops {
out.push_str(&dn.to_dotnet_binding());
out.push('\n');
}
out.push_str("\n\n");
}
if !self.wasm_interops.is_empty() {
out.push_str("// ── WASM Interop Bindings ──\n");
for wm in &self.wasm_interops {
out.push_str(&wm.to_wasm_binding());
out.push('\n');
}
out.push_str("\n\n");
}
out
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum X86CType {
Void,
Char,
SignedChar,
UnsignedChar,
Short,
UnsignedShort,
Int,
UnsignedInt,
Long,
UnsignedLong,
LongLong,
UnsignedLongLong,
LongDouble,
Float,
Double,
Bool,
Pointer(Box<X86CType>),
ConstPointer(Box<X86CType>),
Array(Box<X86CType>, usize),
Struct(String, Vec<(String, X86CType)>),
Union(String, Vec<(String, X86CType)>),
Enum(String, Vec<(String, i32)>),
FunctionPointer(Box<X86CType>, Vec<X86CType>),
Typedef(String, Box<X86CType>),
Opaque(String),
}
impl X86CType {
pub fn to_c_decl(&self) -> String {
match self {
Self::Void => "void".into(),
Self::Char => "char".into(),
Self::SignedChar => "signed char".into(),
Self::UnsignedChar => "unsigned char".into(),
Self::Short => "short".into(),
Self::UnsignedShort => "unsigned short".into(),
Self::Int => "int".into(),
Self::UnsignedInt => "unsigned int".into(),
Self::Long => "long".into(),
Self::UnsignedLong => "unsigned long".into(),
Self::LongLong => "long long".into(),
Self::UnsignedLongLong => "unsigned long long".into(),
Self::LongDouble => "long double".into(),
Self::Float => "float".into(),
Self::Double => "double".into(),
Self::Bool => "_Bool".into(),
Self::Pointer(inner) => format!("{}*", inner.to_c_decl()),
Self::ConstPointer(inner) => format!("const {}*", inner.to_c_decl()),
Self::Array(inner, n) => format!("{}[{}]", inner.to_c_decl(), n),
Self::Struct(name, _) => format!("struct {}", name),
Self::Union(name, _) => format!("union {}", name),
Self::Enum(name, _) => format!("enum {}", name),
Self::FunctionPointer(ret, params) => {
let p: Vec<String> = params.iter().map(|t| t.to_c_decl()).collect();
format!("{}(*)({})", ret.to_c_decl(), p.join(", "))
}
Self::Typedef(name, _) => name.clone(),
Self::Opaque(name) => name.clone(),
}
}
pub fn size_x86_64(&self, abi: X86InteropABI) -> u32 {
let long_sz = if abi == X86InteropABI::MicrosoftX64 {
4
} else {
8
};
self._size_impl(long_sz, 8)
}
pub fn size_i386(&self) -> u32 {
self._size_impl(4, 4)
}
fn _size_impl(&self, long_sz: u32, ptr_sz: u32) -> u32 {
match self {
Self::Void => 0,
Self::Char | Self::SignedChar | Self::UnsignedChar | Self::Bool => 1,
Self::Short | Self::UnsignedShort => 2,
Self::Int | Self::UnsignedInt | Self::Enum(_, _) => 4,
Self::Long | Self::UnsignedLong => long_sz,
Self::LongLong | Self::UnsignedLongLong => 8,
Self::Float => 4,
Self::Double => 8,
Self::LongDouble => 16,
Self::Pointer(_) | Self::ConstPointer(_) | Self::FunctionPointer(_, _) => ptr_sz,
Self::Array(inner, n) => inner._size_impl(long_sz, ptr_sz) * (*n as u32),
Self::Struct(_, fields) => {
let mut offset = 0u32;
let mut max_align = 1u32;
for (_, ty) in fields {
let a = ty._align_impl(long_sz, ptr_sz);
let s = ty._size_impl(long_sz, ptr_sz);
max_align = max_align.max(a);
if offset % a != 0 {
offset += a - (offset % a);
}
offset += s;
}
if offset % max_align != 0 {
offset += max_align - (offset % max_align);
}
offset
}
Self::Union(_, fields) => fields
.iter()
.map(|(_, ty)| ty._size_impl(long_sz, ptr_sz))
.max()
.unwrap_or(0),
Self::Typedef(_, inner) => inner._size_impl(long_sz, ptr_sz),
Self::Opaque(_) => ptr_sz,
}
}
pub fn alignment_x86_64(&self) -> u32 {
self._align_impl(8, 8)
}
pub fn alignment_i386(&self) -> u32 {
self._align_impl(4, 4)
}
fn _align_impl(&self, long_sz: u32, ptr_sz: u32) -> u32 {
match self {
Self::Void => 1,
Self::Char | Self::SignedChar | Self::UnsignedChar | Self::Bool => 1,
Self::Short | Self::UnsignedShort => 2,
Self::Int | Self::UnsignedInt | Self::Enum(_, _) => 4,
Self::Long | Self::UnsignedLong => long_sz,
Self::LongLong | Self::UnsignedLongLong => 8,
Self::Float => 4,
Self::Double => 8,
Self::LongDouble => 16,
Self::Pointer(_) | Self::ConstPointer(_) | Self::FunctionPointer(_, _) => ptr_sz,
Self::Array(inner, _) => inner._align_impl(long_sz, ptr_sz),
Self::Struct(_, fields) => fields
.iter()
.map(|(_, ty)| ty._align_impl(long_sz, ptr_sz))
.max()
.unwrap_or(1),
Self::Union(_, fields) => fields
.iter()
.map(|(_, ty)| ty._align_impl(long_sz, ptr_sz))
.max()
.unwrap_or(1),
Self::Typedef(_, inner) => inner._align_impl(long_sz, ptr_sz),
Self::Opaque(_) => ptr_sz,
}
}
pub fn is_integer(&self) -> bool {
matches!(
self,
Self::Char
| Self::SignedChar
| Self::UnsignedChar
| Self::Short
| Self::UnsignedShort
| Self::Int
| Self::UnsignedInt
| Self::Long
| Self::UnsignedLong
| Self::LongLong
| Self::UnsignedLongLong
| Self::Bool
| Self::Enum(_, _)
)
}
pub fn is_floating(&self) -> bool {
matches!(self, Self::Float | Self::Double | Self::LongDouble)
}
pub fn is_register_class_int(&self) -> bool {
self.is_integer()
|| matches!(
self,
Self::Pointer(_) | Self::ConstPointer(_) | Self::Opaque(_)
)
}
pub fn is_register_class_sse(&self) -> bool {
self.is_floating()
}
pub fn is_memory_class(&self, abi: X86InteropABI) -> bool {
match abi {
X86InteropABI::SystemV => {
if matches!(self, Self::LongDouble) {
return true;
}
match self {
Self::Struct(_, _) | Self::Union(_, _) => self.size_x86_64(abi) > 16,
_ => false,
}
}
X86InteropABI::MicrosoftX64 => {
match self {
Self::Struct(_, _) | Self::Union(_, _) => {
let sz = self.size_x86_64(abi);
sz != 1 && sz != 2 && sz != 4 && sz != 8
}
_ => false,
}
}
_ => false,
}
}
}
impl fmt::Display for X86CType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_c_decl())
}
}
#[derive(Debug, Clone)]
pub struct X86CParam {
pub name: String,
pub ty: X86CType,
}
impl X86CParam {
pub fn new(name: &str, ty: X86CType) -> Self {
Self {
name: name.to_string(),
ty,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X86ExceptionSpec {
None,
Noexcept,
ThrowNone,
ThrowTypes(Vec<String>),
NoexceptConditional(bool),
}
impl X86ExceptionSpec {
pub fn to_cxx_suffix(&self) -> String {
match self {
Self::None => String::new(),
Self::Noexcept => " noexcept".into(),
Self::ThrowNone => " throw()".into(),
Self::ThrowTypes(types) => format!(" throw({})", types.join(", ")),
Self::NoexceptConditional(true) => " noexcept(true)".into(),
Self::NoexceptConditional(false) => " noexcept(false)".into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86LinkageKind {
External,
Internal,
Weak,
WeakODR,
LinkonceODR,
AvailableExternally,
}
impl fmt::Display for X86LinkageKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::External => write!(f, "external"),
Self::Internal => write!(f, "internal"),
Self::Weak => write!(f, "weak"),
Self::WeakODR => write!(f, "weak_odr"),
Self::LinkonceODR => write!(f, "linkonce_odr"),
Self::AvailableExternally => write!(f, "available_externally"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86CallConv {
Cdecl,
Stdcall,
Fastcall,
Thiscall,
Vectorcall,
SysVAMD64,
MicrosoftX64,
Regcall,
}
impl X86CallConv {
pub fn as_attribute(&self) -> Option<&'static str> {
match self {
Self::Cdecl => Some("cdecl"),
Self::Stdcall => Some("stdcall"),
Self::Fastcall => Some("fastcall"),
Self::Thiscall => Some("thiscall"),
Self::Vectorcall => Some("vectorcall"),
Self::SysVAMD64 => None,
Self::MicrosoftX64 => None,
Self::Regcall => Some("regcall"),
}
}
pub fn integer_arg_regs_64(&self) -> usize {
match self {
Self::SysVAMD64 | Self::Regcall => 6,
Self::MicrosoftX64 => 4,
_ => 0,
}
}
pub fn sse_arg_regs_64(&self) -> usize {
match self {
Self::SysVAMD64 => 8,
Self::MicrosoftX64 => 4,
Self::Vectorcall => 6,
_ => 0,
}
}
}
impl fmt::Display for X86CallConv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Cdecl => write!(f, "cdecl"),
Self::Stdcall => write!(f, "stdcall"),
Self::Fastcall => write!(f, "fastcall"),
Self::Thiscall => write!(f, "thiscall"),
Self::Vectorcall => write!(f, "vectorcall"),
Self::SysVAMD64 => write!(f, "sysv_amd64"),
Self::MicrosoftX64 => write!(f, "msx64"),
Self::Regcall => write!(f, "regcall"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X86CAttribute {
Aligned(u32),
Packed,
NoReturn,
Format(FormatArchetype, u32, u32),
Deprecated,
DeprecatedMsg(String),
Visibility(VisibilityKind),
Used,
Unused,
Dllexport,
Dllimport,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FormatArchetype {
Printf,
Scanf,
Strftime,
Strfmon,
}
impl fmt::Display for FormatArchetype {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Printf => write!(f, "printf"),
Self::Scanf => write!(f, "scanf"),
Self::Strftime => write!(f, "strftime"),
Self::Strfmon => write!(f, "strfmon"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VisibilityKind {
Default,
Hidden,
Internal,
Protected,
}
impl fmt::Display for VisibilityKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Default => write!(f, "default"),
Self::Hidden => write!(f, "hidden"),
Self::Internal => write!(f, "internal"),
Self::Protected => write!(f, "protected"),
}
}
}
impl X86CAttribute {
pub fn to_decl(&self) -> String {
match self {
Self::Aligned(n) => format!("__attribute__((aligned({})))", n),
Self::Packed => "__attribute__((packed))".into(),
Self::NoReturn => "__attribute__((noreturn))".into(),
Self::Format(a, si, ftc) => format!("__attribute__((format({}, {}, {})))", a, si, ftc),
Self::Deprecated => "__attribute__((deprecated))".into(),
Self::DeprecatedMsg(m) => format!("__attribute__((deprecated(\"{}\")))", m),
Self::Visibility(v) => format!("__attribute__((visibility(\"{}\")))", v),
Self::Used => "__attribute__((used))".into(),
Self::Unused => "__attribute__((unused))".into(),
Self::Dllexport => "__declspec(dllexport)".into(),
Self::Dllimport => "__declspec(dllimport)".into(),
}
}
}
#[derive(Debug, Clone)]
pub struct X86CXXInterop {
pub cxx_name: String,
pub c_name: String,
pub mangled_name: String,
pub return_type: X86CType,
pub params: Vec<X86CParam>,
pub is_variadic: bool,
pub exception_spec: X86ExceptionSpec,
pub linkage: X86LinkageKind,
pub c_call_conv: X86CallConv,
pub is_method: bool,
pub class_name: Option<String>,
pub is_const: bool,
pub attributes: Vec<X86CAttribute>,
}
impl X86CXXInterop {
pub fn new(cxx_name: &str, c_name: &str, return_type: X86CType) -> Self {
let mangled = mangle_simple_function(cxx_name, &[]);
Self {
cxx_name: cxx_name.to_string(),
c_name: c_name.to_string(),
mangled_name: mangled.into_string(),
return_type,
params: Vec::new(),
is_variadic: false,
exception_spec: X86ExceptionSpec::None,
linkage: X86LinkageKind::External,
c_call_conv: X86CallConv::Cdecl,
is_method: false,
class_name: None,
is_const: false,
attributes: Vec::new(),
}
}
pub fn new_method(
class_name: &str,
method_name: &str,
c_name: &str,
return_type: X86CType,
is_const: bool,
) -> Self {
let cxx_full = format!("{}::{}", class_name, method_name);
let mut entry = Self::new(&cxx_full, c_name, return_type);
entry.is_method = true;
entry.class_name = Some(class_name.to_string());
entry.is_const = is_const;
let this_ty = if is_const {
X86CType::ConstPointer(Box::new(X86CType::Opaque(class_name.to_string())))
} else {
X86CType::Pointer(Box::new(X86CType::Opaque(class_name.to_string())))
};
entry.params.insert(0, X86CParam::new("this_", this_ty));
entry
}
pub fn add_param(&mut self, name: &str, ty: X86CType) -> &mut Self {
self.params.push(X86CParam::new(name, ty));
self
}
pub fn with_exception_spec(mut self, spec: X86ExceptionSpec) -> Self {
self.exception_spec = spec;
self
}
pub fn with_call_conv(mut self, cc: X86CallConv) -> Self {
self.c_call_conv = cc;
self
}
pub fn with_linkage(mut self, lk: X86LinkageKind) -> Self {
self.linkage = lk;
self
}
pub fn with_variadic(mut self, v: bool) -> Self {
self.is_variadic = v;
self
}
pub fn with_attribute(mut self, attr: X86CAttribute) -> Self {
self.attributes.push(attr);
self
}
pub fn to_c_declaration(&self) -> String {
let mut out = String::new();
for attr in &self.attributes {
out.push_str(&attr.to_decl());
out.push(' ');
}
if self.linkage == X86LinkageKind::Internal {
out.push_str("static ");
}
let params_str: Vec<String> = self
.params
.iter()
.map(|p| format!("{} {}", p.ty.to_c_decl(), p.name))
.collect();
let variadic = if self.is_variadic { ", ..." } else { "" };
if let Some(cc_attr) = self.c_call_conv.as_attribute() {
out.push_str(&format!(
"{} __attribute__(({})) {}({}{});",
self.return_type.to_c_decl(),
cc_attr,
self.c_name,
params_str.join(", "),
variadic
));
} else {
out.push_str(&format!(
"{} {}({}{});",
self.return_type.to_c_decl(),
self.c_name,
params_str.join(", "),
variadic
));
}
out
}
pub fn to_extern_c_block(&self) -> String {
let mut out = String::new();
out.push_str("extern \"C\" {\n");
let params_str: Vec<String> = self
.params
.iter()
.map(|p| format!("{} {}", p.ty.to_c_decl(), p.name))
.collect();
let variadic = if self.is_variadic { ", ..." } else { "" };
out.push_str(&format!(
" {} {}({}{}){};\n",
self.return_type.to_c_decl(),
self.c_name,
params_str.join(", "),
variadic,
self.exception_spec.to_cxx_suffix()
));
out.push_str("}\n");
out
}
pub fn to_c_wrapper_impl(&self) -> String {
let mut out = String::new();
if !self.is_method {
let params_str: Vec<String> = self
.params
.iter()
.map(|p| format!("{} {}", p.ty.to_c_decl(), p.name))
.collect();
let variadic = if self.is_variadic { ", ..." } else { "" };
let arg_names: Vec<String> = self.params.iter().map(|p| p.name.clone()).collect();
out.push_str(&format!(
"extern \"C\" {} {}({}{}){} {{\n",
self.return_type.to_c_decl(),
self.c_name,
params_str.join(", "),
variadic,
self.exception_spec.to_cxx_suffix()
));
if self.return_type == X86CType::Void {
out.push_str(&format!(" {}::{}(", self.cxx_name, self.cxx_name));
out.push_str(&arg_names.join(", "));
out.push_str(");\n");
} else {
out.push_str(&format!(
" return {}::{}({}",
self.cxx_name,
self.cxx_name,
arg_names.join(", ")
));
out.push_str(");\n");
}
if self.exception_spec == X86ExceptionSpec::None {
let body = out.clone();
out.clear();
out.push_str(&format!(
"extern \"C\" {} {}({}{}) {{\n",
self.return_type.to_c_decl(),
self.c_name,
params_str.join(", "),
variadic
));
out.push_str(" try {\n");
for line in body.lines().skip(1) {
out.push_str(&format!(" {}\n", line));
}
out.push_str(" } catch (const std::exception& e) {\n");
out.push_str(" // Log or handle C++ exception at C boundary\n");
out.push_str(" return ");
out.push_str(&self.default_return_value());
out.push_str(";\n");
out.push_str(" } catch (...) {\n");
out.push_str(" return ");
out.push_str(&self.default_return_value());
out.push_str(";\n");
out.push_str(" }\n");
}
out.push_str("}\n");
} else {
let params_after_this: Vec<_> = self.params.iter().skip(1).collect();
let params_str: Vec<String> = params_after_this
.iter()
.map(|p| format!("{} {}", p.ty.to_c_decl(), p.name))
.collect();
let arg_names: Vec<String> = params_after_this.iter().map(|p| p.name.clone()).collect();
let class = self.class_name.as_deref().unwrap_or("UnknownClass");
out.push_str(&format!(
"extern \"C\" {} {}({}){} {{\n",
self.return_type.to_c_decl(),
self.c_name,
params_str.join(", "),
self.exception_spec.to_cxx_suffix()
));
let const_cast = if self.is_const {
format!(
"const_cast<{}*>(static_cast<const {}*>(this_))",
class, class
)
} else {
"this_".to_string()
};
if self.return_type == X86CType::Void {
out.push_str(&format!(
" {0}->{1}({2});\n",
const_cast,
self.cxx_name.rsplit("::").next().unwrap_or(&self.cxx_name),
arg_names.join(", ")
));
} else {
out.push_str(&format!(
" return {0}->{1}({2});\n",
const_cast,
self.cxx_name.rsplit("::").next().unwrap_or(&self.cxx_name),
arg_names.join(", ")
));
}
out.push_str("}\n");
}
out
}
fn default_return_value(&self) -> String {
match &self.return_type {
X86CType::Void => String::new(),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => {
"nullptr".into()
}
X86CType::Bool => "false".into(),
X86CType::Float | X86CType::Double | X86CType::LongDouble => "0.0".into(),
_ => "0".into(),
}
}
pub fn build_rule(&self) -> String {
format!(
"g++ -c wrapper_{}.cpp -o wrapper_{}.o",
self.c_name, self.c_name
)
}
pub fn to_llvm_declare(&self) -> String {
let param_types: Vec<String> = self
.params
.iter()
.map(|p| ctype_to_llvm_ir_type(&p.ty))
.collect();
format!(
"declare {} @{}({})",
ctype_to_llvm_ir_type(&self.return_type),
self.c_name,
param_types.join(", ")
)
}
pub fn to_mangling_entry(&self) -> String {
format!(
"// {} -> {} [mangled: {}]",
self.cxx_name, self.c_name, self.mangled_name
)
}
}
pub fn ctype_to_llvm_ir_type(ct: &X86CType) -> String {
match ct {
X86CType::Void => "void".into(),
X86CType::Char | X86CType::SignedChar => "i8".into(),
X86CType::UnsignedChar => "i8".into(),
X86CType::Short | X86CType::UnsignedShort => "i16".into(),
X86CType::Int | X86CType::UnsignedInt => "i32".into(),
X86CType::Long | X86CType::UnsignedLong => "i64".into(),
X86CType::LongLong | X86CType::UnsignedLongLong => "i64".into(),
X86CType::Float => "float".into(),
X86CType::Double => "double".into(),
X86CType::LongDouble => "x86_fp80".into(),
X86CType::Bool => "i1".into(),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => "ptr".into(),
X86CType::Array(inner, _) => format!("[{} x {}]", 0, ctype_to_llvm_ir_type(inner)), X86CType::FunctionPointer(_, _) => "ptr".into(),
X86CType::Struct(name, _) => format!("%struct.{}", name),
X86CType::Union(name, _) => format!("%union.{}", name),
X86CType::Enum(_, _) => "i32".into(),
X86CType::Typedef(_, inner) => ctype_to_llvm_ir_type(inner),
}
}
#[derive(Debug, Clone, Default)]
pub struct X86ManglingRegistry {
pub mangle_to_c: HashMap<String, String>,
pub c_to_mangles: HashMap<String, Vec<String>>,
pub name_to_mangle: HashMap<String, String>,
}
impl X86ManglingRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, cxx_name: &str, mangled: &str, c_name: &str) {
self.mangle_to_c
.insert(mangled.to_string(), c_name.to_string());
self.c_to_mangles
.entry(c_name.to_string())
.or_default()
.push(mangled.to_string());
self.name_to_mangle
.insert(cxx_name.to_string(), mangled.to_string());
}
pub fn c_name_for_mangled(&self, mangled: &str) -> Option<&str> {
self.mangle_to_c.get(mangled).map(|s| s.as_str())
}
pub fn mangles_for_c(&self, c_name: &str) -> Option<&Vec<String>> {
self.c_to_mangles.get(c_name)
}
pub fn mangle_for(&self, cxx_name: &str) -> Option<&str> {
self.name_to_mangle.get(cxx_name).map(|s| s.as_str())
}
pub fn is_registered_mangled(&self, mangled: &str) -> bool {
self.mangle_to_c.contains_key(mangled)
}
pub fn len(&self) -> usize {
self.mangle_to_c.len()
}
pub fn to_define_header(&self) -> String {
let mut out = String::new();
out.push_str("/* Auto-generated C++ mangling → C linkage mappings */\n");
out.push_str("#ifndef X86_MANGLING_REGISTRY_H\n");
out.push_str("#define X86_MANGLING_REGISTRY_H\n\n");
for (mangled, c_name) in &self.mangle_to_c {
out.push_str(&format!("/* {} -> {} */\n", mangled, c_name));
out.push_str(&format!(
"extern void {} __asm__(\"{}\");\n",
c_name, mangled
));
}
out.push_str("\n#endif /* X86_MANGLING_REGISTRY_H */\n");
out
}
pub fn to_linker_script(&self) -> String {
let mut out = String::new();
out.push_str("/* Auto-generated linker aliases for C++ mangling */\n");
for (mangled, c_name) in &self.mangle_to_c {
out.push_str(&format!("PROVIDE({} = {});\n", c_name, mangled));
}
out
}
}
#[derive(Debug, Clone)]
pub struct X86RustInterop {
pub rust_name: String,
pub c_name: String,
pub return_type: X86RustFfiType,
pub params: Vec<(String, X86RustFfiType)>,
pub attributes: Vec<X86RustFfiAttribute>,
pub is_unsafe: bool,
pub direction: X86RustFfiDirection,
pub abi: String,
pub use_cbingen: bool,
pub use_autocxx: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RustFfiDirection {
RustCallsC,
CCallsRust,
Bidirectional,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X86RustFfiAttribute {
NoMangle,
ReprC,
ReprPacked,
ReprTransparent,
ReprAlign(usize),
ExternC,
LinkName(String),
UnsafeFn,
Doc(String),
AllowDeadCode,
AllowNonCamelCaseTypes,
AllowNonSnakeCase,
Cold,
Inline,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum X86RustFfiType {
Unit,
I8,
U8,
I16,
U16,
I32,
U32,
I64,
U64,
I128,
U128,
F32,
F64,
Bool,
CChar,
CSchar,
CUchar,
CShort,
CUshort,
CInt,
CUint,
CLong,
CUlong,
CLonglong,
CUlonglong,
CFloat,
CDouble,
MutVoidPtr,
ConstVoidPtr,
Ref(Box<X86RustFfiType>),
MutRef(Box<X86RustFfiType>),
Slice(Box<X86RustFfiType>),
Custom(String),
Opaque(String),
FnPtr(Box<X86RustFfiType>, Vec<X86RustFfiType>),
Nullable(Box<X86RustFfiType>),
}
impl X86RustFfiType {
pub fn to_rust_decl(&self) -> String {
match self {
Self::Unit => "()".into(),
Self::I8 => "i8".into(),
Self::U8 => "u8".into(),
Self::I16 => "i16".into(),
Self::U16 => "u16".into(),
Self::I32 => "i32".into(),
Self::U32 => "u32".into(),
Self::I64 => "i64".into(),
Self::U64 => "u64".into(),
Self::I128 => "i128".into(),
Self::U128 => "u128".into(),
Self::F32 => "f32".into(),
Self::F64 => "f64".into(),
Self::Bool => "bool".into(),
Self::CChar => "std::os::raw::c_char".into(),
Self::CSchar => "std::os::raw::c_schar".into(),
Self::CUchar => "std::os::raw::c_uchar".into(),
Self::CShort => "std::os::raw::c_short".into(),
Self::CUshort => "std::os::raw::c_ushort".into(),
Self::CInt => "std::os::raw::c_int".into(),
Self::CUint => "std::os::raw::c_uint".into(),
Self::CLong => "std::os::raw::c_long".into(),
Self::CUlong => "std::os::raw::c_ulong".into(),
Self::CLonglong => "std::os::raw::c_longlong".into(),
Self::CUlonglong => "std::os::raw::c_ulonglong".into(),
Self::CFloat => "std::os::raw::c_float".into(),
Self::CDouble => "std::os::raw::c_double".into(),
Self::MutVoidPtr => "*mut std::os::raw::c_void".into(),
Self::ConstVoidPtr => "*const std::os::raw::c_void".into(),
Self::Ref(inner) => format!("&{}", inner.to_rust_decl()),
Self::MutRef(inner) => format!("&mut {}", inner.to_rust_decl()),
Self::Slice(inner) => format!("&[{}]", inner.to_rust_decl()),
Self::Custom(name) => name.clone(),
Self::Opaque(name) => format!("*mut {}", name),
Self::FnPtr(ret, params) => {
let p: Vec<String> = params.iter().map(|t| t.to_rust_decl()).collect();
format!(
"extern \"C\" fn({}) -> {}",
p.join(", "),
ret.to_rust_decl()
)
}
Self::Nullable(inner) => format!("Option<{}>", inner.to_rust_decl()),
}
}
}
impl fmt::Display for X86RustFfiType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_rust_decl())
}
}
impl X86RustInterop {
pub fn new(rust_name: &str, c_name: &str, return_type: X86RustFfiType) -> Self {
Self {
rust_name: rust_name.to_string(),
c_name: c_name.to_string(),
return_type,
params: Vec::new(),
attributes: vec![
X86RustFfiAttribute::ExternC,
X86RustFfiAttribute::AllowDeadCode,
],
is_unsafe: true,
direction: X86RustFfiDirection::RustCallsC,
abi: "C".into(),
use_cbingen: false,
use_autocxx: false,
}
}
pub fn new_export(rust_name: &str, c_name: &str, return_type: X86RustFfiType) -> Self {
Self {
rust_name: rust_name.to_string(),
c_name: c_name.to_string(),
return_type,
params: Vec::new(),
attributes: vec![X86RustFfiAttribute::NoMangle, X86RustFfiAttribute::ExternC],
is_unsafe: false,
direction: X86RustFfiDirection::CCallsRust,
abi: "C".into(),
use_cbingen: false,
use_autocxx: false,
}
}
pub fn add_param(&mut self, name: &str, ty: X86RustFfiType) -> &mut Self {
self.params.push((name.to_string(), ty));
self
}
pub fn with_attribute(mut self, attr: X86RustFfiAttribute) -> Self {
self.attributes.push(attr);
self
}
pub fn with_cbingen(mut self) -> Self {
self.use_cbingen = true;
self
}
pub fn with_autocxx(mut self) -> Self {
self.use_autocxx = true;
self
}
pub fn with_abi(mut self, abi: &str) -> Self {
self.abi = abi.to_string();
self
}
pub fn to_rust_import_block(&self) -> String {
let mut out = String::new();
for attr in &self.attributes {
out.push_str(&self.render_attribute(attr));
}
out.push_str(&format!("extern \"{}\" {{\n", self.abi));
let params_str: Vec<String> = self
.params
.iter()
.map(|(n, t)| format!("{}: {}", n, t.to_rust_decl()))
.collect();
out.push_str(&format!(
" pub fn {}({})",
self.c_name,
params_str.join(", ")
));
if self.return_type != X86RustFfiType::Unit {
out.push_str(&format!(" -> {}", self.return_type.to_rust_decl()));
}
out.push_str(";\n");
out.push_str("}\n");
out
}
pub fn to_rust_export_fn(&self) -> String {
let mut out = String::new();
for attr in &self.attributes {
out.push_str(&self.render_attribute(attr));
}
let params_str: Vec<String> = self
.params
.iter()
.map(|(n, t)| format!("{}: {}", n, t.to_rust_decl()))
.collect();
out.push_str(&format!(
"pub extern \"{}\" fn {}({})",
self.abi,
self.c_name,
params_str.join(", ")
));
if self.return_type != X86RustFfiType::Unit {
out.push_str(&format!(" -> {}", self.return_type.to_rust_decl()));
}
out.push_str(" {\n");
out.push_str(&format!(" {}(\n", self.rust_name));
let arg_names: Vec<String> = self.params.iter().map(|(n, _)| n.clone()).collect();
out.push_str(&format!(" {}\n", arg_names.join(",\n ")));
out.push_str(" )\n");
out.push_str("}\n");
out
}
pub fn to_rust_safe_wrapper(&self) -> String {
let mut out = String::new();
out.push_str(&format!("/// Safe wrapper for `{}`.\n", self.c_name));
let wrapper_params: Vec<String> = self
.params
.iter()
.map(|(n, t)| self.safe_param_decl(n, t))
.collect();
let call_args: Vec<String> = self
.params
.iter()
.map(|(n, t)| self.ffi_call_arg(n, t))
.collect();
out.push_str(&format!(
"pub fn {}({})",
self.rust_name,
wrapper_params.join(", ")
));
if self.return_type != X86RustFfiType::Unit {
out.push_str(&format!(" -> {}", self.safe_return_decl()));
}
out.push_str(" {\n");
out.push_str(" unsafe {\n");
out.push_str(&format!(
" {}({})\n",
self.c_name,
call_args.join(", ")
));
out.push_str(" }\n");
out.push_str("}\n");
out
}
pub fn to_repr_c_struct(&self, name: &str, fields: &[(String, X86RustFfiType)]) -> String {
let mut out = String::new();
out.push_str("#[repr(C)]\n");
out.push_str(&format!("pub struct {} {{\n", name));
for (field_name, ty) in fields {
out.push_str(&format!(" pub {}: {},\n", field_name, ty.to_rust_decl()));
}
out.push_str("}\n");
out
}
pub fn to_repr_c_enum(name: &str, repr: &str, variants: &[(String, i32)]) -> String {
let mut out = String::new();
out.push_str(&format!("#[repr({})]\n", repr));
out.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n");
out.push_str(&format!("pub enum {} {{\n", name));
for (var_name, val) in variants {
out.push_str(&format!(" {} = {},\n", var_name, val));
}
out.push_str("}\n");
out
}
pub fn to_repr_c_union(name: &str, fields: &[(String, X86RustFfiType)]) -> String {
let mut out = String::new();
out.push_str("#[repr(C)]\n");
out.push_str(&format!("pub union {} {{\n", name));
for (field_name, ty) in fields {
out.push_str(&format!(" pub {}: {},\n", field_name, ty.to_rust_decl()));
}
out.push_str("}\n");
out
}
pub fn to_cbingen_output(&self) -> String {
let mut out = String::new();
out.push_str("// cbingen auto-generated bindings\n");
out.push_str("#[allow(non_camel_case_types)]\n");
out.push_str("#[allow(non_snake_case)]\n");
out.push_str("#[allow(dead_code)]\n\n");
out.push_str(&self.to_rust_import_block());
out
}
pub fn to_autocxx_output(&self) -> String {
let mut out = String::new();
out.push_str("use autocxx::prelude::*;\n\n");
out.push_str("include_cpp! {\n");
out.push_str(" #include \"x86_interop_ffi.h\"\n");
out.push_str(&format!(" generate!(\"{}\")\n", self.c_name));
out.push_str("}\n");
out
}
pub fn to_rust_binding(&self) -> String {
let mut out = String::new();
out.push_str(&self.to_rust_import_block());
out.push('\n');
out.push_str(&self.to_rust_safe_wrapper());
out
}
pub fn to_c_declaration(&self) -> String {
let ret_c = rust_type_to_c(&self.return_type);
let params_c: Vec<String> = self
.params
.iter()
.map(|(n, t)| format!("{} {}", rust_type_to_c(t), n))
.collect();
format!("{} {}({});", ret_c, self.c_name, params_c.join(", "))
}
pub fn build_rule(&self) -> String {
format!("cargo:rustc-link-lib={}", self.c_name)
}
fn render_attribute(&self, attr: &X86RustFfiAttribute) -> String {
match attr {
X86RustFfiAttribute::NoMangle => "#[no_mangle]\n".into(),
X86RustFfiAttribute::ReprC => "#[repr(C)]\n".into(),
X86RustFfiAttribute::ReprPacked => "#[repr(packed)]\n".into(),
X86RustFfiAttribute::ReprTransparent => "#[repr(transparent)]\n".into(),
X86RustFfiAttribute::ReprAlign(n) => format!("#[repr(align({}))]\n", n),
X86RustFfiAttribute::ExternC => String::new(), X86RustFfiAttribute::LinkName(s) => format!("#[link_name = \"{}\"]\n", s),
X86RustFfiAttribute::UnsafeFn => String::new(),
X86RustFfiAttribute::Doc(s) => format!("/// {}\n", s),
X86RustFfiAttribute::AllowDeadCode => "#[allow(dead_code)]\n".into(),
X86RustFfiAttribute::AllowNonCamelCaseTypes => {
"#[allow(non_camel_case_types)]\n".into()
}
X86RustFfiAttribute::AllowNonSnakeCase => "#[allow(non_snake_case)]\n".into(),
X86RustFfiAttribute::Cold => "#[cold]\n".into(),
X86RustFfiAttribute::Inline => "#[inline]\n".into(),
}
}
fn safe_param_decl(&self, name: &str, ty: &X86RustFfiType) -> String {
match ty {
X86RustFfiType::MutVoidPtr => format!("{}: *mut std::ffi::c_void", name),
X86RustFfiType::ConstVoidPtr => format!("{}: *const std::ffi::c_void", name),
X86RustFfiType::CChar => format!("{}: std::os::raw::c_char", name),
X86RustFfiType::Ref(inner) => format!("{}: &{}", name, inner.to_rust_decl()),
X86RustFfiType::Slice(inner) => format!("{}: &[{}]", name, inner.to_rust_decl()),
X86RustFfiType::Nullable(inner) => {
format!("{}: Option<{}>", name, inner.to_rust_decl())
}
_ => format!("{}: {}", name, ty.to_rust_decl()),
}
}
fn ffi_call_arg(&self, name: &str, ty: &X86RustFfiType) -> String {
match ty {
X86RustFfiType::Ref(_) => name.to_string(),
X86RustFfiType::Slice(_) => format!("{}.as_ptr()", name),
X86RustFfiType::Nullable(_) => {
format!("{}.map_or(std::ptr::null(), |p| p as *const _)", name)
}
_ => name.to_string(),
}
}
fn safe_return_decl(&self) -> String {
match &self.return_type {
X86RustFfiType::ConstVoidPtr => "*const std::ffi::c_void".into(),
X86RustFfiType::MutVoidPtr => "*mut std::ffi::c_void".into(),
X86RustFfiType::Nullable(inner) => format!("Option<{}>", inner.to_rust_decl()),
other => other.to_rust_decl(),
}
}
}
pub fn map_c_type_to_rust(ct: &X86CType) -> X86RustFfiType {
match ct {
X86CType::Void => X86RustFfiType::Unit,
X86CType::Char => X86RustFfiType::CChar,
X86CType::SignedChar => X86RustFfiType::CSchar,
X86CType::UnsignedChar => X86RustFfiType::CUchar,
X86CType::Short => X86RustFfiType::CShort,
X86CType::UnsignedShort => X86RustFfiType::CUshort,
X86CType::Int => X86RustFfiType::CInt,
X86CType::UnsignedInt => X86RustFfiType::CUint,
X86CType::Long => X86RustFfiType::CLong,
X86CType::UnsignedLong => X86RustFfiType::CUlong,
X86CType::LongLong => X86RustFfiType::CLonglong,
X86CType::UnsignedLongLong => X86RustFfiType::CUlonglong,
X86CType::Float => X86RustFfiType::CFloat,
X86CType::Double => X86RustFfiType::CDouble,
X86CType::LongDouble => X86RustFfiType::F64, X86CType::Bool => X86RustFfiType::Bool,
X86CType::Pointer(_) | X86CType::Opaque(_) => X86RustFfiType::MutVoidPtr,
X86CType::ConstPointer(_) => X86RustFfiType::ConstVoidPtr,
X86CType::Array(inner, _) => X86RustFfiType::Slice(Box::new(map_c_type_to_rust(inner))),
X86CType::Struct(name, _) => X86RustFfiType::Custom(name.clone()),
X86CType::Union(name, _) => X86RustFfiType::Custom(name.clone()),
X86CType::Enum(name, _) => X86RustFfiType::Custom(name.clone()),
X86CType::FunctionPointer(_, _) => X86RustFfiType::MutVoidPtr,
X86CType::Typedef(_, inner) => map_c_type_to_rust(inner),
}
}
pub fn rust_type_to_c(rt: &X86RustFfiType) -> String {
match rt {
X86RustFfiType::Unit => "void".into(),
X86RustFfiType::I8 | X86RustFfiType::CSchar => "signed char".into(),
X86RustFfiType::U8 | X86RustFfiType::CUchar => "unsigned char".into(),
X86RustFfiType::I16 | X86RustFfiType::CShort => "short".into(),
X86RustFfiType::U16 | X86RustFfiType::CUshort => "unsigned short".into(),
X86RustFfiType::I32 | X86RustFfiType::CInt => "int".into(),
X86RustFfiType::U32 | X86RustFfiType::CUint => "unsigned int".into(),
X86RustFfiType::I64 | X86RustFfiType::CLong | X86RustFfiType::CLonglong => {
"long long".into()
}
X86RustFfiType::U64 | X86RustFfiType::CUlong | X86RustFfiType::CUlonglong => {
"unsigned long long".into()
}
X86RustFfiType::F32 | X86RustFfiType::CFloat => "float".into(),
X86RustFfiType::F64 | X86RustFfiType::CDouble => "double".into(),
X86RustFfiType::Bool => "_Bool".into(),
X86RustFfiType::CChar => "char".into(),
X86RustFfiType::MutVoidPtr | X86RustFfiType::Opaque(_) => "void*".into(),
X86RustFfiType::ConstVoidPtr => "const void*".into(),
X86RustFfiType::Custom(name) => format!("struct {}", name),
_ => "void*".into(),
}
}
#[derive(Debug, Clone)]
pub struct X86RustStructLayout {
pub name: String,
pub fields: Vec<X86StructField>,
pub total_size: u32,
pub alignment: u32,
}
#[derive(Debug, Clone)]
pub struct X86StructField {
pub name: String,
pub offset: u32,
pub size: u32,
pub ty: X86RustFfiType,
}
impl X86RustStructLayout {
pub fn compute_x86_64(name: &str, fields: &[(String, X86CType)]) -> Self {
let mut field_layouts = Vec::new();
let mut offset = 0u32;
let mut max_align = 1u32;
for (fname, ftype) in fields {
let align = ftype.alignment_x86_64();
let size = ftype.size_x86_64(X86InteropABI::SystemV);
max_align = max_align.max(align);
if offset % align != 0 {
offset += align - (offset % align);
}
field_layouts.push(X86StructField {
name: fname.clone(),
offset,
size,
ty: map_c_type_to_rust(ftype),
});
offset += size;
}
if offset % max_align != 0 {
offset += max_align - (offset % max_align);
}
Self {
name: name.to_string(),
fields: field_layouts,
total_size: offset,
alignment: max_align,
}
}
pub fn verify(&self) -> bool {
let mut expected_offset = 0u32;
for field in &self.fields {
if field.offset != expected_offset {
return false;
}
expected_offset = field.offset + field.size;
if expected_offset % self.alignment != 0 {
}
}
true
}
pub fn to_rust_def(&self) -> String {
let mut out = String::new();
out.push_str("#[repr(C)]\n");
out.push_str("#[derive(Debug, Clone)]\n");
out.push_str(&format!("pub struct {} {{\n", self.name));
for field in &self.fields {
out.push_str(&format!(
" pub {}: {},\n",
field.name,
field.ty.to_rust_decl()
));
}
out.push_str("}\n");
out
}
pub fn to_c_def(&self, fields: &[(String, X86CType)]) -> String {
let mut out = String::new();
out.push_str(&format!("struct {} {{\n", self.name));
for (fname, ftype) in fields {
out.push_str(&format!(" {} {};\n", ftype.to_c_decl(), fname));
}
out.push_str("};\n");
out
}
pub fn to_static_assert_rust(&self) -> String {
format!(
"const _: [(); {}] = [(); std::mem::size_of::<{}>()];",
self.total_size, self.name
)
}
pub fn to_static_assert_c(&self) -> String {
format!(
"_Static_assert(sizeof(struct {0}) == {1}, \"{0} size mismatch\");",
self.name, self.total_size
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PythonBackend {
CPython,
Ctypes,
Cffi,
Pybind11,
Cython,
NumPy,
}
impl fmt::Display for X86PythonBackend {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CPython => write!(f, "cpython"),
Self::Ctypes => write!(f, "ctypes"),
Self::Cffi => write!(f, "cffi"),
Self::Pybind11 => write!(f, "pybind11"),
Self::Cython => write!(f, "cython"),
Self::NumPy => write!(f, "numpy"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86PythonInterop {
pub backend: X86PythonBackend,
pub module_name: String,
pub py_func_name: String,
pub c_func_name: String,
pub return_type: X86CType,
pub params: Vec<X86CParam>,
pub docstring: Option<String>,
pub method_flags: X86PyMethodFlags,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PyMethodFlags {
VarArgs,
VarArgsKeywords,
NoArgs,
SingleArg,
}
impl X86PyMethodFlags {
pub fn to_c_macro(&self) -> String {
match self {
Self::VarArgs => "METH_VARARGS".into(),
Self::VarArgsKeywords => "METH_VARARGS | METH_KEYWORDS".into(),
Self::NoArgs => "METH_NOARGS".into(),
Self::SingleArg => "METH_O".into(),
}
}
}
impl X86PythonInterop {
pub fn new(
backend: X86PythonBackend,
module_name: &str,
py_func_name: &str,
c_func_name: &str,
return_type: X86CType,
) -> Self {
Self {
backend,
module_name: module_name.to_string(),
py_func_name: py_func_name.to_string(),
c_func_name: c_func_name.to_string(),
return_type,
params: Vec::new(),
docstring: None,
method_flags: X86PyMethodFlags::VarArgs,
}
}
pub fn add_param(&mut self, name: &str, ty: X86CType) -> &mut Self {
self.params.push(X86CParam::new(name, ty));
self
}
pub fn with_docstring(mut self, doc: &str) -> Self {
self.docstring = Some(doc.to_string());
self
}
pub fn with_method_flags(mut self, flags: X86PyMethodFlags) -> Self {
self.method_flags = flags;
self
}
pub fn to_python_binding(&self) -> String {
match self.backend {
X86PythonBackend::CPython => self.to_cpython_binding(),
X86PythonBackend::Ctypes => self.to_ctypes_binding(),
X86PythonBackend::Cffi => self.to_cffi_binding(),
X86PythonBackend::Pybind11 => self.to_pybind11_binding(),
X86PythonBackend::Cython => self.to_cython_binding(),
X86PythonBackend::NumPy => self.to_numpy_binding(),
}
}
pub fn to_cpython_binding(&self) -> String {
let mut out = String::new();
out.push_str("#include <Python.h>\n\n");
out.push_str(&format!(
"extern {} {}({});\n\n",
self.return_type.to_c_decl(),
self.c_func_name,
self.params
.iter()
.map(|p| format!("{} {}", p.ty.to_c_decl(), p.name))
.collect::<Vec<_>>()
.join(", ")
));
out.push_str(&format!(
"static PyObject*\n{}_wrapper(PyObject* self, PyObject* args) {{\n",
self.py_func_name
));
let format_chars: String = self
.params
.iter()
.map(|p| py_parse_format_char(&p.ty))
.collect();
let parse_vars: Vec<String> = self
.params
.iter()
.map(|p| format!("&{}_val", p.name))
.collect();
for p in &self.params {
out.push_str(&format!(" {} {}_val;\n", py_ctype_for(&p.ty), p.name));
}
out.push_str(&format!(
" if (!PyArg_ParseTuple(args, \"{}\", {})) {{\n",
format_chars,
parse_vars.join(", ")
));
out.push_str(" return NULL;\n");
out.push_str(" }\n\n");
let call_args: Vec<String> = self
.params
.iter()
.map(|p| py_to_c_cast(&p.ty, &format!("{}_val", p.name)))
.collect();
if self.return_type == X86CType::Void {
out.push_str(&format!(
" {}({});\n",
self.c_func_name,
call_args.join(", ")
));
out.push_str(" Py_RETURN_NONE;\n");
} else {
out.push_str(&format!(
" {} _result = {}({});\n",
py_ctype_for(&self.return_type),
self.c_func_name,
call_args.join(", ")
));
out.push_str(&format!(
" return {};\n",
c_to_py_build(&self.return_type, "_result")
));
}
out.push_str("}\n\n");
out.push_str("static PyMethodDef module_methods[] = {\n");
let doc = self.docstring.as_deref().unwrap_or("");
out.push_str(&format!(
" {{\"{}\", {}_wrapper, {}, \"{}\"}},\n",
self.py_func_name,
self.py_func_name,
self.method_flags.to_c_macro(),
doc
));
out.push_str(" {NULL, NULL, 0, NULL}\n");
out.push_str("};\n\n");
out.push_str(&format!(
"static struct PyModuleDef {}_module = {{\n",
self.module_name
));
out.push_str(" PyModuleDef_HEAD_INIT,\n");
out.push_str(&format!(" \"{}\",\n", self.module_name));
out.push_str(" NULL,\n");
out.push_str(" -1,\n");
out.push_str(" module_methods\n");
out.push_str("};\n\n");
out.push_str(&format!(
"PyMODINIT_FUNC PyInit_{}(void) {{\n",
self.module_name
));
out.push_str(&format!(
" return PyModule_Create(&{}_module);\n",
self.module_name
));
out.push_str("}\n");
out
}
pub fn to_ctypes_binding(&self) -> String {
let mut out = String::new();
out.push_str("import ctypes\n\n");
out.push_str(&format!("# Load the shared library\n",));
out.push_str(&format!(
"_lib = ctypes.CDLL(\"lib{}.so\")\n\n",
self.module_name
));
let argtypes: Vec<String> = self
.params
.iter()
.map(|p| ctype_to_python_ctypes_type(&p.ty))
.collect();
out.push_str(&format!(
"_lib.{}.argtypes = [{}]\n",
self.c_func_name,
argtypes.join(", ")
));
out.push_str(&format!(
"_lib.{}.restype = {}\n\n",
self.c_func_name,
ctype_to_python_ctypes_type(&self.return_type)
));
let py_params: Vec<String> = self.params.iter().map(|p| p.name.clone()).collect();
out.push_str(&format!(
"def {}({}):\n",
self.py_func_name,
py_params.join(", ")
));
if let Some(doc) = &self.docstring {
out.push_str(&format!(" \"\"\"{}\"\"\"\n", doc));
}
out.push_str(&format!(
" return _lib.{}({})\n",
self.c_func_name,
py_params.join(", ")
));
out
}
pub fn to_cffi_binding(&self) -> String {
let mut out = String::new();
out.push_str("from cffi import FFI\n\n");
out.push_str("ffi = FFI()\n\n");
out.push_str("ffi.cdef(\"\"\"\n");
out.push_str(&format!(
" {} {}({});\n",
self.return_type.to_c_decl(),
self.c_func_name,
self.params
.iter()
.map(|p| format!("{} {}", p.ty.to_c_decl(), p.name))
.collect::<Vec<_>>()
.join(", ")
));
out.push_str("\"\"\")\n\n");
out.push_str(&format!(
"_lib = ffi.dlopen(\"lib{}.so\")\n\n",
self.module_name
));
let py_params: Vec<String> = self.params.iter().map(|p| p.name.clone()).collect();
out.push_str(&format!(
"def {}({}):\n",
self.py_func_name,
py_params.join(", ")
));
out.push_str(&format!(
" return _lib.{}({})\n",
self.c_func_name,
py_params.join(", ")
));
out
}
pub fn to_pybind11_binding(&self) -> String {
let mut out = String::new();
out.push_str("#include <pybind11/pybind11.h>\n\n");
out.push_str("namespace py = pybind11;\n\n");
out.push_str(&format!(
"extern \"C\" {} {}({});\n\n",
self.return_type.to_c_decl(),
self.c_func_name,
self.params
.iter()
.map(|p| format!("{} {}", p.ty.to_c_decl(), p.name))
.collect::<Vec<_>>()
.join(", ")
));
out.push_str(&format!("PYBIND11_MODULE({}, m) {{\n", self.module_name));
if let Some(doc) = &self.docstring {
out.push_str(&format!(" m.doc() = \"{}\";\n", doc));
}
out.push_str(&format!(
" m.def(\"{}\", &{}, \"{}\");\n",
self.py_func_name,
self.c_func_name,
self.docstring.as_deref().unwrap_or("")
));
out.push_str("}\n");
out
}
pub fn to_cython_binding(&self) -> String {
let mut out = String::new();
out.push_str("# cython: language_level=3\n\n");
out.push_str("cdef extern from \"x86_interop_ffi.h\":\n");
let params_str: Vec<String> = self
.params
.iter()
.map(|p| {
let cy_type = ctype_to_cython_type(&p.ty);
format!("{} {}", cy_type, p.name)
})
.collect();
let ret_cy = ctype_to_cython_type(&self.return_type);
out.push_str(&format!(
" {} {}({})\n\n",
ret_cy,
self.c_func_name,
params_str.join(", ")
));
let py_params: Vec<String> = self.params.iter().map(|p| format!("{}", p.name)).collect();
out.push_str(&format!(
"def {}({}):\n",
self.py_func_name,
py_params.join(", ")
));
if let Some(doc) = &self.docstring {
out.push_str(&format!(" \"\"\"{}\"\"\"\n", doc));
}
out.push_str(&format!(
" return {}({})\n",
self.c_func_name,
py_params.join(", ")
));
out
}
pub fn to_numpy_binding(&self) -> String {
let mut out = String::new();
out.push_str("#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n");
out.push_str("#include <Python.h>\n");
out.push_str("#include <numpy/arrayobject.h>\n\n");
out.push_str(&format!(
"static PyObject*\n{}_numpy_wrapper(PyObject* self, PyObject* args) {{\n",
self.py_func_name
));
out.push_str(" PyArrayObject* input_array = NULL;\n");
out.push_str(" if (!PyArg_ParseTuple(args, \"O!\", &PyArray_Type, &input_array)) {\n");
out.push_str(" return NULL;\n");
out.push_str(" }\n\n");
out.push_str(" // Get array properties\n");
out.push_str(" npy_intp* dims = PyArray_DIMS(input_array);\n");
out.push_str(" int ndim = PyArray_NDIM(input_array);\n");
out.push_str(" void* data = PyArray_DATA(input_array);\n\n");
out.push_str(" // Process with C function...\n");
out.push_str(&format!(" {}(data);\n", self.c_func_name));
out.push_str(" Py_RETURN_NONE;\n");
out.push_str("}\n");
out
}
pub fn build_rule(&self) -> String {
match self.backend {
X86PythonBackend::CPython | X86PythonBackend::NumPy => {
format!(
"gcc -shared -o {}.so {}_wrapper.c $(python3-config --includes --ldflags)",
self.module_name, self.module_name
)
}
X86PythonBackend::Pybind11 => {
format!(
"g++ -shared -fPIC -o {}.so {}_binding.cpp $(python3 -m pybind11 --includes)",
self.module_name, self.module_name
)
}
X86PythonBackend::Cython => {
format!(
"cython {}.pyx && gcc -shared -fPIC -o {}.so {}.c $(python3-config --includes --ldflags)",
self.module_name, self.module_name, self.module_name
)
}
_ => format!("# No build rule needed for {}", self.backend),
}
}
}
pub fn py_parse_format_char(ct: &X86CType) -> &'static str {
match ct {
X86CType::Char | X86CType::SignedChar => "b",
X86CType::UnsignedChar => "B",
X86CType::Short => "h",
X86CType::UnsignedShort => "H",
X86CType::Int => "i",
X86CType::UnsignedInt => "I",
X86CType::Long => "l",
X86CType::UnsignedLong => "k",
X86CType::LongLong => "L",
X86CType::UnsignedLongLong => "K",
X86CType::Float => "f",
X86CType::Double => "d",
X86CType::Bool => "p", X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => "O", _ => "O",
}
}
pub fn py_ctype_for(ct: &X86CType) -> String {
match ct {
X86CType::Char | X86CType::SignedChar => "signed char".into(),
X86CType::UnsignedChar => "unsigned char".into(),
X86CType::Short => "short".into(),
X86CType::UnsignedShort => "unsigned short".into(),
X86CType::Int => "int".into(),
X86CType::UnsignedInt => "unsigned int".into(),
X86CType::Long => "long".into(),
X86CType::UnsignedLong => "unsigned long".into(),
X86CType::LongLong => "long long".into(),
X86CType::UnsignedLongLong => "unsigned long long".into(),
X86CType::Float => "float".into(),
X86CType::Double => "double".into(),
X86CType::Bool => "int".into(),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => {
"PyObject*".into()
}
_ => "PyObject*".into(),
}
}
pub fn py_to_c_cast(ct: &X86CType, var: &str) -> String {
match ct {
X86CType::Pointer(_) | X86CType::ConstPointer(_) => {
format!("PyLong_AsVoidPtr({})", var)
}
_ => var.to_string(),
}
}
pub fn c_to_py_build(ct: &X86CType, var: &str) -> String {
match ct {
X86CType::Int => format!("PyLong_FromLong({})", var),
X86CType::UnsignedInt => format!("PyLong_FromUnsignedLong({})", var),
X86CType::Long => format!("PyLong_FromLong({})", var),
X86CType::UnsignedLong => format!("PyLong_FromUnsignedLong({})", var),
X86CType::LongLong => format!("PyLong_FromLongLong({})", var),
X86CType::UnsignedLongLong => format!("PyLong_FromUnsignedLongLong({})", var),
X86CType::Float | X86CType::Double => format!("PyFloat_FromDouble({})", var),
X86CType::Bool => format!("PyBool_FromLong({})", var),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => {
format!("PyLong_FromVoidPtr({})", var)
}
X86CType::Char | X86CType::SignedChar | X86CType::UnsignedChar => {
format!("PyLong_FromLong({})", var)
}
_ => format!("PyLong_FromLong((long){})", var),
}
}
pub fn ctype_to_python_ctypes_type(ct: &X86CType) -> String {
match ct {
X86CType::Void => "None".into(),
X86CType::Char => "ctypes.c_char".into(),
X86CType::SignedChar => "ctypes.c_byte".into(),
X86CType::UnsignedChar => "ctypes.c_ubyte".into(),
X86CType::Short => "ctypes.c_short".into(),
X86CType::UnsignedShort => "ctypes.c_ushort".into(),
X86CType::Int => "ctypes.c_int".into(),
X86CType::UnsignedInt => "ctypes.c_uint".into(),
X86CType::Long => "ctypes.c_long".into(),
X86CType::UnsignedLong => "ctypes.c_ulong".into(),
X86CType::LongLong => "ctypes.c_longlong".into(),
X86CType::UnsignedLongLong => "ctypes.c_ulonglong".into(),
X86CType::Float => "ctypes.c_float".into(),
X86CType::Double => "ctypes.c_double".into(),
X86CType::Bool => "ctypes.c_bool".into(),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => {
"ctypes.c_void_p".into()
}
_ => "ctypes.c_void_p".into(),
}
}
pub fn ctype_to_cython_type(ct: &X86CType) -> String {
match ct {
X86CType::Void => "void".into(),
X86CType::Char | X86CType::SignedChar => "char".into(),
X86CType::UnsignedChar => "unsigned char".into(),
X86CType::Short => "short".into(),
X86CType::UnsignedShort => "unsigned short".into(),
X86CType::Int => "int".into(),
X86CType::UnsignedInt => "unsigned int".into(),
X86CType::Long => "long".into(),
X86CType::UnsignedLong => "unsigned long".into(),
X86CType::LongLong => "long long".into(),
X86CType::UnsignedLongLong => "unsigned long long".into(),
X86CType::Float => "float".into(),
X86CType::Double => "double".into(),
X86CType::LongDouble => "long double".into(),
X86CType::Bool => "bint".into(),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => "void*".into(),
X86CType::FunctionPointer(_, _) => "void*".into(),
X86CType::Struct(name, _) => name.clone(),
X86CType::Enum(name, _) => name.clone(),
_ => "void*".into(),
}
}
#[derive(Debug, Clone)]
pub struct X86GoInterop {
pub package_name: String,
pub go_func_name: String,
pub c_func_name: String,
pub return_type: X86CType,
pub params: Vec<X86CParam>,
pub cgo_directives: Vec<X86CgoDirective>,
pub is_callback: bool,
pub go_doc: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X86CgoDirective {
CFlags(String),
LdFlags(String),
PkgConfig(String),
CppFlags(String),
CxxFlags(String),
PlatformCFlags(String, String),
PlatformLdFlags(String, String),
}
impl X86CgoDirective {
pub fn to_directive(&self) -> String {
match self {
Self::CFlags(s) => format!("#cgo CFLAGS: {}", s),
Self::LdFlags(s) => format!("#cgo LDFLAGS: {}", s),
Self::PkgConfig(s) => format!("#cgo pkg-config: {}", s),
Self::CppFlags(s) => format!("#cgo CPPFLAGS: {}", s),
Self::CxxFlags(s) => format!("#cgo CXXFLAGS: {}", s),
Self::PlatformCFlags(platform, flags) => format!("#cgo {} CFLAGS: {}", platform, flags),
Self::PlatformLdFlags(platform, flags) => {
format!("#cgo {} LDFLAGS: {}", platform, flags)
}
}
}
}
impl X86GoInterop {
pub fn new(
package_name: &str,
go_func_name: &str,
c_func_name: &str,
return_type: X86CType,
) -> Self {
Self {
package_name: package_name.to_string(),
go_func_name: go_func_name.to_string(),
c_func_name: c_func_name.to_string(),
return_type,
params: Vec::new(),
cgo_directives: Vec::new(),
is_callback: false,
go_doc: None,
}
}
pub fn new_callback(package_name: &str, go_func_name: &str, return_type: X86CType) -> Self {
let mut entry = Self::new(package_name, go_func_name, "", return_type);
entry.is_callback = true;
entry
}
pub fn add_param(&mut self, name: &str, ty: X86CType) -> &mut Self {
self.params.push(X86CParam::new(name, ty));
self
}
pub fn with_cgo_directive(mut self, dir: X86CgoDirective) -> Self {
self.cgo_directives.push(dir);
self
}
pub fn with_doc(mut self, doc: &str) -> Self {
self.go_doc = Some(doc.to_string());
self
}
pub fn to_go_binding(&self) -> String {
let mut out = String::new();
out.push_str(&format!("package {}\n\n", self.package_name));
out.push_str("/*\n");
for dir in &self.cgo_directives {
out.push_str(&dir.to_directive());
out.push('\n');
}
out.push_str("\n");
out.push_str("#include \"x86_interop_ffi.h\"\n");
out.push_str("*/\n");
out.push_str("import \"C\"\n\n");
let needs_unsafe = self.params.iter().any(|p| {
matches!(
p.ty,
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_)
)
}) || matches!(
self.return_type,
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_)
);
if needs_unsafe {
out.push_str("import \"unsafe\"\n\n");
}
if !self.is_callback {
if let Some(doc) = &self.go_doc {
out.push_str(&format!("// {}\n", doc));
}
let go_params: Vec<String> = self
.params
.iter()
.map(|p| format!("{} {}", p.name, ctype_to_go_type(&p.ty)))
.collect();
let go_ret = ctype_to_go_type(&self.return_type);
out.push_str(&format!(
"func {}({}) {} {{\n",
self.go_func_name,
go_params.join(", "),
go_ret
));
let c_args: Vec<String> = self
.params
.iter()
.map(|p| go_to_c_cast(&p.ty, &p.name))
.collect();
if self.return_type == X86CType::Void {
out.push_str(&format!(
" C.{}({})\n",
self.c_func_name,
c_args.join(", ")
));
} else if needs_unsafe {
out.push_str(&format!(
" ret := C.{}({})\n",
self.c_func_name,
c_args.join(", ")
));
out.push_str(&format!(
" return {}\n",
c_to_go_cast(&self.return_type, "ret")
));
} else {
out.push_str(&format!(
" return {}(C.{}({}))\n",
go_ret,
self.c_func_name,
c_args.join(", ")
));
}
out.push_str("}\n");
} else {
out.push_str(&format!("//export {}\n", self.go_func_name));
let go_params: Vec<String> = self
.params
.iter()
.map(|p| format!("{} C.{}", p.name, ctype_to_cgo_type(&p.ty)))
.collect();
let go_ret = if self.return_type == X86CType::Void {
String::new()
} else {
format!(" {}", ctype_to_cgo_type(&self.return_type))
};
out.push_str(&format!(
"func {}({}){} {{\n",
self.go_func_name,
go_params.join(", "),
go_ret
));
out.push_str(" // Go implementation\n");
out.push_str("}\n");
}
out
}
pub fn to_c_declaration(&self) -> String {
let ret_c = self.return_type.to_c_decl();
let params_c: Vec<String> = self
.params
.iter()
.map(|p| format!("{} {}", p.ty.to_c_decl(), p.name))
.collect();
format!("{} {}({});", ret_c, self.c_func_name, params_c.join(", "))
}
pub fn build_rule(&self) -> String {
format!(
"CGO_ENABLED=1 go build -o lib{}.so -buildmode=c-shared",
self.package_name
)
}
}
pub fn ctype_to_go_type(ct: &X86CType) -> String {
match ct {
X86CType::Void => String::new(),
X86CType::Char | X86CType::SignedChar => "int8".into(),
X86CType::UnsignedChar => "uint8".into(),
X86CType::Short => "int16".into(),
X86CType::UnsignedShort => "uint16".into(),
X86CType::Int => "int32".into(),
X86CType::UnsignedInt => "uint32".into(),
X86CType::Long => "int64".into(),
X86CType::UnsignedLong => "uint64".into(),
X86CType::LongLong => "int64".into(),
X86CType::UnsignedLongLong => "uint64".into(),
X86CType::Float => "float32".into(),
X86CType::Double => "float64".into(),
X86CType::Bool => "bool".into(),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => {
"unsafe.Pointer".into()
}
X86CType::Array(inner, _) => format!("[]{}", ctype_to_go_type(inner)),
_ => "unsafe.Pointer".into(),
}
}
pub fn ctype_to_cgo_type(ct: &X86CType) -> String {
match ct {
X86CType::Char => "char".into(),
X86CType::SignedChar => "schar".into(),
X86CType::UnsignedChar => "uchar".into(),
X86CType::Short => "short".into(),
X86CType::UnsignedShort => "ushort".into(),
X86CType::Int => "int".into(),
X86CType::UnsignedInt => "uint".into(),
X86CType::Long => "long".into(),
X86CType::UnsignedLong => "ulong".into(),
X86CType::LongLong => "longlong".into(),
X86CType::UnsignedLongLong => "ulonglong".into(),
X86CType::Float => "float".into(),
X86CType::Double => "double".into(),
_ => "void*".into(), }
}
pub fn go_to_c_cast(ct: &X86CType, var: &str) -> String {
match ct {
X86CType::Char | X86CType::SignedChar => format!("C.char({})", var),
X86CType::UnsignedChar => format!("C.uchar({})", var),
X86CType::Short => format!("C.short({})", var),
X86CType::UnsignedShort => format!("C.ushort({})", var),
X86CType::Int => format!("C.int({})", var),
X86CType::UnsignedInt => format!("C.uint({})", var),
X86CType::Long => format!("C.long({})", var),
X86CType::UnsignedLong => format!("C.ulong({})", var),
X86CType::LongLong => format!("C.longlong({})", var),
X86CType::UnsignedLongLong => format!("C.ulonglong({})", var),
X86CType::Float => format!("C.float({})", var),
X86CType::Double => format!("C.double({})", var),
X86CType::Bool => format!("C._Bool({})", var),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => {
format!("unsafe.Pointer({})", var)
}
_ => var.to_string(),
}
}
pub fn c_to_go_cast(ct: &X86CType, var: &str) -> String {
match ct {
X86CType::Char | X86CType::SignedChar => format!("int8({})", var),
X86CType::UnsignedChar => format!("uint8({})", var),
X86CType::Short => format!("int16({})", var),
X86CType::UnsignedShort => format!("uint16({})", var),
X86CType::Int => format!("int32({})", var),
X86CType::UnsignedInt => format!("uint32({})", var),
X86CType::Long | X86CType::LongLong => format!("int64({})", var),
X86CType::UnsignedLong | X86CType::UnsignedLongLong => format!("uint64({})", var),
X86CType::Float => format!("float32({})", var),
X86CType::Double => format!("float64({})", var),
X86CType::Bool => format!("bool({})", var),
_ => var.to_string(),
}
}
pub fn go_string_to_c_string(go_var: &str, c_var: &str) -> String {
format!(
"{} := C.CString({})\n defer C.free(unsafe.Pointer({}))",
c_var, go_var, c_var
)
}
pub fn c_string_to_go_string(c_var: &str, go_var: &str) -> String {
format!("{} := C.GoString({})", go_var, c_var)
}
#[derive(Debug, Clone)]
pub struct X86JavaInterop {
pub java_class: String,
pub java_method: String,
pub java_signature: String,
pub c_func_name: String,
pub return_type: X86CType,
pub params: Vec<X86CParam>,
pub is_static: bool,
pub is_constructor: bool,
pub exception_check: Option<String>,
}
impl X86JavaInterop {
pub fn new(
java_class: &str,
java_method: &str,
java_signature: &str,
return_type: X86CType,
) -> Self {
let jni_name = format!(
"Java_{}_{}",
java_class.replace('/', "_").replace('.', "_"),
java_method.replace('_', "_1")
);
Self {
java_class: java_class.to_string(),
java_method: java_method.to_string(),
java_signature: java_signature.to_string(),
c_func_name: jni_name,
return_type,
params: Vec::new(),
is_static: false,
is_constructor: false,
exception_check: None,
}
}
pub fn new_constructor(java_class: &str, java_signature: &str) -> Self {
let mut entry = Self::new(
java_class,
"<init>",
java_signature,
X86CType::Opaque("jobject".into()),
);
entry.is_constructor = true;
entry
}
pub fn add_param(&mut self, name: &str, ty: X86CType) -> &mut Self {
self.params.push(X86CParam::new(name, ty));
self
}
pub fn with_static(mut self) -> Self {
self.is_static = true;
self
}
pub fn with_exception_check(mut self, exc_class: &str) -> Self {
self.exception_check = Some(exc_class.to_string());
self
}
pub fn to_java_binding(&self) -> String {
let mut out = String::new();
out.push_str("#include <jni.h>\n\n");
let ret_jni = ctype_to_jni_type(&self.return_type);
let second_param = if self.is_static {
"jclass cls"
} else {
"jobject obj"
};
let mut all_params = vec![second_param.to_string()];
for p in &self.params {
all_params.push(format!("{} {}", ctype_to_jni_type(&p.ty), p.name));
}
out.push_str(&format!(
"JNIEXPORT {} JNICALL\n{}(JNIEnv* env, {}) {{\n",
ret_jni,
self.c_func_name,
all_params.join(", ")
));
out.push_str(" // Native implementation\n");
if self.is_constructor {
out.push_str(" // Construct object\n");
}
if let Some(exc_class) = &self.exception_check {
out.push_str(&format!(" if ((*env)->ExceptionCheck(env)) {{\n"));
out.push_str(&format!(
" jclass exc = (*env)->FindClass(env, \"{}\");\n",
exc_class
));
out.push_str(" if (exc != NULL) {\n");
out.push_str(" (*env)->ThrowNew(env, exc, \"Error in native call\");\n");
out.push_str(" }\n");
out.push_str(&format!(
" return {};\n",
jni_default_return(&self.return_type)
));
out.push_str(" }\n");
}
out.push_str(&format!(
" return {};\n",
jni_default_return(&self.return_type)
));
out.push_str("}\n");
out
}
pub fn generate_signature_from_params(params: &[X86CParam], return_type: &X86CType) -> String {
let param_sigs: String = params
.iter()
.map(|p| ctype_to_jni_signature(&p.ty))
.collect();
let ret_sig = ctype_to_jni_signature(return_type);
format!("({}){}", param_sigs, ret_sig)
}
pub fn build_rule(&self) -> String {
format!(
"gcc -shared -fPIC -o lib{}.so {}_jni.c -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux",
self.java_class.replace('/', "_"),
self.java_class.replace('/', "_")
)
}
}
pub fn ctype_to_jni_type(ct: &X86CType) -> String {
match ct {
X86CType::Void => "void".into(),
X86CType::Bool => "jboolean".into(),
X86CType::Char | X86CType::SignedChar => "jbyte".into(),
X86CType::UnsignedChar => "jbyte".into(),
X86CType::Short => "jshort".into(),
X86CType::UnsignedShort => "jshort".into(),
X86CType::Int => "jint".into(),
X86CType::UnsignedInt => "jint".into(),
X86CType::Long => "jlong".into(),
X86CType::UnsignedLong => "jlong".into(),
X86CType::LongLong => "jlong".into(),
X86CType::UnsignedLongLong => "jlong".into(),
X86CType::Float => "jfloat".into(),
X86CType::Double => "jdouble".into(),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => "jobject".into(),
X86CType::Array(inner, _) => format!("{}Array", ctype_to_jni_type(inner)),
_ => "jobject".into(),
}
}
pub fn ctype_to_jni_signature(ct: &X86CType) -> String {
match ct {
X86CType::Void => "V".into(),
X86CType::Bool => "Z".into(),
X86CType::Char | X86CType::SignedChar => "B".into(),
X86CType::UnsignedChar => "B".into(),
X86CType::Short => "S".into(),
X86CType::UnsignedShort => "S".into(),
X86CType::Int => "I".into(),
X86CType::UnsignedInt => "I".into(),
X86CType::Long => "J".into(),
X86CType::UnsignedLong => "J".into(),
X86CType::LongLong => "J".into(),
X86CType::UnsignedLongLong => "J".into(),
X86CType::Float => "F".into(),
X86CType::Double => "D".into(),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => {
"Ljava/lang/Object;".into()
}
_ => "Ljava/lang/Object;".into(),
}
}
pub fn jni_default_return(ct: &X86CType) -> String {
match ct {
X86CType::Void => String::new(),
X86CType::Bool => "JNI_FALSE".into(),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => "NULL".into(),
_ => "0".into(),
}
}
#[derive(Debug, Clone)]
pub struct X86DotNetInterop {
pub namespace: String,
pub class_name: String,
pub method_name: String,
pub c_func_name: String,
pub dll_name: String,
pub return_type: X86CType,
pub params: Vec<X86CParam>,
pub calling_convention: X86DotNetCallConv,
pub char_set: X86DotNetCharSet,
pub set_last_error: bool,
pub marshaling: Vec<X86DotNetMarshal>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86DotNetCallConv {
WinApi,
Cdecl,
StdCall,
FastCall,
ThisCall,
}
impl fmt::Display for X86DotNetCallConv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::WinApi => write!(f, "WinApi"),
Self::Cdecl => write!(f, "Cdecl"),
Self::StdCall => write!(f, "StdCall"),
Self::FastCall => write!(f, "FastCall"),
Self::ThisCall => write!(f, "ThisCall"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86DotNetCharSet {
Ansi,
Unicode,
Auto,
None,
}
impl fmt::Display for X86DotNetCharSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Ansi => write!(f, "CharSet.Ansi"),
Self::Unicode => write!(f, "CharSet.Unicode"),
Self::Auto => write!(f, "CharSet.Auto"),
Self::None => write!(f, "CharSet.None"),
}
}
}
#[derive(Debug, Clone)]
pub enum X86DotNetMarshal {
LPStr,
LPWStr,
LPTStr,
BStr,
ByValArray(usize),
SafeArray,
IUnknown,
IDispatch,
Struct,
Bool,
SysInt,
SysUInt,
FunctionPtr,
}
impl X86DotNetMarshal {
pub fn to_attribute(&self) -> String {
match self {
Self::LPStr => "[MarshalAs(UnmanagedType.LPStr)]".into(),
Self::LPWStr => "[MarshalAs(UnmanagedType.LPWStr)]".into(),
Self::LPTStr => "[MarshalAs(UnmanagedType.LPTStr)]".into(),
Self::BStr => "[MarshalAs(UnmanagedType.BStr)]".into(),
Self::ByValArray(n) => {
format!("[MarshalAs(UnmanagedType.ByValArray, SizeConst = {})]", n)
}
Self::SafeArray => "[MarshalAs(UnmanagedType.SafeArray)]".into(),
Self::IUnknown => "[MarshalAs(UnmanagedType.IUnknown)]".into(),
Self::IDispatch => "[MarshalAs(UnmanagedType.IDispatch)]".into(),
Self::Struct => "[MarshalAs(UnmanagedType.Struct)]".into(),
Self::Bool => "[MarshalAs(UnmanagedType.Bool)]".into(),
Self::SysInt => "[MarshalAs(UnmanagedType.SysInt)]".into(),
Self::SysUInt => "[MarshalAs(UnmanagedType.SysUInt)]".into(),
Self::FunctionPtr => "[MarshalAs(UnmanagedType.FunctionPtr)]".into(),
}
}
}
impl X86DotNetInterop {
pub fn new(
namespace: &str,
class_name: &str,
method_name: &str,
c_func_name: &str,
dll_name: &str,
return_type: X86CType,
) -> Self {
Self {
namespace: namespace.to_string(),
class_name: class_name.to_string(),
method_name: method_name.to_string(),
c_func_name: c_func_name.to_string(),
dll_name: dll_name.to_string(),
return_type,
params: Vec::new(),
calling_convention: X86DotNetCallConv::Cdecl,
char_set: X86DotNetCharSet::Ansi,
set_last_error: false,
marshaling: Vec::new(),
}
}
pub fn add_param(&mut self, name: &str, ty: X86CType) -> &mut Self {
self.params.push(X86CParam::new(name, ty));
self
}
pub fn with_call_conv(mut self, cc: X86DotNetCallConv) -> Self {
self.calling_convention = cc;
self
}
pub fn with_char_set(mut self, cs: X86DotNetCharSet) -> Self {
self.char_set = cs;
self
}
pub fn with_set_last_error(mut self) -> Self {
self.set_last_error = true;
self
}
pub fn with_marshal(mut self, m: X86DotNetMarshal) -> Self {
self.marshaling.push(m);
self
}
pub fn to_dotnet_binding(&self) -> String {
let mut out = String::new();
out.push_str("using System;\n");
out.push_str("using System.Runtime.InteropServices;\n\n");
if !self.namespace.is_empty() {
out.push_str(&format!("namespace {} {{\n", self.namespace));
}
out.push_str(&format!("public static class {} {{\n", self.class_name));
out.push_str(&format!(" [DllImport(\"{}\"", self.dll_name));
if self.calling_convention != X86DotNetCallConv::WinApi {
out.push_str(&format!(
", CallingConvention = CallingConvention.{}",
self.calling_convention
));
}
if self.char_set != X86DotNetCharSet::None {
out.push_str(&format!(", CharSet = {}", self.char_set));
}
if self.set_last_error {
out.push_str(", SetLastError = true");
}
out.push_str(")]\n");
let dotnet_ret = ctype_to_dotnet_type(&self.return_type);
let extern_mod = "static extern";
out.push_str(&format!(
" public {} {} {}(\n",
extern_mod, dotnet_ret, self.method_name
));
let params: Vec<String> = self
.params
.iter()
.enumerate()
.map(|(i, p)| {
let mut s = String::new();
if i < self.marshaling.len() {
s.push_str(" ");
s.push_str(&self.marshaling[i].to_attribute());
s.push('\n');
}
s.push_str(&format!(
" {} {}",
ctype_to_dotnet_type(&p.ty),
p.name
));
s
})
.collect();
out.push_str(¶ms.join(",\n"));
out.push_str("\n );\n");
out.push_str("}\n");
if !self.namespace.is_empty() {
out.push_str("}\n");
}
out
}
pub fn to_reverse_pinvoke(&self) -> String {
let mut out = String::new();
out.push_str(&format!(
"[UnmanagedFunctionPointer(CallingConvention.{})]\n",
self.calling_convention
));
let dotnet_ret = ctype_to_dotnet_type(&self.return_type);
let params: Vec<String> = self
.params
.iter()
.map(|p| format!("{} {}", ctype_to_dotnet_type(&p.ty), p.name))
.collect();
out.push_str(&format!(
"public delegate {} {}({});\n",
dotnet_ret,
format!("{}Delegate", self.method_name),
params.join(", ")
));
out
}
pub fn build_rule(&self) -> String {
format!(
"csc /target:library /out:{}.dll {}_pinvoke.cs",
self.class_name, self.class_name
)
}
}
pub fn ctype_to_dotnet_type(ct: &X86CType) -> String {
match ct {
X86CType::Void => "void".into(),
X86CType::Char => "byte".into(),
X86CType::SignedChar => "sbyte".into(),
X86CType::UnsignedChar => "byte".into(),
X86CType::Short => "short".into(),
X86CType::UnsignedShort => "ushort".into(),
X86CType::Int => "int".into(),
X86CType::UnsignedInt => "uint".into(),
X86CType::Long => "long".into(),
X86CType::UnsignedLong => "ulong".into(),
X86CType::LongLong => "long".into(),
X86CType::UnsignedLongLong => "ulong".into(),
X86CType::Float => "float".into(),
X86CType::Double => "double".into(),
X86CType::Bool => "bool".into(),
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => "IntPtr".into(),
X86CType::FunctionPointer(_, _) => "IntPtr".into(),
X86CType::Struct(name, _) => name.clone(),
X86CType::Enum(name, _) => name.clone(),
_ => "IntPtr".into(),
}
}
#[derive(Debug, Clone)]
pub struct X86WASMInterop {
pub module_name: String,
pub wasm_func_name: String,
pub c_func_name: String,
pub direction: X86WASMDirection,
pub wasm_signature: X86WASMSignature,
pub wasi_api: Option<String>,
pub emscripten_config: Option<X86EmscriptenConfig>,
pub wasm_inline: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86WASMDirection {
Import,
Export,
Both,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct X86WASMSignature {
pub params: Vec<X86WASMType>,
pub results: Vec<X86WASMType>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86WASMType {
I32,
I64,
F32,
F64,
V128,
FuncRef,
ExternRef,
}
impl X86WASMType {
pub fn to_wasm_text(&self) -> &'static str {
match self {
Self::I32 => "i32",
Self::I64 => "i64",
Self::F32 => "f32",
Self::F64 => "f64",
Self::V128 => "v128",
Self::FuncRef => "funcref",
Self::ExternRef => "externref",
}
}
}
#[derive(Debug, Clone, Default)]
pub struct X86EmscriptenConfig {
pub keepalive: bool,
pub em_js: Option<String>,
pub em_asm: Option<String>,
pub ccall_name: Option<String>,
pub use_embind: bool,
pub exported_functions: Vec<String>,
pub extra_runtime_methods: Vec<String>,
}
impl X86WASMInterop {
pub fn new(
module_name: &str,
wasm_func_name: &str,
c_func_name: &str,
direction: X86WASMDirection,
) -> Self {
Self {
module_name: module_name.to_string(),
wasm_func_name: wasm_func_name.to_string(),
c_func_name: c_func_name.to_string(),
direction,
wasm_signature: X86WASMSignature {
params: Vec::new(),
results: Vec::new(),
},
wasi_api: None,
emscripten_config: None,
wasm_inline: false,
}
}
pub fn add_param(&mut self, ty: X86WASMType) -> &mut Self {
self.wasm_signature.params.push(ty);
self
}
pub fn set_result(&mut self, ty: X86WASMType) -> &mut Self {
self.wasm_signature.results.push(ty);
self
}
pub fn with_wasi_api(mut self, api: &str) -> Self {
self.wasi_api = Some(api.to_string());
self
}
pub fn with_emscripten(mut self, config: X86EmscriptenConfig) -> Self {
self.emscripten_config = Some(config);
self
}
pub fn to_wasm_binding(&self) -> String {
let mut out = String::new();
out.push_str(";; Auto-generated WASM interop binding\n\n");
let param_wat: Vec<String> = self
.wasm_signature
.params
.iter()
.map(|t| format!("(param {})", t.to_wasm_text()))
.collect();
let result_wat: Vec<String> = self
.wasm_signature
.results
.iter()
.map(|t| format!("(result {})", t.to_wasm_text()))
.collect();
match self.direction {
X86WASMDirection::Import => {
out.push_str(&format!(
"(import \"{}\" \"{}\" (func ${}",
self.module_name, self.wasm_func_name, self.wasm_func_name
));
if !param_wat.is_empty() {
out.push(' ');
out.push_str(¶m_wat.join(" "));
}
if !result_wat.is_empty() {
out.push(' ');
out.push_str(&result_wat.join(" "));
}
out.push_str("))\n");
}
X86WASMDirection::Export => {
out.push_str(&format!("(func ${}", self.wasm_func_name));
if !param_wat.is_empty() {
out.push(' ');
out.push_str(¶m_wat.join(" "));
}
if !result_wat.is_empty() {
out.push(' ');
out.push_str(&result_wat.join(" "));
}
out.push_str(&format!(
"\n ;; C implementation: {}\n)\n",
self.c_func_name
));
out.push_str(&format!(
"(export \"{}\" (func ${}))\n",
self.wasm_func_name, self.wasm_func_name
));
}
X86WASMDirection::Both => {
out.push_str(&format!(
"(import \"{}\" \"{}\" (func ${}_import",
self.module_name, self.wasm_func_name, self.wasm_func_name
));
if !param_wat.is_empty() {
out.push(' ');
out.push_str(¶m_wat.join(" "));
}
if !result_wat.is_empty() {
out.push(' ');
out.push_str(&result_wat.join(" "));
}
out.push_str("))\n");
out.push_str(&format!(
"(export \"{}\" (func ${}_import))\n",
self.wasm_func_name, self.wasm_func_name
));
}
}
out
}
pub fn to_emscripten_binding(&self) -> String {
let mut out = String::new();
if let Some(config) = &self.emscripten_config {
if config.keepalive {
out.push_str("#include <emscripten.h>\n\n");
out.push_str(&format!(
"EMSCRIPTEN_KEEPALIVE\n{} {}({}) {{\n",
self.wasm_signature
.results
.first()
.map(|t| wasm_type_to_c(t))
.unwrap_or("void"),
self.c_func_name,
self.wasm_signature
.params
.iter()
.enumerate()
.map(|(i, t)| format!("{} arg{}", wasm_type_to_c(t), i))
.collect::<Vec<_>>()
.join(", ")
));
out.push_str(" // Implementation\n");
out.push_str("}\n");
}
if let Some(em_js) = &config.em_js {
out.push_str(&format!("EM_JS({})\n", em_js));
}
if let Some(em_asm) = &config.em_asm {
out.push_str(&format!("EM_ASM({})\n", em_asm));
}
if let Some(ccall_name) = &config.ccall_name {
out.push_str(&format!(
"// Accessible via Module.ccall('{}', ...)\n",
ccall_name
));
}
}
out
}
pub fn to_wasi_stub(&self) -> String {
let mut out = String::new();
if let Some(wasi_api) = &self.wasi_api {
out.push_str(&format!("// WASI API stub for: {}\n", wasi_api));
let ret_c = self
.wasm_signature
.results
.first()
.map(|t| wasm_type_to_c(t))
.unwrap_or("void");
match wasi_api.as_str() {
"fd_write" => {
out.push_str(
"__wasi_errno_t __wasi_fd_write(\n __wasi_fd_t fd,\n const __wasi_ciovec_t* iovs,\n size_t iovs_len,\n __wasi_size_t* nwritten\n) {\n"
);
out.push_str(" // Delegate to host implementation\n");
out.push_str(" return __WASI_ERRNO_SUCCESS;\n");
out.push_str("}\n");
}
"fd_read" => {
out.push_str(
"__wasi_errno_t __wasi_fd_read(\n __wasi_fd_t fd,\n const __wasi_iovec_t* iovs,\n size_t iovs_len,\n __wasi_size_t* nread\n) {\n"
);
out.push_str(" return __WASI_ERRNO_SUCCESS;\n");
out.push_str("}\n");
}
"fd_close" => {
out.push_str("__wasi_errno_t __wasi_fd_close(__wasi_fd_t fd) {\n");
out.push_str(" return __WASI_ERRNO_SUCCESS;\n");
out.push_str("}\n");
}
"fd_seek" => {
out.push_str(
"__wasi_errno_t __wasi_fd_seek(__wasi_fd_t fd, __wasi_filedelta_t offset, __wasi_whence_t whence, __wasi_filesize_t* newoffset) {\n"
);
out.push_str(" return __WASI_ERRNO_SUCCESS;\n");
out.push_str("}\n");
}
"proc_exit" => {
out.push_str("void __wasi_proc_exit(__wasi_exitcode_t code) {\n");
out.push_str(" __builtin_wasm_trap();\n");
out.push_str("}\n");
}
"random_get" => {
out.push_str(
"__wasi_errno_t __wasi_random_get(uint8_t* buf, __wasi_size_t buf_len) {\n",
);
out.push_str(" return __WASI_ERRNO_NOSYS;\n");
out.push_str("}\n");
}
"clock_time_get" => {
out.push_str(
"__wasi_errno_t __wasi_clock_time_get(__wasi_clockid_t id, __wasi_timestamp_t precision, __wasi_timestamp_t* time) {\n"
);
out.push_str(" return __WASI_ERRNO_NOSYS;\n");
out.push_str("}\n");
}
"args_get" => {
out.push_str(
"__wasi_errno_t __wasi_args_get(uint8_t** argv, uint8_t* argv_buf) {\n",
);
out.push_str(" return __WASI_ERRNO_SUCCESS;\n");
out.push_str("}\n");
}
"args_sizes_get" => {
out.push_str(
"__wasi_errno_t __wasi_args_sizes_get(__wasi_size_t* argc, __wasi_size_t* argv_buf_size) {\n"
);
out.push_str(" *argc = 0;\n");
out.push_str(" *argv_buf_size = 0;\n");
out.push_str(" return __WASI_ERRNO_SUCCESS;\n");
out.push_str("}\n");
}
"environ_get" => {
out.push_str(
"__wasi_errno_t __wasi_environ_get(uint8_t** environ, uint8_t* environ_buf) {\n"
);
out.push_str(" return __WASI_ERRNO_SUCCESS;\n");
out.push_str("}\n");
}
"environ_sizes_get" => {
out.push_str(
"__wasi_errno_t __wasi_environ_sizes_get(__wasi_size_t* environ_count, __wasi_size_t* environ_buf_size) {\n"
);
out.push_str(" *environ_count = 0;\n");
out.push_str(" *environ_buf_size = 0;\n");
out.push_str(" return __WASI_ERRNO_SUCCESS;\n");
out.push_str("}\n");
}
_ => {
out.push_str(&format!(
"{} __wasi_{}({}) {{\n",
ret_c,
wasi_api,
self.wasm_signature
.params
.iter()
.enumerate()
.map(|(i, t)| format!("{} arg{}", wasm_type_to_c(t), i))
.collect::<Vec<_>>()
.join(", ")
));
out.push_str(" return 0;\n");
out.push_str("}\n");
}
}
}
out
}
pub fn build_rule(&self) -> String {
if self.emscripten_config.is_some() {
format!(
"emcc {}.c -o {}.js -s EXPORTED_FUNCTIONS='[\"_{}\"]'",
self.module_name, self.module_name, self.c_func_name
)
} else {
format!(
"clang --target=wasm32-unknown-wasi -nostdlib -o {}.wasm {}.c",
self.module_name, self.module_name
)
}
}
}
pub fn wasm_type_to_c(wt: &X86WASMType) -> &'static str {
match wt {
X86WASMType::I32 => "int32_t",
X86WASMType::I64 => "int64_t",
X86WASMType::F32 => "float",
X86WASMType::F64 => "double",
X86WASMType::V128 => "v128_t",
X86WASMType::FuncRef => "void*",
X86WASMType::ExternRef => "void*",
}
}
pub fn ctype_to_wasm_type(ct: &X86CType) -> X86WASMType {
match ct {
X86CType::Char
| X86CType::SignedChar
| X86CType::UnsignedChar
| X86CType::Short
| X86CType::UnsignedShort
| X86CType::Int
| X86CType::UnsignedInt
| X86CType::Bool => X86WASMType::I32,
X86CType::Long
| X86CType::UnsignedLong
| X86CType::LongLong
| X86CType::UnsignedLongLong => X86WASMType::I64,
X86CType::Float => X86WASMType::F32,
X86CType::Double => X86WASMType::F64,
X86CType::Pointer(_) | X86CType::ConstPointer(_) | X86CType::Opaque(_) => X86WASMType::I32,
_ => X86WASMType::I32,
}
}
pub fn parse_ctype_string(s: &str) -> Option<X86CType> {
match s.trim() {
"void" => Some(X86CType::Void),
"char" => Some(X86CType::Char),
"signed char" => Some(X86CType::SignedChar),
"unsigned char" => Some(X86CType::UnsignedChar),
"short" => Some(X86CType::Short),
"unsigned short" => Some(X86CType::UnsignedShort),
"int" => Some(X86CType::Int),
"unsigned int" => Some(X86CType::UnsignedInt),
"long" => Some(X86CType::Long),
"unsigned long" => Some(X86CType::UnsignedLong),
"long long" => Some(X86CType::LongLong),
"unsigned long long" => Some(X86CType::UnsignedLongLong),
"float" => Some(X86CType::Float),
"double" => Some(X86CType::Double),
"long double" => Some(X86CType::LongDouble),
"_Bool" | "bool" => Some(X86CType::Bool),
s if s.ends_with('*') && s.len() > 1 => {
let inner = parse_ctype_string(&s[..s.len() - 1].trim())?;
Some(X86CType::Pointer(Box::new(inner)))
}
s if s.starts_with("const ") && s.ends_with('*') && s.len() > 7 => {
let inner = parse_ctype_string(&s[6..s.len() - 1].trim())?;
Some(X86CType::ConstPointer(Box::new(inner)))
}
_ => Some(X86CType::Opaque(s.to_string())),
}
}
pub fn parse_c_function_decl(decl: &str) -> Option<(String, X86CType, Vec<X86CParam>)> {
let decl = decl.trim();
let paren_open = decl.find('(')?;
let paren_close = decl.rfind(')')?;
let before_paren = &decl[..paren_open].trim();
let params_str = &decl[paren_open + 1..paren_close].trim();
let parts: Vec<&str> = before_paren.split_whitespace().collect();
if parts.len() < 2 {
return None;
}
let name = parts.last()?.to_string();
let ret_type_str = parts[..parts.len() - 1].join(" ");
let return_type = parse_ctype_string(&ret_type_str)?;
let mut params = Vec::new();
if !params_str.is_empty() && *params_str != "void" {
for param in params_str.split(',') {
let param = param.trim();
if param == "..." {
continue;
}
let parts: Vec<&str> = param.rsplitn(2, ' ').collect();
if parts.len() == 2 {
let pname = parts[0].to_string();
let ptype = parse_ctype_string(parts[1])?;
params.push(X86CParam::new(&pname, ptype));
}
}
}
Some((name, return_type, params))
}
pub fn is_c_compatible_type(ct: &X86CType) -> bool {
match ct {
X86CType::Void
| X86CType::Char
| X86CType::SignedChar
| X86CType::UnsignedChar
| X86CType::Short
| X86CType::UnsignedShort
| X86CType::Int
| X86CType::UnsignedInt
| X86CType::Long
| X86CType::UnsignedLong
| X86CType::LongLong
| X86CType::UnsignedLongLong
| X86CType::Float
| X86CType::Double
| X86CType::LongDouble
| X86CType::Bool
| X86CType::Pointer(_)
| X86CType::ConstPointer(_)
| X86CType::Enum(_, _)
| X86CType::Opaque(_) => true,
X86CType::Struct(_, _) | X86CType::Union(_, _) => true, X86CType::Array(_, _) => true,
X86CType::FunctionPointer(_, _) => true,
X86CType::Typedef(_, inner) => is_c_compatible_type(inner),
}
}
pub fn make_c_wrapper_name(cxx_name: &str) -> String {
let mut name = cxx_name
.replace("::", "_")
.replace('<', "_")
.replace('>', "_")
.replace(' ', "_")
.replace(',', "_")
.replace('*', "Ptr")
.replace('&', "Ref");
while name.contains("__") {
name = name.replace("__", "_");
}
name
}
pub fn make_rust_safe_name(c_name: &str) -> String {
c_name.to_lowercase().replace('_', "_")
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RegClass {
Integer,
SSE,
SSEUp,
X87,
X87Up,
ComplexX87,
Memory,
NoClass,
}
pub fn classify_sysv_amd64(ct: &X86CType, offset: u32) -> Vec<X86RegClass> {
let sz = ct.size_x86_64(X86InteropABI::SystemV);
match ct {
X86CType::Bool
| X86CType::Char
| X86CType::SignedChar
| X86CType::UnsignedChar
| X86CType::Short
| X86CType::UnsignedShort
| X86CType::Int
| X86CType::UnsignedInt
| X86CType::Long
| X86CType::UnsignedLong
| X86CType::LongLong
| X86CType::UnsignedLongLong
| X86CType::Enum(_, _)
| X86CType::Pointer(_)
| X86CType::ConstPointer(_)
| X86CType::Opaque(_) => {
vec![X86RegClass::Integer]
}
X86CType::Float => {
vec![X86RegClass::SSE]
}
X86CType::Double => {
vec![X86RegClass::SSE]
}
X86CType::LongDouble => {
vec![X86RegClass::X87, X86RegClass::X87Up]
}
X86CType::Struct(_, fields) | X86CType::Union(_, fields) => {
if sz > 16 {
return vec![X86RegClass::Memory];
}
let mut classes = Vec::new();
let mut field_offset = 0u32;
for (_, ftype) in fields {
let fsz = ftype.size_x86_64(X86InteropABI::SystemV);
let fclasses = classify_sysv_amd64(ftype, field_offset);
for (i, cls) in fclasses.iter().enumerate() {
let eightbyte = (field_offset as usize + i * 8) / 8;
while classes.len() <= eightbyte {
classes.push(X86RegClass::NoClass);
}
classes[eightbyte] = merge_reg_class(classes[eightbyte], *cls);
}
field_offset += fsz;
}
for cls in &mut classes {
if *cls == X86RegClass::NoClass {
*cls = X86RegClass::Integer;
}
}
if classes.iter().any(|c| *c == X86RegClass::Memory) {
return vec![X86RegClass::Memory];
}
classes
}
_ => vec![X86RegClass::Integer],
}
}
pub fn merge_reg_class(a: X86RegClass, b: X86RegClass) -> X86RegClass {
use X86RegClass::*;
match (a, b) {
(NoClass, other) | (other, NoClass) => other,
(Memory, _) | (_, Memory) => Memory,
(Integer, Integer) => Integer,
(SSE, SSE) => SSE,
(SSEUp, SSEUp) => SSEUp,
(X87, X87) => X87,
(X87Up, X87Up) => X87Up,
(ComplexX87, ComplexX87) => ComplexX87,
(Integer, _) | (_, Integer) => Integer,
(SSE, X87)
| (X87, SSE)
| (SSE, X87Up)
| (X87Up, SSE)
| (SSEUp, X87)
| (X87, SSEUp)
| (SSEUp, X87Up)
| (X87Up, SSEUp) => X87,
_ => Memory,
}
}
pub fn count_int_regs(classes: &[X86RegClass]) -> usize {
classes
.iter()
.filter(|c| **c == X86RegClass::Integer)
.count()
}
pub fn count_sse_regs(classes: &[X86RegClass]) -> usize {
classes.iter().filter(|c| **c == X86RegClass::SSE).count()
}
pub fn classify_msx64(ct: &X86CType) -> X86RegClass {
let sz = ct.size_x86_64(X86InteropABI::MicrosoftX64);
match ct {
X86CType::Bool
| X86CType::Char
| X86CType::SignedChar
| X86CType::UnsignedChar
| X86CType::Short
| X86CType::UnsignedShort
| X86CType::Int
| X86CType::UnsignedInt
| X86CType::Long
| X86CType::UnsignedLong
| X86CType::LongLong
| X86CType::UnsignedLongLong
| X86CType::Enum(_, _)
| X86CType::Pointer(_)
| X86CType::ConstPointer(_)
| X86CType::Opaque(_) => X86RegClass::Integer,
X86CType::Float | X86CType::Double => X86RegClass::SSE,
X86CType::Struct(_, _) | X86CType::Union(_, _) => {
if sz == 1 || sz == 2 || sz == 4 || sz == 8 {
X86RegClass::Integer
} else {
X86RegClass::Memory
}
}
_ => X86RegClass::Memory,
}
}
#[derive(Debug, Clone)]
pub struct X86CXXClassWrapper {
pub cxx_class: String,
pub c_opaque_name: String,
pub methods: Vec<X86CXXInterop>,
pub constructor: Option<X86CXXInterop>,
pub destructor: Option<X86CXXInterop>,
}
impl X86CXXClassWrapper {
pub fn new(cxx_class: &str) -> Self {
let c_opaque = format!("{}Handle", cxx_class.replace("::", "_"));
Self {
cxx_class: cxx_class.to_string(),
c_opaque_name: c_opaque,
methods: Vec::new(),
constructor: None,
destructor: None,
}
}
pub fn add_method(
&mut self,
method_name: &str,
return_type: X86CType,
is_const: bool,
) -> &mut X86CXXInterop {
let c_name = format!(
"{}_{}",
self.c_opaque_name,
make_c_wrapper_name(method_name)
);
let mut interop =
X86CXXInterop::new_method(&self.cxx_class, method_name, &c_name, return_type, is_const);
self.methods.push(interop);
self.methods.last_mut().unwrap()
}
pub fn set_constructor(&mut self, params: Vec<X86CParam>) -> &mut Self {
let c_name = format!("{}_create", self.c_opaque_name);
let mut interop = X86CXXInterop::new(
&format!("{}::{}", self.cxx_class, self.cxx_class),
&c_name,
X86CType::Pointer(Box::new(X86CType::Opaque(self.c_opaque_name.clone()))),
);
interop.is_method = false;
interop.params = params;
self.constructor = Some(interop);
self
}
pub fn set_destructor(&mut self) -> &mut Self {
let c_name = format!("{}_destroy", self.c_opaque_name);
let mut interop = X86CXXInterop::new_method(
&self.cxx_class,
&format!("~{}", self.cxx_class.split("::").last().unwrap_or("")),
&c_name,
X86CType::Void,
false,
);
self.destructor = Some(interop);
self
}
pub fn to_c_header(&self) -> String {
let mut out = String::new();
out.push_str(&format!(
"/* Opaque C wrapper for C++ class: {} */\n",
self.cxx_class
));
out.push_str(&format!(
"typedef struct {}Opaque {}Handle;\n\n",
self.c_opaque_name, self.c_opaque_name
));
if let Some(ctor) = &self.constructor {
out.push_str(&ctor.to_c_declaration());
out.push('\n');
}
for method in &self.methods {
out.push_str(&method.to_c_declaration());
out.push('\n');
}
if let Some(dtor) = &self.destructor {
out.push_str(&dtor.to_c_declaration());
out.push('\n');
}
out
}
pub fn to_cpp_wrapper_impl(&self) -> String {
let mut out = String::new();
out.push_str(&format!(
"// C wrapper implementation for class: {}\n",
self.cxx_class
));
out.push_str(&format!("#include \"{}_wrapper.h\"\n", self.c_opaque_name));
out.push_str(&format!(
"#include \"{}.h\"\n\n",
self.cxx_class.replace("::", "/")
));
if let Some(ctor) = &self.constructor {
out.push_str(&ctor.to_c_wrapper_impl());
out.push('\n');
}
for method in &self.methods {
out.push_str(&method.to_c_wrapper_impl());
out.push('\n');
}
if let Some(dtor) = &self.destructor {
out.push_str(&dtor.to_c_wrapper_impl());
out.push('\n');
}
out
}
}
#[derive(Debug, Clone)]
pub struct X86CallbackType {
pub typedef_name: String,
pub return_type: X86CType,
pub params: Vec<X86CType>,
pub call_conv: X86CallConv,
}
impl X86CallbackType {
pub fn new(typedef_name: &str, return_type: X86CType, call_conv: X86CallConv) -> Self {
Self {
typedef_name: typedef_name.to_string(),
return_type,
params: Vec::new(),
call_conv,
}
}
pub fn add_param(&mut self, ty: X86CType) -> &mut Self {
self.params.push(ty);
self
}
pub fn to_c_typedef(&self) -> String {
let params_str: Vec<String> = self.params.iter().map(|t| t.to_c_decl()).collect();
if let Some(cc) = self.call_conv.as_attribute() {
format!(
"typedef {} (__attribute__(({})) *{})({});",
self.return_type.to_c_decl(),
cc,
self.typedef_name,
params_str.join(", ")
)
} else {
format!(
"typedef {} (*{})({});",
self.return_type.to_c_decl(),
self.typedef_name,
params_str.join(", ")
)
}
}
pub fn to_rust_type_alias(&self) -> String {
let params_rust: Vec<String> = self
.params
.iter()
.map(|t| map_c_type_to_rust(t).to_rust_decl())
.collect();
let ret_rust = map_c_type_to_rust(&self.return_type).to_rust_decl();
format!(
"pub type {} = extern \"C\" fn({}) -> {};",
self.typedef_name,
params_rust.join(", "),
ret_rust
)
}
}
#[derive(Debug, Clone)]
pub struct X86GoCallback {
pub go_func_name: String,
pub callback_type: X86CallbackType,
pub go_body: String,
}
impl X86GoCallback {
pub fn new(go_func_name: &str, callback_type: X86CallbackType) -> Self {
Self {
go_func_name: go_func_name.to_string(),
callback_type,
go_body: String::new(),
}
}
pub fn to_go_code(&self) -> String {
let mut out = String::new();
out.push_str(&format!("//export {}\n", self.go_func_name));
let params_go: Vec<String> = self
.callback_type
.params
.iter()
.enumerate()
.map(|(i, t)| format!("arg{} C.{}", i, ctype_to_cgo_type(t)))
.collect();
let ret_cgo = if self.callback_type.return_type == X86CType::Void {
String::new()
} else {
format!(" C.{}", ctype_to_cgo_type(&self.callback_type.return_type))
};
out.push_str(&format!(
"func {}({}){} {{\n",
self.go_func_name,
params_go.join(", "),
ret_cgo
));
out.push_str(&self.go_body);
out.push_str("\n}\n");
out
}
}
#[derive(Debug, Clone)]
pub struct X86RustCallback {
pub rust_func_name: String,
pub callback_type: X86CallbackType,
pub use_trampoline: bool,
pub user_data_type: Option<X86RustFfiType>,
}
impl X86RustCallback {
pub fn new(rust_func_name: &str, callback_type: X86CallbackType) -> Self {
Self {
rust_func_name: rust_func_name.to_string(),
callback_type,
use_trampoline: false,
user_data_type: None,
}
}
pub fn to_rust_code(&self) -> String {
let mut out = String::new();
let params_rust: Vec<String> = self
.callback_type
.params
.iter()
.map(|t| {
let rt = map_c_type_to_rust(t);
rt.to_rust_decl()
})
.collect();
let ret_rust = map_c_type_to_rust(&self.callback_type.return_type).to_rust_decl();
out.push_str("#[no_mangle]\n");
out.push_str(&format!(
"pub extern \"C\" fn {}({}) -> {} {{\n",
self.rust_func_name,
params_rust.join(", "),
ret_rust
));
out.push_str(" // Rust callback implementation\n");
if self.callback_type.return_type != X86CType::Void {
out.push_str(" unimplemented!()\n");
}
out.push_str("}\n");
out
}
pub fn to_trampoline_code(&self) -> String {
let mut out = String::new();
let params_rust: Vec<String> = self
.callback_type
.params
.iter()
.map(|t| map_c_type_to_rust(t).to_rust_decl())
.collect();
let ret_rust = map_c_type_to_rust(&self.callback_type.return_type).to_rust_decl();
out.push_str(&format!(
"pub unsafe extern \"C\" fn {}_trampoline(\n",
self.rust_func_name
));
for (i, p) in params_rust.iter().enumerate() {
out.push_str(&format!(" arg{}: {},\n", i, p));
}
out.push_str(" user_data: *mut std::os::raw::c_void,\n");
out.push_str(&format!(") -> {} {{\n", ret_rust));
out.push_str(" let callback: fn(");
for (i, _) in params_rust.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
out.push_str(&format!("{}", params_rust[i]));
}
out.push_str(&format!(
") -> {} = std::mem::transmute(user_data);\n",
ret_rust
));
out.push_str(&format!(
" callback({})\n",
(0..params_rust.len())
.map(|i| format!("arg{}", i))
.collect::<Vec<_>>()
.join(", ")
));
out.push_str("}\n");
out
}
}
pub fn gen_rust_to_c_string(rust_var: &str, c_var: &str) -> String {
format!(
"let {} = std::ffi::CString::new({}).unwrap();\n\
let {}_ptr = {}.as_ptr();",
c_var, rust_var, c_var, c_var
)
}
pub fn gen_c_string_to_rust(c_var: &str, rust_var: &str) -> String {
format!(
"let {} = unsafe {{ std::ffi::CStr::from_ptr({}) }};\n\
let {} = {}.to_str().unwrap().to_string();",
rust_var, c_var, rust_var, rust_var
)
}
pub fn gen_go_string_to_c(go_var: &str, c_var: &str) -> String {
format!(
"{} := C.CString({})\n\
defer C.free(unsafe.Pointer({}))",
c_var, go_var, c_var
)
}
pub fn gen_c_string_to_go(c_var: &str, go_var: &str) -> String {
format!("{} := C.GoString({})", go_var, c_var)
}
pub fn gen_py_string_to_c(py_var: &str, c_var: &str) -> String {
format!(
"const char* {} = PyUnicode_AsUTF8({});\n\
if ({} == NULL) return NULL;",
c_var, py_var, c_var
)
}
pub fn gen_c_string_to_py(c_var: &str, py_var: &str) -> String {
format!("PyObject* {} = PyUnicode_FromString({});", py_var, c_var)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86SIMDType {
M128,
M128d,
M128i,
M256,
M256d,
M256i,
M512,
M512d,
M512i,
Mmask8,
Mmask16,
Mmask32,
Mmask64,
}
impl X86SIMDType {
pub fn size(&self) -> u32 {
match self {
Self::M128 | Self::M128d | Self::M128i => 16,
Self::M256 | Self::M256d | Self::M256i => 32,
Self::M512 | Self::M512d | Self::M512i => 64,
Self::Mmask8 => 1,
Self::Mmask16 => 2,
Self::Mmask32 => 4,
Self::Mmask64 => 8,
}
}
pub fn alignment(&self) -> u32 {
self.size()
}
pub fn to_intel_intrinsic(&self) -> &'static str {
match self {
Self::M128 => "__m128",
Self::M128d => "__m128d",
Self::M128i => "__m128i",
Self::M256 => "__m256",
Self::M256d => "__m256d",
Self::M256i => "__m256i",
Self::M512 => "__m512",
Self::M512d => "__m512d",
Self::M512i => "__m512i",
Self::Mmask8 => "__mmask8",
Self::Mmask16 => "__mmask16",
Self::Mmask32 => "__mmask32",
Self::Mmask64 => "__mmask64",
}
}
pub fn to_c_decl(&self) -> &'static str {
self.to_intel_intrinsic()
}
pub fn to_rust_type(&self) -> &'static str {
match self {
Self::M128 => "std::arch::x86_64::__m128",
Self::M128d => "std::arch::x86_64::__m128d",
Self::M128i => "std::arch::x86_64::__m128i",
Self::M256 => "std::arch::x86_64::__m256",
Self::M256d => "std::arch::x86_64::__m256d",
Self::M256i => "std::arch::x86_64::__m256i",
Self::M512 => "std::arch::x86_64::__m512",
Self::M512d => "std::arch::x86_64::__m512d",
Self::M512i => "std::arch::x86_64::__m512i",
Self::Mmask8 => "std::arch::x86_64::__mmask8",
Self::Mmask16 => "std::arch::x86_64::__mmask16",
Self::Mmask32 => "std::arch::x86_64::__mmask32",
Self::Mmask64 => "std::arch::x86_64::__mmask64",
}
}
pub fn to_numpy_dtype(&self) -> &'static str {
match self {
Self::M128 => "np.float32",
Self::M128d => "np.float64",
Self::M256 => "np.float32",
Self::M256d => "np.float64",
Self::M512 => "np.float32",
Self::M512d => "np.float64",
_ => "np.uint8",
}
}
pub fn requires_vectorcall(&self) -> bool {
matches!(
self,
Self::M256 | Self::M256d | Self::M256i | Self::M512 | Self::M512d | Self::M512i
)
}
}
#[derive(Debug, Clone, Default)]
pub struct X86SIMDCallConvMapping {
pub overrides: HashMap<X86SIMDType, X86CallConv>,
}
impl X86SIMDCallConvMapping {
pub fn for_simd_type(&self, st: &X86SIMDType) -> X86CallConv {
if let Some(cc) = self.overrides.get(st) {
*cc
} else if st.requires_vectorcall() {
X86CallConv::Vectorcall
} else {
X86CallConv::SysVAMD64
}
}
}
#[derive(Debug, Clone)]
pub struct X86SIMDBoundaryBridge {
pub name: String,
pub return_simd_type: X86SIMDType,
pub c_return_type: X86CType,
pub simd_params: Vec<X86SIMDType>,
}
impl X86SIMDBoundaryBridge {
pub fn new(
name: &str,
return_simd: X86SIMDType,
c_return: X86CType,
simd_params: Vec<X86SIMDType>,
) -> Self {
Self {
name: name.to_string(),
return_simd_type: return_simd,
c_return_type: c_return,
simd_params,
}
}
pub fn to_c_declaration(&self) -> String {
let params: Vec<String> = self
.simd_params
.iter()
.map(|t| t.to_c_decl().to_string())
.collect();
format!(
"{} {}({});",
self.return_simd_type.to_c_decl(),
self.name,
params.join(", ")
)
}
}
#[derive(Debug, Clone)]
pub struct X86ZeroCopyLayout {
pub name: String,
pub fields: Vec<X86StructField>,
pub total_size: u32,
pub alignment: u32,
pub is_pod: bool,
}
impl X86ZeroCopyLayout {
pub fn compute(name: &str, fields: &[(String, X86CType)]) -> Self {
let rust_layout = X86RustStructLayout::compute_x86_64(name, fields);
Self {
name: name.to_string(),
fields: rust_layout.fields,
total_size: rust_layout.total_size,
alignment: rust_layout.alignment,
is_pod: true,
}
}
pub fn to_rust_zero_copy_def(&self) -> String {
let mut out = String::new();
out.push_str("#[repr(C)]\n");
out.push_str("#[derive(Debug, Clone, Copy)]\n");
out.push_str(&format!("pub struct {} {{\n", self.name));
for field in &self.fields {
out.push_str(&format!(
" pub {}: {},\n",
field.name,
field.ty.to_rust_decl()
));
}
out.push_str("}\n\n");
out.push_str(&format!(
"// Safety: {} is #[repr(C)] and all fields are plain data.\n",
self.name
));
out.push_str(&format!(
"unsafe impl bytemuck::Zeroable for {} {{}}\n",
self.name
));
out.push_str(&format!(
"unsafe impl bytemuck::Pod for {} {{}}\n",
self.name
));
out
}
pub fn validate<T: Sized>(&self) -> bool {
self.total_size as usize == std::mem::size_of::<T>()
}
}
#[derive(Debug, Clone)]
pub struct X86AsyncFfiBridge {
pub c_name: String,
pub return_type: X86CType,
pub params: Vec<X86CParam>,
pub timeout_ms: Option<u64>,
}
impl X86AsyncFfiBridge {
pub fn new(c_name: &str, return_type: X86CType, params: Vec<X86CParam>) -> Self {
Self {
c_name: c_name.to_string(),
return_type,
params,
timeout_ms: None,
}
}
pub fn to_rust_async_wrapper(&self) -> String {
let mut out = String::new();
let fn_params: Vec<String> = self
.params
.iter()
.map(|p| format!("{}: {}", p.name, rust_type_to_c(&map_c_type_to_rust(&p.ty))))
.collect();
let ret_rust = map_c_type_to_rust(&self.return_type).to_rust_decl();
out.push_str(&format!(
"pub async fn {}_async({}) -> {} {{\n",
self.c_name,
fn_params.join(", "),
ret_rust
));
out.push_str(" tokio::task::spawn_blocking(move || {\n");
out.push_str(" unsafe {\n");
let call_args: Vec<String> = self.params.iter().map(|p| p.name.clone()).collect();
out.push_str(&format!(
" {}({})\n",
self.c_name,
call_args.join(", ")
));
out.push_str(" }\n");
out.push_str(" }).await.unwrap()\n");
out.push_str("}\n");
out
}
}
#[derive(Debug, Clone)]
pub struct X86AsyncCallbackPattern {
pub name: String,
pub callback_type: X86CType,
pub params: Vec<X86CParam>,
}
impl X86AsyncCallbackPattern {
pub fn new(name: &str, callback_type: X86CType, params: Vec<X86CParam>) -> Self {
Self {
name: name.to_string(),
callback_type,
params,
}
}
pub fn to_c_pattern(&self) -> String {
let mut out = String::new();
let params_c: Vec<String> = self
.params
.iter()
.map(|p| format!("{} {}", p.ty.to_c_decl(), p.name))
.collect();
out.push_str(&format!(
"typedef {} (*{}_callback_t)({}, void* user_data);\n",
self.callback_type.to_c_decl(),
self.name,
params_c.join(", ")
));
out.push_str(&format!(
"void register_{}_callback({}_callback_t cb, void* user_data);\n",
self.name, self.name
));
out
}
pub fn to_rust_pattern(&self) -> String {
let mut out = String::new();
let params_rust: Vec<String> = self
.params
.iter()
.map(|p| map_c_type_to_rust(&p.ty).to_rust_decl())
.collect();
let ret_rust = map_c_type_to_rust(&self.callback_type).to_rust_decl();
out.push_str(&format!(
"pub type {}Callback = unsafe extern \"C\" fn({}, *mut std::os::raw::c_void) -> {};\n",
self.name,
params_rust.join(", "),
ret_rust
));
out
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86NumPyDtype {
Int8,
Int16,
Int32,
Int64,
Uint8,
Uint16,
Uint32,
Uint64,
Float32,
Float64,
Complex64,
Complex128,
}
impl X86NumPyDtype {
pub fn to_npy_type(&self) -> &'static str {
match self {
Self::Int8 => "NPY_INT8",
Self::Int16 => "NPY_INT16",
Self::Int32 => "NPY_INT32",
Self::Int64 => "NPY_INT64",
Self::Uint8 => "NPY_UINT8",
Self::Uint16 => "NPY_UINT16",
Self::Uint32 => "NPY_UINT32",
Self::Uint64 => "NPY_UINT64",
Self::Float32 => "NPY_FLOAT32",
Self::Float64 => "NPY_FLOAT64",
Self::Complex64 => "NPY_COMPLEX64",
Self::Complex128 => "NPY_COMPLEX128",
}
}
}
#[derive(Debug, Clone)]
pub struct X86NumPyArraySpec {
pub name: String,
pub dtype: X86NumPyDtype,
pub ndim: usize,
pub shape: Vec<usize>,
}
impl X86NumPyArraySpec {
pub fn new(name: &str, dtype: X86NumPyDtype, shape: Vec<usize>) -> Self {
let ndim = shape.len();
Self {
name: name.to_string(),
dtype,
ndim,
shape,
}
}
pub fn new_i32(name: &str, shape: Vec<usize>) -> Self {
Self::new(name, X86NumPyDtype::Int32, shape)
}
pub fn new_f64(name: &str, shape: Vec<usize>) -> Self {
Self::new(name, X86NumPyDtype::Float64, shape)
}
pub fn to_c_creation_code(&self) -> String {
let mut out = String::new();
out.push_str(&format!("npy_intp {}_dims[{}] = {{", self.name, self.ndim));
let dims: Vec<String> = self.shape.iter().map(|d| d.to_string()).collect();
out.push_str(&dims.join(", "));
out.push_str("};\n");
out.push_str(&format!(
"PyObject* {}_array = PyArray_SimpleNew({}, {}_dims, {});\n",
self.name,
self.ndim,
self.name,
self.dtype.to_npy_type()
));
out
}
pub fn to_contiguous_check(&self, var_name: &str) -> String {
format!(
"if (!PyArray_ISCONTIGUOUS({})) {{\n PyErr_SetString(PyExc_ValueError, \"Array must be contiguous\");\n return NULL;\n}}",
var_name
)
}
}
#[derive(Debug, Clone)]
pub struct X86CythonModule {
pub name: String,
pub functions: Vec<X86CythonFunction>,
pub classes: Vec<X86CythonClass>,
}
#[derive(Debug, Clone)]
pub struct X86CythonFunction {
pub py_name: String,
pub c_name: String,
pub return_type: X86CType,
pub params: Vec<X86CParam>,
}
#[derive(Debug, Clone)]
pub struct X86CythonClass {
pub name: String,
pub fields: Vec<(String, X86CType)>,
pub methods: Vec<X86CythonFunction>,
}
impl X86CythonModule {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
functions: Vec::new(),
classes: Vec::new(),
}
}
pub fn add_function(
&mut self,
py_name: &str,
c_name: &str,
return_type: X86CType,
params: Vec<X86CParam>,
) {
self.functions.push(X86CythonFunction {
py_name: py_name.to_string(),
c_name: c_name.to_string(),
return_type,
params,
});
}
pub fn add_cdef_class(&mut self, name: &str, fields: Vec<(String, X86CType)>) {
self.classes.push(X86CythonClass {
name: name.to_string(),
fields,
methods: Vec::new(),
});
}
pub fn to_pyx_file(&self) -> String {
let mut out = String::new();
out.push_str("# cython: language_level=3\n\n");
out.push_str("cdef extern from \"x86_interop_ffi.h\":\n");
for func in &self.functions {
let ret = ctype_to_cython_type(&func.return_type);
let params: Vec<String> = func
.params
.iter()
.map(|p| format!("{} {}", ctype_to_cython_type(&p.ty), p.name))
.collect();
out.push_str(&format!(
" {} {}({})\n",
ret,
func.c_name,
params.join(", ")
));
}
out.push('\n');
for func in &self.functions {
let params: Vec<String> = func.params.iter().map(|p| p.name.clone()).collect();
out.push_str(&format!("def {}({}):\n", func.py_name, params.join(", ")));
let args: Vec<String> = func.params.iter().map(|p| p.name.clone()).collect();
out.push_str(&format!(
" return {}({})\n\n",
func.c_name,
args.join(", ")
));
}
for cls in &self.classes {
out.push_str(&format!("cdef class {}:\n", cls.name));
for (fname, ftype) in &cls.fields {
out.push_str(&format!(
" cdef public {} {}\n",
ctype_to_cython_type(ftype),
fname
));
}
out.push('\n');
}
out
}
}
#[derive(Debug, Clone)]
pub struct X86PyObjectLifetime {
pub name: String,
}
impl X86PyObjectLifetime {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
}
}
pub fn to_incref(&self) -> String {
format!("Py_INCREF({});", self.name)
}
pub fn to_decref(&self) -> String {
format!("Py_DECREF({});", self.name)
}
}
#[derive(Debug, Clone)]
pub struct X86PyExceptionTranslator {
pub mappings: Vec<X86PyExceptionMapping>,
}
#[derive(Debug, Clone)]
pub struct X86PyExceptionMapping {
pub cpp_exception: String,
pub python_exception: String,
pub message: String,
}
impl X86PyExceptionTranslator {
pub fn new() -> Self {
Self {
mappings: Vec::new(),
}
}
pub fn add_mapping(
&mut self,
cpp_exc: &str,
py_exc: &str,
msg: &str,
) -> &X86PyExceptionMapping {
self.mappings.push(X86PyExceptionMapping {
cpp_exception: cpp_exc.to_string(),
python_exception: py_exc.to_string(),
message: msg.to_string(),
});
self.mappings.last().unwrap()
}
}
impl X86PyExceptionMapping {
pub fn to_catch_block(&self) -> String {
format!(
"catch (const {}& e) {{\n PyErr_SetString(PyExc_{}, \"{}\");\n return NULL;\n}}",
self.cpp_exception, self.python_exception, self.message
)
}
}
#[derive(Debug, Clone)]
pub struct X86JNIStringConversion {
pub java_name: String,
pub c_name: String,
}
impl X86JNIStringConversion {
pub fn new(java_name: &str, c_name: &str) -> Self {
Self {
java_name: java_name.to_string(),
c_name: c_name.to_string(),
}
}
pub fn java_to_c(&self) -> String {
format!(
"const char* {} = (*env)->GetStringUTFChars(env, {}, NULL);\n\
// ... use {} ...\n\
(*env)->ReleaseStringUTFChars(env, {}, {});",
self.c_name, self.java_name, self.c_name, self.java_name, self.c_name
)
}
pub fn c_to_java(&self) -> String {
format!(
"jstring {} = (*env)->NewStringUTF(env, {});",
self.java_name, self.c_name
)
}
}
#[derive(Debug, Clone)]
pub struct X86JNIArrayOps {
pub array_type: String,
pub element_type: String,
pub length: usize,
}
impl X86JNIArrayOps {
pub fn new_int_array(name: &str, length: usize) -> Self {
Self {
array_type: format!("{}Array", name),
element_type: "jint".into(),
length,
}
}
pub fn get_elements(&self, env: &str, arr: &str) -> String {
format!(
"{}* elems = (*{})->Get{}Elements({}, {}, NULL);",
self.element_type, env, self.array_type, env, arr
)
}
pub fn release_elements(&self, env: &str, arr: &str, elems: &str, mode: &str) -> String {
format!(
"(*{})->Release{}Elements({}, {}, {}, {});",
env, self.array_type, env, arr, elems, mode
)
}
}
#[derive(Debug, Clone)]
pub struct X86JNIGlobalRef {
pub obj_name: String,
pub class_name: String,
}
impl X86JNIGlobalRef {
pub fn new(obj_name: &str, class_name: &str) -> Self {
Self {
obj_name: obj_name.to_string(),
class_name: class_name.to_string(),
}
}
pub fn create(&self) -> String {
format!(
"jobject {}_global = (*env)->NewGlobalRef(env, {});",
self.obj_name, self.obj_name
)
}
pub fn delete(&self) -> String {
format!("(*env)->DeleteGlobalRef(env, {}_global);", self.obj_name)
}
}
#[derive(Debug, Clone)]
pub struct X86JNIExceptionCheck;
impl X86JNIExceptionCheck {
pub fn new() -> Self {
Self
}
pub fn to_check_block(&self) -> String {
let mut out = String::new();
out.push_str("if ((*env)->ExceptionCheck(env)) {\n");
out.push_str(" (*env)->ExceptionDescribe(env);\n");
out.push_str(" (*env)->ExceptionClear(env);\n");
out.push_str(" return NULL;\n");
out.push_str("}\n");
out
}
}
#[derive(Debug, Clone)]
pub struct X86DotNetSafeHandle {
pub class_name: String,
pub handle_type: String,
}
impl X86DotNetSafeHandle {
pub fn new(class_name: &str, handle_type: &str) -> Self {
Self {
class_name: class_name.to_string(),
handle_type: handle_type.to_string(),
}
}
pub fn to_cs_class(&self) -> String {
format!(
"public sealed class {} : SafeHandle {{\n\
private {}() : base(IntPtr.Zero, true) {{}}\n\
public override bool IsInvalid => handle == IntPtr.Zero;\n\
protected override bool ReleaseHandle() {{\n\
// Release native resource\n\
return true;\n\
}}\n\
}}",
self.class_name, self.class_name
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86DotNetLayoutKind {
Sequential,
Explicit,
Auto,
}
impl fmt::Display for X86DotNetLayoutKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Sequential => write!(f, "LayoutKind.Sequential"),
Self::Explicit => write!(f, "LayoutKind.Explicit"),
Self::Auto => write!(f, "LayoutKind.Auto"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86DotNetStructLayout {
pub name: String,
pub kind: X86DotNetLayoutKind,
pub pack: Option<u32>,
pub size: Option<u32>,
}
impl X86DotNetStructLayout {
pub fn new(name: &str, kind: X86DotNetLayoutKind) -> Self {
Self {
name: name.to_string(),
kind,
pack: None,
size: None,
}
}
pub fn to_cs_struct(&self, fields: &[(String, X86CType)]) -> String {
let mut out = String::new();
let pack_str = self
.pack
.map(|p| format!(", Pack = {}", p))
.unwrap_or_default();
let size_str = self
.size
.map(|s| format!(", Size = {}", s))
.unwrap_or_default();
out.push_str(&format!(
"[StructLayout({}{}{})]\n",
self.kind, pack_str, size_str
));
out.push_str(&format!("public struct {} {{\n", self.name));
for (fname, ftype) in fields {
out.push_str(&format!(
" public {} {};\n",
ctype_to_dotnet_type(ftype),
fname
));
}
out.push_str("}\n");
out
}
}
pub fn is_dotnet_blittable(ct: &X86CType) -> bool {
match ct {
X86CType::Char
| X86CType::SignedChar
| X86CType::UnsignedChar
| X86CType::Short
| X86CType::UnsignedShort
| X86CType::Int
| X86CType::UnsignedInt
| X86CType::Long
| X86CType::UnsignedLong
| X86CType::LongLong
| X86CType::UnsignedLongLong
| X86CType::Float
| X86CType::Double
| X86CType::Bool
| X86CType::Pointer(_)
| X86CType::ConstPointer(_) => true,
X86CType::Struct(_, _) => ct.size_x86_64(X86InteropABI::MicrosoftX64) <= 16,
X86CType::Enum(_, _) => true,
X86CType::FunctionPointer(_, _) => true,
_ => false,
}
}
#[derive(Debug, Clone)]
pub struct X86DotNetComInterop {
pub guid: String,
pub interface_name: String,
}
impl X86DotNetComInterop {
pub fn new(guid: &str, interface_name: &str) -> Self {
Self {
guid: guid.to_string(),
interface_name: interface_name.to_string(),
}
}
pub fn to_pinvoke_declaration(&self) -> String {
format!(
"[ComImport]\n[Guid(\"{}\")]\n[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\
public interface {} {{\n}}",
self.guid, self.interface_name
)
}
}
#[derive(Debug, Clone)]
pub struct X86WASMComponentModel {
pub interface_name: String,
pub types: Vec<X86WASMWitType>,
pub functions: Vec<X86WASMWitFunction>,
}
#[derive(Debug, Clone)]
pub struct X86WASMWitType {
pub name: String,
pub kind: X86WASMWitTypeKind,
}
#[derive(Debug, Clone)]
pub enum X86WASMWitTypeKind {
U8,
U16,
U32,
U64,
S8,
S16,
S32,
S64,
Float32,
Float64,
String,
List(Box<X86WASMWitTypeKind>),
Record(String, Vec<(String, X86WASMWitTypeKind)>),
Variant(String, Vec<(String, Option<X86WASMWitTypeKind>)>),
}
#[derive(Debug, Clone)]
pub struct X86WASMWitFunction {
pub name: String,
pub params: Vec<(String, X86WASMWitTypeKind)>,
pub results: Vec<(String, X86WASMWitTypeKind)>,
}
impl X86WASMComponentModel {
pub fn new(interface_name: &str) -> Self {
Self {
interface_name: interface_name.to_string(),
types: Vec::new(),
functions: Vec::new(),
}
}
pub fn to_wit_file(&self) -> String {
format!(
"interface {} {{\n // Types\n // Functions\n}}\n",
self.interface_name
)
}
}
#[derive(Debug, Clone)]
pub struct X86WASMMemoryModel {
pub initial_pages: u32,
pub max_pages: Option<u32>,
pub shared: bool,
}
impl X86WASMMemoryModel {
pub fn new(initial: u32, max: Option<u32>) -> Self {
Self {
initial_pages: initial,
max_pages: max,
shared: false,
}
}
pub fn to_wat_memory(&self) -> String {
let max_str = self
.max_pages
.map(|m| format!(" {}", m))
.unwrap_or_default();
format!("(memory {} {})", self.initial_pages, max_str)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86WASMTableType {
FuncRef,
ExternRef,
}
impl fmt::Display for X86WASMTableType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FuncRef => write!(f, "funcref"),
Self::ExternRef => write!(f, "externref"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86WASMTable {
pub initial: u32,
pub max: Option<u32>,
pub table_type: X86WASMTableType,
}
impl X86WASMTable {
pub fn new(initial: u32, max: Option<u32>, table_type: X86WASMTableType) -> Self {
Self {
initial,
max,
table_type,
}
}
pub fn to_wat_declaration(&self) -> String {
let max_str = self.max.map(|m| format!(" {}", m)).unwrap_or_default();
format!("(table {} {} {})", self.initial, max_str, self.table_type)
}
}
#[derive(Debug, Clone)]
pub struct X86WASMGlobal {
pub name: String,
pub wasm_type: X86WASMType,
pub initial_value: i64,
pub mutable: bool,
}
impl X86WASMGlobal {
pub fn new(name: &str, wasm_type: X86WASMType, value: i64) -> Self {
Self {
name: name.to_string(),
wasm_type,
initial_value: value,
mutable: true,
}
}
pub fn new_i32(name: &str, value: i32) -> Self {
Self::new(name, X86WASMType::I32, value as i64)
}
pub fn to_wat_declaration(&self) -> String {
let mut_str = if self.mutable { "(mut " } else { "" };
let close = if self.mutable { ")" } else { "" };
format!(
"(global ${} {}{}{} ({}.const {}))",
self.name,
mut_str,
self.wasm_type.to_wasm_text(),
close,
self.wasm_type.to_wasm_text(),
self.initial_value
)
}
}
#[derive(Debug, Clone)]
pub struct X86WASIPreview2Stubs;
impl X86WASIPreview2Stubs {
pub fn generate_all() -> String {
let mut out = String::new();
out.push_str("// Auto-generated WASI Preview 2 stubs\n\n");
let stub_modules = vec![
(
"wasi:io/streams",
vec!["read", "write", "flush", "blocking-read", "blocking-write"],
),
("wasi:cli/run", vec!["run"]),
("wasi:cli/stdin", vec!["get-stdin"]),
("wasi:cli/stdout", vec!["get-stdout"]),
("wasi:cli/stderr", vec!["get-stderr"]),
("wasi:cli/terminal-input", vec![]),
("wasi:cli/terminal-output", vec![]),
("wasi:cli/terminal-stdin", vec![]),
("wasi:cli/terminal-stdout", vec![]),
("wasi:cli/terminal-stderr", vec![]),
("wasi:clocks/wall-clock", vec!["now", "resolution"]),
(
"wasi:clocks/monotonic-clock",
vec!["now", "resolution", "subscribe"],
),
("wasi:clocks/timezone", vec!["display"]),
(
"wasi:filesystem/types",
vec!["descriptor-stat", "read-via-stream", "write-via-stream"],
),
("wasi:filesystem/preopens", vec!["get-directories"]),
(
"wasi:random/random",
vec!["get-random-bytes", "get-random-u64"],
),
(
"wasi:random/insecure",
vec!["get-insecure-random-bytes", "get-insecure-random-u64"],
),
("wasi:random/insecure-seed", vec!["insecure-seed"]),
("wasi:sockets/instance-network", vec![]),
("wasi:sockets/network", vec![]),
("wasi:sockets/tcp", vec!["subscribe", "drop-tcp-socket"]),
("wasi:sockets/udp", vec!["subscribe", "drop-udp-socket"]),
("wasi:http/outgoing-handler", vec!["handle"]),
("wasi:http/types", vec!["http-error-code"]),
];
for (module, funcs) in &stub_modules {
out.push_str(&format!("// Module: {}\n", module));
for func in funcs {
out.push_str(&format!("// - {}\n", func));
}
out.push('\n');
}
out
}
}
#[derive(Debug, Clone)]
pub struct X86EmscriptenEmbind {
pub module_name: String,
pub bindings: Vec<String>,
}
impl X86EmscriptenEmbind {
pub fn new(module_name: &str) -> Self {
Self {
module_name: module_name.to_string(),
bindings: Vec::new(),
}
}
pub fn add_function(&mut self, name: &str, c_name: &str) -> &mut Self {
self.bindings
.push(format!("function(\"{}\", &{})", name, c_name));
self
}
pub fn add_param(&mut self, _name: &str, _ty: &str) -> &mut Self {
self
}
pub fn returns(&mut self, _ty: &str) -> &mut Self {
self
}
pub fn build(&self) -> String {
let mut out = String::new();
out.push_str(&format!("EMSCRIPTEN_BINDINGS({}) {{\n", self.module_name));
for binding in &self.bindings {
out.push_str(&format!(" {}\n", binding));
}
out.push_str("}\n");
out
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct X86ErrorCode {
pub code: i32,
pub message: String,
}
impl X86ErrorCode {
pub fn new(code: i32, message: &str) -> Self {
Self {
code,
message: message.to_string(),
}
}
pub fn is_error(&self) -> bool {
self.code != 0
}
}
#[derive(Debug, Clone, Default)]
pub struct X86ErrorRegistry {
pub codes: BTreeMap<i32, String>,
}
impl X86ErrorRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn with_common_errors() -> Self {
let mut reg = Self::new();
reg.register(0, "Success");
reg.register(-1, "GenericError");
reg.register(-2, "InvalidArgument");
reg.register(-3, "NotFound");
reg.register(-4, "PermissionDenied");
reg.register(-5, "Timeout");
reg.register(-6, "OutOfMemory");
reg.register(-7, "NotSupported");
reg.register(-8, "BufferTooSmall");
reg.register(-9, "WouldBlock");
reg
}
pub fn register(&mut self, code: i32, name: &str) {
self.codes.insert(code, name.to_string());
}
pub fn lookup(&self, code: i32) -> Option<&String> {
self.codes.get(&code)
}
pub fn to_rust_error_enum(&self) -> String {
let mut out = String::new();
out.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n");
out.push_str("pub enum FfiError {\n");
for (code, name) in &self.codes {
if *code == 0 {
out.push_str(&format!(" {},\n", name));
} else {
out.push_str(&format!(" {},\n", name));
}
}
out.push_str("}\n\n");
out.push_str("impl std::fmt::Display for FfiError {\n");
out.push_str(" fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n");
out.push_str(" write!(f, \"{:?}\", self)\n");
out.push_str(" }\n");
out.push_str("}\n\n");
out.push_str("impl std::error::Error for FfiError {}\n");
out
}
pub fn to_python_exception_classes(&self) -> String {
let mut out = String::new();
out.push_str("class FfiError(Exception):\n pass\n\n");
for (_code, name) in &self.codes {
if name != "Success" {
out.push_str(&format!("class Ffi{}(FfiError):\n pass\n\n", name));
}
}
out
}
pub fn to_go_error_type(&self) -> String {
let mut out = String::new();
out.push_str("type FfiError int\n\n");
out.push_str("const (\n");
for (code, name) in &self.codes {
out.push_str(&format!(" Ffi{} FfiError = {}\n", name, code));
}
out.push_str(")\n\n");
out.push_str("func (e FfiError) Error() string {\n");
out.push_str(" switch e {\n");
for (code, name) in &self.codes {
out.push_str(&format!(" case Ffi{}: return \"{}\"\n", name, name));
}
out.push_str(" default: return \"unknown error\"\n");
out.push_str(" }\n");
out.push_str("}\n");
out
}
}
#[derive(Debug, Clone)]
pub struct X86ThreadSafety {
pub name: String,
pub is_send: bool,
pub is_sync: bool,
}
impl X86ThreadSafety {
pub fn new_send(name: &str) -> Self {
Self {
name: name.to_string(),
is_send: true,
is_sync: false,
}
}
pub fn new_send_sync(name: &str) -> Self {
Self {
name: name.to_string(),
is_send: true,
is_sync: true,
}
}
pub fn to_rust_impl(&self) -> String {
let mut out = String::new();
if self.is_send {
out.push_str(&format!("unsafe impl Send for {} {{}}\n", self.name));
}
if self.is_sync {
out.push_str(&format!("unsafe impl Sync for {} {{}}\n", self.name));
}
out
}
pub fn to_c_comment(&self) -> String {
format!("/* {} is thread-safe */", self.name)
}
}
#[derive(Debug, Clone)]
pub struct X86ThreadLocalStorage {
pub name: String,
pub ty: X86CType,
}
impl X86ThreadLocalStorage {
pub fn new(name: &str, ty: X86CType) -> Self {
Self {
name: name.to_string(),
ty,
}
}
pub fn to_c_declaration(&self) -> String {
format!("_Thread_local {} {};", self.ty.to_c_decl(), self.name)
}
pub fn to_rust_declaration(&self) -> String {
let rust_ty = map_c_type_to_rust(&self.ty).to_rust_decl();
format!(
"thread_local! {{\n static {}: std::cell::RefCell<{}> = std::cell::RefCell::new({}::default());\n}}",
self.name.to_uppercase(), rust_ty, rust_ty
)
}
}
#[derive(Debug, Clone)]
pub struct X86DebugInfoAnnotation {
pub function_name: String,
pub file_name: String,
pub line_number: u32,
}
impl X86DebugInfoAnnotation {
pub fn new(function_name: &str, file_name: &str, line_number: u32) -> Self {
Self {
function_name: function_name.to_string(),
file_name: file_name.to_string(),
line_number,
}
}
pub fn to_rust_debug_macro(&self) -> String {
format!("#[debug_handler]\npub fn {}()", self.function_name)
}
pub fn to_python_decorator(&self) -> String {
format!(
"@ffi_debug(file='{}', line={})\ndef {}(): pass",
self.file_name, self.line_number, self.function_name
)
}
}
#[derive(Debug, Clone)]
pub struct X86SharedMemoryLayout {
pub name: String,
pub fields: Vec<(String, X86CType)>,
pub total_size: u32,
pub alignment: u32,
}
impl X86SharedMemoryLayout {
pub fn new(name: &str, fields: &[(String, X86CType)]) -> Self {
let mut offset = 0u32;
let mut max_align = 1u32;
for (_, ty) in fields {
let a = ty.alignment_x86_64();
let s = ty.size_x86_64(X86InteropABI::SystemV);
max_align = max_align.max(a);
if offset % a != 0 {
offset += a - (offset % a);
}
offset += s;
}
if offset % max_align != 0 {
offset += max_align - (offset % max_align);
}
Self {
name: name.to_string(),
fields: fields.to_vec(),
total_size: offset,
alignment: max_align,
}
}
pub fn to_c_header(&self) -> String {
let mut out = String::new();
out.push_str(&format!("// Shared memory layout: {}\n", self.name));
out.push_str(&format!(
"// Total size: {} bytes, alignment: {}\n",
self.total_size, self.alignment
));
out.push_str(&format!("struct {} {{\n", self.name));
for (fname, ftype) in &self.fields {
out.push_str(&format!(
" alignas({}) volatile {} {};\n",
ftype.alignment_x86_64(),
ftype.to_c_decl(),
fname
));
}
out.push_str("};\n");
out.push_str(&format!(
"_Static_assert(sizeof(struct {}) == {}, \"Size mismatch\");\n",
self.name, self.total_size
));
out
}
}
#[derive(Debug, Clone)]
pub struct X86AtomicInterop {
pub name: String,
pub ty: X86CType,
}
impl X86AtomicInterop {
pub fn new(name: &str, ty: X86CType) -> Self {
Self {
name: name.to_string(),
ty,
}
}
pub fn to_c_atomic_ops(&self) -> String {
let cty = self.ty.to_c_decl();
format!(
"#include <stdatomic.h>\n\
atomic_{} {};\n\
{} atomic_fetch_add_{}({} delta) {{ return atomic_fetch_add(&{}, delta); }}\n\
{} atomic_load_{}(void) {{ return atomic_load(&{}); }}",
cty, self.name, cty, self.name, cty, self.name, cty, self.name, self.name
)
}
pub fn to_rust_atomic_ops(&self) -> String {
match self.ty {
X86CType::Int => format!(
"use std::sync::atomic::{{AtomicI32, Ordering}};\n\
static {}: AtomicI32 = AtomicI32::new(0);\n\
pub fn fetch_add_{}(delta: i32) -> i32 {{ {}.fetch_add(delta, Ordering::SeqCst) }}",
self.name.to_uppercase(), self.name, self.name.to_uppercase()
),
_ => format!("// Atomic ops for type {:?}", self.ty),
}
}
}
#[derive(Debug, Clone)]
pub struct X86PackedStructLayout {
pub name: String,
pub fields: Vec<(String, X86CType)>,
pub total_size: u32,
}
impl X86PackedStructLayout {
pub fn compute(name: &str, fields: &[(String, X86CType)]) -> Self {
let total_size: u32 = fields
.iter()
.map(|(_, ty)| ty.size_x86_64(X86InteropABI::SystemV))
.sum();
Self {
name: name.to_string(),
fields: fields.to_vec(),
total_size,
}
}
pub fn to_c_definition(&self) -> String {
let mut out = String::new();
out.push_str(&format!(
"struct __attribute__((packed)) {} {{\n",
self.name
));
for (fname, ftype) in &self.fields {
out.push_str(&format!(" {} {};\n", ftype.to_c_decl(), fname));
}
out.push_str("};\n");
out
}
}
#[derive(Debug, Clone)]
pub struct X86BitfieldLayout {
pub name: String,
pub fields: Vec<(String, u32)>,
pub total_bytes: u32,
}
impl X86BitfieldLayout {
pub fn compute(name: &str, fields: &[(String, u32)]) -> Self {
let total_bits: u32 = fields.iter().map(|(_, w)| w).sum();
let total_bytes = (total_bits + 7) / 8;
Self {
name: name.to_string(),
fields: fields.to_vec(),
total_bytes,
}
}
pub fn to_c_definition(&self) -> String {
let mut out = String::new();
out.push_str(&format!("struct {} {{\n", self.name));
for (fname, width) in &self.fields {
out.push_str(&format!(" unsigned int {} : {};\n", fname, width));
}
out.push_str("};\n");
out
}
}
#[derive(Debug, Clone)]
pub struct X86VarargsWrapper {
pub name: String,
pub return_type: X86CType,
}
impl X86VarargsWrapper {
pub fn new(name: &str, return_type: X86CType) -> Self {
Self {
name: name.to_string(),
return_type,
}
}
pub fn to_c_wrapper(&self) -> String {
format!(
"#include <stdarg.h>\n\
{} {}(const char* fmt, ...) {{\n\
va_list args;\n\
va_start(args, fmt);\n\
{} result = v{}(fmt, args);\n\
va_end(args);\n\
return result;\n\
}}",
self.return_type.to_c_decl(),
self.name,
self.return_type.to_c_decl(),
self.name
)
}
pub fn to_rust_wrapper(&self) -> String {
format!(
"pub fn {}(fmt: *const std::os::raw::c_char, ...) -> {} {{\n\
unsafe {{\n\
let mut ap: std::ffi::VaList;\n\
va_start!(ap, fmt);\n\
let result = v{}(fmt, ap);\n\
result\n\
}}\n\
}}",
self.name,
map_c_type_to_rust(&self.return_type).to_rust_decl(),
self.name
)
}
}
#[derive(Debug, Clone)]
pub struct X86ExportAnnotation {
pub function_name: String,
}
impl X86ExportAnnotation {
pub fn new(function_name: &str) -> Self {
Self {
function_name: function_name.to_string(),
}
}
pub fn to_linux_visibility(&self) -> String {
format!(
"__attribute__((visibility(\"default\"))) {} {}();",
"void", self.function_name
)
}
pub fn to_windows_dllexport(&self) -> String {
format!("__declspec(dllexport) void {}();", self.function_name)
}
pub fn to_rust_cfg_attr(&self) -> String {
format!(
"#[cfg_attr(target_os = \"windows\", link(name = \"{}\", kind = \"dylib\"))]\n\
extern \"C\" {{}}",
self.function_name
)
}
}
#[derive(Debug, Clone)]
pub struct X86ImportAnnotation {
pub function_name: String,
}
impl X86ImportAnnotation {
pub fn new(function_name: &str) -> Self {
Self {
function_name: function_name.to_string(),
}
}
pub fn to_windows_dllimport(&self) -> String {
format!("__declspec(dllimport) void {}();", self.function_name)
}
}
#[derive(Debug, Clone)]
pub struct X86SysVArgClassResult {
pub int_regs_used: usize,
pub sse_regs_used: usize,
pub needs_stack: bool,
pub is_hfa: bool,
pub hfa_type: Option<X86CType>,
}
#[derive(Debug, Clone)]
pub struct X86SysVArgClassifier;
impl X86SysVArgClassifier {
pub fn new() -> Self {
Self
}
pub fn classify_args(&self, args: &[X86CType]) -> X86SysVArgClassResult {
let mut int_regs = 0usize;
let mut sse_regs = 0usize;
let mut needs_stack = false;
let mut is_hfa = false;
let mut hfa_type = None;
let max_int = 6;
let max_sse = 8;
for arg in args {
let classes = classify_sysv_amd64(arg, 0);
for cls in &classes {
match cls {
X86RegClass::Integer => {
if int_regs < max_int {
int_regs += 1;
} else {
needs_stack = true;
}
}
X86RegClass::SSE => {
if sse_regs < max_sse {
sse_regs += 1;
} else {
needs_stack = true;
}
}
X86RegClass::Memory => {
needs_stack = true;
}
_ => {
needs_stack = true;
}
}
}
}
if let X86CType::Struct(_, fields) = args.first().unwrap_or(&X86CType::Void) {
if !fields.is_empty() {
let first_fp_type = &fields[0].1;
if first_fp_type.is_floating() {
let all_same_type = fields.iter().all(|(_, t)| {
std::mem::discriminant(t) == std::mem::discriminant(first_fp_type)
});
if all_same_type && fields.len() <= 4 {
is_hfa = true;
hfa_type = Some(first_fp_type.clone());
}
}
}
}
X86SysVArgClassResult {
int_regs_used: int_regs,
sse_regs_used: sse_regs,
needs_stack,
is_hfa,
hfa_type,
}
}
}
#[derive(Debug, Clone)]
pub struct X86MSX64ShadowSpace {
pub size: u32,
}
impl Default for X86MSX64ShadowSpace {
fn default() -> Self {
Self { size: 32 }
}
}
impl X86MSX64ShadowSpace {
pub fn to_asm_comment(&self) -> String {
format!("; shadow space: {} bytes", self.size)
}
}
#[derive(Debug, Clone)]
pub struct X86PlatformLinker {
pub platform: X86Platform,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86Platform {
LinuxGnu,
LinuxMusl,
MacOS,
Windows,
FreeBSD,
}
impl X86PlatformLinker {
pub fn linux_gnu() -> Self {
Self {
platform: X86Platform::LinuxGnu,
}
}
pub fn macos() -> Self {
Self {
platform: X86Platform::MacOS,
}
}
pub fn to_linker_script(&self, lib_name: &str) -> String {
match self.platform {
X86Platform::LinuxGnu => {
format!(
"SECTIONS {{\n .interp : {{ *(.interp*) }}\n .hash : {{ *(.hash*) }}\n .dynsym : {{ *(.dynsym*) }}\n .dynstr : {{ *(.dynstr*) }}\n .text : {{ *(.text*) }}\n .rodata : {{ *(.rodata*) }}\n .data : {{ *(.data*) }}\n .bss : {{ *(.bss*) }}\n}}\n",
)
}
_ => format!("/* Linker script for {} */\n", lib_name),
}
}
pub fn to_ld_flags(&self) -> String {
match self.platform {
X86Platform::MacOS => "-twolevel_namespace -undefined dynamic_lookup".into(),
X86Platform::LinuxGnu => "-Wl,--export-dynamic -Wl,--no-undefined".into(),
_ => String::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct X86PlatformDef {
pub lib_name: String,
pub platform: X86Platform,
}
impl X86PlatformDef {
pub fn windows(lib_name: &str) -> Self {
Self {
lib_name: lib_name.to_string(),
platform: X86Platform::Windows,
}
}
pub fn to_def_file(&self, exports: &[&str]) -> String {
let mut out = String::new();
out.push_str(&format!("LIBRARY {}\n", self.lib_name));
out.push_str("EXPORTS\n");
for exp in exports {
out.push_str(&format!(" {}\n", exp));
}
out
}
}
#[derive(Debug, Clone)]
pub struct X86FfiOverheadModel {
pub abi: X86InteropABI,
}
impl X86FfiOverheadModel {
pub fn new(abi: X86InteropABI) -> Self {
Self { abi }
}
pub fn estimate_direct_call_ns(&self) -> f64 {
match self.abi {
X86InteropABI::SystemV => 3.0,
X86InteropABI::MicrosoftX64 => 4.0, X86InteropABI::Cdecl => 5.0, _ => 5.0,
}
}
pub fn estimate_marshal_overhead_ns(&self, ty: &X86CType) -> f64 {
let sz = ty.size_x86_64(self.abi.clone());
sz as f64 * 0.5 + 2.0
}
}
#[derive(Debug, Clone)]
pub struct X86VersionedStruct {
pub name: String,
pub version: u32,
pub fields: Vec<(String, X86CType)>,
pub v2_additions: Vec<(String, X86CType, u32)>,
}
impl X86VersionedStruct {
pub fn new(name: &str, version: u32) -> Self {
Self {
name: name.to_string(),
version,
fields: Vec::new(),
v2_additions: Vec::new(),
}
}
pub fn add_field_v2(mut self, name: &str, ty: X86CType, since_version: u32) -> Self {
self.v2_additions
.push((name.to_string(), ty, since_version));
self
}
pub fn to_c_definition(&self) -> String {
let mut out = String::new();
out.push_str(&format!("struct {} {{\n", self.name));
out.push_str(" uint32_t version;\n");
for (fname, ftype) in &self.fields {
out.push_str(&format!(" {} {};\n", ftype.to_c_decl(), fname));
}
for (fname, ftype, _) in &self.v2_additions {
out.push_str(&format!(" {} {};\n", ftype.to_c_decl(), fname));
}
out.push_str("};\n");
out
}
pub fn to_rust_definition(&self) -> String {
let mut out = String::new();
out.push_str("#[repr(C)]\n");
out.push_str(&format!("pub struct {} {{\n", self.name));
out.push_str(" pub version: u32,\n");
for (fname, ftype) in &self.fields {
out.push_str(&format!(
" pub {}: {},\n",
fname,
map_c_type_to_rust(ftype).to_rust_decl()
));
}
for (fname, ftype, _) in &self.v2_additions {
out.push_str(&format!(
" pub {}: {},\n",
fname,
map_c_type_to_rust(ftype).to_rust_decl()
));
}
out.push_str("}\n");
out
}
pub fn generate_migration_code(&self, _newer: &Self) -> String {
format!(
"// Migration from {} v{} to v{}\n\
pub fn migrate_{}_to_v{}(old: &{}) -> {} {{\n\
// Copy old fields, initialize new fields\n\
unimplemented!()\n\
}}\n",
self.name,
self.version,
self.version + 1,
self.name,
self.version + 1,
self.name,
self.name
)
}
}
#[derive(Debug, Clone)]
pub struct X86SymbolVersionScript {
pub lib_name: String,
pub versions: Vec<(String, Vec<String>)>,
}
impl X86SymbolVersionScript {
pub fn new(lib_name: &str) -> Self {
Self {
lib_name: lib_name.to_string(),
versions: Vec::new(),
}
}
pub fn add_version(mut self, version: &str, symbols: &[&str]) -> Self {
self.versions.push((
version.to_string(),
symbols.iter().map(|s| s.to_string()).collect(),
));
self
}
pub fn build(&self) -> String {
let mut out = String::new();
out.push_str(&format!(
"/* Symbol version script for {} */\n",
self.lib_name
));
for (ver, syms) in &self.versions {
out.push_str(&format!("{} {{\n", ver));
out.push_str(" global:\n");
for s in syms {
out.push_str(&format!(" {};\n", s));
}
out.push_str("};\n");
}
out.push_str("\n");
out.push_str("VERSION_1.0 {\n");
out.push_str("} VERSION_PRIVATE;\n");
out.push_str("VERSION_PRIVATE {\n");
out.push_str(" local:\n");
out.push_str(" *;\n");
out.push_str("};\n");
out
}
}
#[derive(Debug, Clone)]
pub struct X86WeakSymbol {
pub name: String,
pub return_type: X86CType,
}
impl X86WeakSymbol {
pub fn new(name: &str, return_type: X86CType) -> Self {
Self {
name: name.to_string(),
return_type,
}
}
pub fn to_c_declaration(&self) -> String {
format!(
"{} {}() __attribute__((weak));",
self.return_type.to_c_decl(),
self.name
)
}
pub fn to_rust_declaration(&self) -> String {
format!(
"#[link_name = \"{}\"]\n\
extern \"C\" {{\n\
fn {}();\n\
}}",
self.name, self.name
)
}
}
#[derive(Debug, Clone)]
pub struct X86ABICompatibilityMatrix {
pub compat_map: HashMap<(X86InteropABI, X86InteropABI), bool>,
}
impl Default for X86ABICompatibilityMatrix {
fn default() -> Self {
let mut map = HashMap::new();
map.insert((X86InteropABI::SystemV, X86InteropABI::SystemV), true);
map.insert(
(X86InteropABI::MicrosoftX64, X86InteropABI::MicrosoftX64),
true,
);
map.insert((X86InteropABI::Cdecl, X86InteropABI::Cdecl), true);
map.insert((X86InteropABI::Stdcall, X86InteropABI::Stdcall), true);
map.insert((X86InteropABI::SystemV, X86InteropABI::MicrosoftX64), false);
map.insert((X86InteropABI::MicrosoftX64, X86InteropABI::SystemV), false);
map.insert((X86InteropABI::Cdecl, X86InteropABI::Stdcall), true);
map.insert((X86InteropABI::Stdcall, X86InteropABI::Cdecl), true);
Self { compat_map: map }
}
}
impl X86ABICompatibilityMatrix {
pub fn is_compatible(&self, a: X86InteropABI, b: X86InteropABI) -> bool {
self.compat_map.get(&(a, b)).copied().unwrap_or(false)
}
pub fn compatibility_warnings(&self, a: X86InteropABI, b: X86InteropABI) -> Vec<String> {
let mut warnings = Vec::new();
if !self.is_compatible(a.clone(), b.clone()) {
warnings.push(format!("ABI {} is not fully compatible with {}", a, b));
if (a == X86InteropABI::SystemV && b == X86InteropABI::MicrosoftX64)
|| (a == X86InteropABI::MicrosoftX64 && b == X86InteropABI::SystemV)
{
warnings.push(" - sizeof(long) differs (8 vs 4)".into());
warnings.push(" - Calling convention register usage differs".into());
warnings.push(" - Struct passing rules differ".into());
}
}
warnings
}
}
pub fn generate_interop_markdown_docs(interop: &X86Interop) -> String {
let mut out = String::new();
out.push_str(&format!("# X86 Interop Documentation\n\n"));
out.push_str(&format!("- **Architecture**: {}\n", interop.arch));
out.push_str(&format!("- **ABI**: {}\n", interop.abi));
out.push_str(&format!("- **64-bit**: {}\n", interop.is_64bit));
out.push_str(&format!(
"- **Pointer size**: {} bytes\n",
interop.pointer_size()
));
out.push_str(&format!(
"- **Long size**: {} bytes\n\n",
interop.long_size()
));
if !interop.cxx_interops.is_empty() {
out.push_str("## C++ Interop\n\n");
out.push_str("| C++ Name | C Name | Return Type |\n");
out.push_str("|----------|--------|-------------|\n");
for cxx in &interop.cxx_interops {
out.push_str(&format!(
"| {} | {} | {} |\n",
cxx.cxx_name,
cxx.c_name,
cxx.return_type.to_c_decl()
));
}
out.push('\n');
}
if !interop.rust_interops.is_empty() {
out.push_str("## Rust FFI\n\n");
for rust in &interop.rust_interops {
out.push_str(&format!("### `{}` → `{}`\n\n", rust.c_name, rust.rust_name));
out.push_str("```rust\n");
out.push_str(&rust.to_rust_import_block());
out.push_str("\n```\n\n");
}
}
out
}
pub fn to_doxygen_comment(interop: &X86CXXInterop) -> String {
let mut out = String::new();
out.push_str("/**\n");
out.push_str(&format!(
" * @brief C-compatible wrapper for {}\n",
interop.cxx_name
));
out.push_str(&format!(" * @name {}\n", interop.c_name));
for p in &interop.params {
out.push_str(&format!(" * @param {} {}\n", p.name, p.ty.to_c_decl()));
}
if interop.return_type != X86CType::Void {
out.push_str(&format!(" * @return {}\n", interop.return_type.to_c_decl()));
}
out.push_str(" */\n");
out
}
pub fn c_name_to_rust_snake(c_name: &str) -> String {
let mut result = String::new();
let mut chars = c_name.chars().peekable();
while let Some(ch) = chars.next() {
if ch.is_uppercase() {
if !result.is_empty() && !result.ends_with('_') {
result.push('_');
}
result.push(ch.to_ascii_lowercase());
while let Some(&next) = chars.peek() {
if next.is_uppercase() {
result.push(chars.next().unwrap().to_ascii_lowercase());
} else {
break;
}
}
} else {
result.push(ch);
}
}
result.trim_matches('_').to_string()
}
pub fn rust_name_to_camel(snake: &str) -> String {
snake
.split('_')
.filter(|s| !s.is_empty())
.map(|word| {
let mut c = word.chars();
match c.next() {
None => String::new(),
Some(first) => first.to_uppercase().to_string() + c.as_str(),
}
})
.collect()
}
pub fn escape_rust_keyword(name: &str) -> String {
let keywords: &[&str] = &[
"abstract", "as", "async", "await", "become", "box", "break", "const", "continue", "crate",
"do", "dyn", "else", "enum", "extern", "false", "final", "fn", "for", "if", "impl", "in",
"let", "loop", "macro", "match", "mod", "move", "mut", "override", "priv", "pub", "ref",
"return", "self", "Self", "static", "struct", "super", "trait", "true", "try", "type",
"typeof", "union", "unsafe", "unsized", "use", "virtual", "where", "while", "yield",
];
if keywords.contains(&name) {
format!("r#{}", name)
} else {
name.to_string()
}
}
pub fn escape_c_keyword(name: &str) -> String {
let keywords: &[&str] = &[
"auto",
"break",
"case",
"char",
"const",
"continue",
"default",
"do",
"double",
"else",
"enum",
"extern",
"float",
"for",
"goto",
"if",
"inline",
"int",
"long",
"register",
"restrict",
"return",
"short",
"signed",
"sizeof",
"static",
"struct",
"switch",
"typedef",
"union",
"unsigned",
"void",
"volatile",
"while",
"_Bool",
"_Complex",
"_Imaginary",
"class",
"namespace",
"template",
"typename",
"virtual",
];
if keywords.contains(&name) {
format!("_{}", name)
} else {
name.to_string()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86MemoryOwnership {
CallerAllocated,
CalleeAllocated,
SharedRefCounted,
Static,
Scoped,
}
impl X86MemoryOwnership {
pub fn to_doc_comment(&self) -> &'static str {
match self {
Self::CallerAllocated => {
"Caller allocates memory, callee fills it. Caller is responsible for freeing."
}
Self::CalleeAllocated => {
"Callee allocates memory. Caller must free using the provided deallocator."
}
Self::SharedRefCounted => "Memory is reference-counted. Caller must release when done.",
Self::Static => "Memory is statically allocated. Do not free.",
Self::Scoped => {
"Memory lifetime is tied to an object. Freed when the object is destroyed."
}
}
}
pub fn to_c_annotation(&self, func_name: &str) -> String {
match self {
Self::CallerAllocated => format!("// {}: caller-allocated buffer\n", func_name),
Self::CalleeAllocated => format!(
"// {}: caller must free() the returned pointer\n",
func_name
),
Self::SharedRefCounted => {
format!("// {}: caller must call _release() when done\n", func_name)
}
Self::Static => format!("// {}: returns static storage, do not free\n", func_name),
Self::Scoped => format!(
"// {}: result valid until parent object is destroyed\n",
func_name
),
}
}
pub fn to_rust_annotation(&self, func_name: &str) -> String {
match self {
Self::CallerAllocated => format!(
"// Safety: caller provides a pre-allocated buffer for {}\n",
func_name
),
Self::CalleeAllocated => format!(
"// Safety: returned pointer must be freed with lib::free_{}()\n",
func_name
),
Self::SharedRefCounted => {
format!("// The returned handle must be released via Drop or explicit _release()\n")
}
Self::Static => format!("// Returns a 'static reference; no deallocation needed\n"),
Self::Scoped => {
format!("// Returned reference is valid for 'lifetime tied to the parent object\n")
}
}
}
}
#[derive(Debug, Clone)]
pub struct X86AllocatorPair {
pub alloc_func: String,
pub free_func: String,
pub alloc_size_param: Option<String>,
pub context_type: Option<X86CType>,
}
impl X86AllocatorPair {
pub fn new(alloc_func: &str, free_func: &str) -> Self {
Self {
alloc_func: alloc_func.to_string(),
free_func: free_func.to_string(),
alloc_size_param: None,
context_type: None,
}
}
pub fn to_rust_allocator(&self) -> String {
format!(
"struct FfiAllocator;\n\
unsafe impl std::alloc::GlobalAlloc for FfiAllocator {{\n\
unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 {{\n\
{}(layout.size()) as *mut u8\n\
}}\n\
unsafe fn dealloc(&self, ptr: *mut u8, _layout: std::alloc::Layout) {{\n\
{}(ptr as *mut std::os::raw::c_void);\n\
}}\n\
}}",
self.alloc_func, self.free_func
)
}
pub fn to_c_header(&self) -> String {
format!(
"void* {}(size_t size);\nvoid {}(void* ptr);\n",
self.alloc_func, self.free_func
)
}
}
#[derive(Debug, Clone)]
pub struct X86ResourceHandle {
pub handle_type_name: String,
pub destroy_func: String,
pub is_opaque: bool,
pub is_send_sync: bool,
}
impl X86ResourceHandle {
pub fn new(handle_type_name: &str, destroy_func: &str) -> Self {
Self {
handle_type_name: handle_type_name.to_string(),
destroy_func: destroy_func.to_string(),
is_opaque: true,
is_send_sync: true,
}
}
pub fn to_rust_drop_impl(&self) -> String {
let mut out = String::new();
if self.is_opaque {
out.push_str(&format!(
"pub struct {}(*mut std::os::raw::c_void);\n\n",
self.handle_type_name
));
}
out.push_str(&format!("impl Drop for {} {{\n", self.handle_type_name));
out.push_str(&format!(" fn drop(&mut self) {{\n"));
if self.is_opaque {
out.push_str(&format!(
" unsafe {{ {}(self.0); }}\n",
self.destroy_func
));
} else {
out.push_str(&format!(
" unsafe {{ {}(&mut self.inner); }}\n",
self.destroy_func
));
}
out.push_str(" }\n");
out.push_str("}\n");
if self.is_send_sync {
out.push_str(&format!(
"unsafe impl Send for {} {{}}\n",
self.handle_type_name
));
out.push_str(&format!(
"unsafe impl Sync for {} {{}}\n",
self.handle_type_name
));
}
out
}
pub fn to_python_del(&self) -> String {
format!(
"class {}:\n def __init__(self, ptr):\n self._ptr = ptr\n\n def __del__(self):\n if self._ptr:\n _lib.{}(self._ptr)\n self._ptr = None\n",
self.handle_type_name, self.destroy_func
)
}
pub fn to_go_finalizer(&self) -> String {
format!(
"type {} struct {{ ptr unsafe.Pointer }}\n\n\
func New{}() *{} {{\n h := &{}{{}}\n runtime.SetFinalizer(h, func(h *{}) {{ C.{}(h.ptr) }})\n return h\n}}\n",
self.handle_type_name, self.handle_type_name, self.handle_type_name,
self.handle_type_name, self.handle_type_name, self.destroy_func
)
}
}
#[derive(Debug, Clone, Default)]
pub struct X86TypeCoercionTable {
pub entries: Vec<X86TypeCoercionEntry>,
}
#[derive(Debug, Clone)]
pub struct X86TypeCoercionEntry {
pub c_type: X86CType,
pub cxx_type: String,
pub rust_type: X86RustFfiType,
pub python_type: String,
pub go_type: String,
pub java_type: String,
pub dotnet_type: String,
pub wasm_type: X86WASMType,
pub size_x86_64: u32,
pub size_i386: u32,
}
impl X86TypeCoercionTable {
pub fn build_comprehensive() -> Self {
let mut table = Self::default();
let entries = vec![
(
"void",
X86CType::Void,
"void",
X86RustFfiType::Unit,
"None",
"",
"void",
"void",
0,
0,
),
(
"_Bool",
X86CType::Bool,
"bool",
X86RustFfiType::Bool,
"bool",
"bool",
"boolean",
"bool",
1,
1,
),
(
"char",
X86CType::Char,
"char",
X86RustFfiType::CChar,
"bytes",
"byte",
"byte",
"sbyte",
1,
1,
),
(
"signed char",
X86CType::SignedChar,
"signed char",
X86RustFfiType::CSchar,
"int",
"int8",
"byte",
"sbyte",
1,
1,
),
(
"unsigned char",
X86CType::UnsignedChar,
"unsigned char",
X86RustFfiType::CUchar,
"int",
"uint8",
"byte",
"byte",
1,
1,
),
(
"short",
X86CType::Short,
"short",
X86RustFfiType::CShort,
"int",
"int16",
"short",
"short",
2,
2,
),
(
"unsigned short",
X86CType::UnsignedShort,
"unsigned short",
X86RustFfiType::CUshort,
"int",
"uint16",
"int",
"ushort",
2,
2,
),
(
"int",
X86CType::Int,
"int",
X86RustFfiType::CInt,
"int",
"int32",
"int",
"int",
4,
4,
),
(
"unsigned int",
X86CType::UnsignedInt,
"unsigned int",
X86RustFfiType::CUint,
"int",
"uint32",
"int",
"uint",
4,
4,
),
(
"long (LP64)",
X86CType::Long,
"long",
X86RustFfiType::CLong,
"int",
"int64",
"long",
"long",
8,
4,
),
(
"unsigned long (LP64)",
X86CType::UnsignedLong,
"unsigned long",
X86RustFfiType::CUlong,
"int",
"uint64",
"long",
"ulong",
8,
4,
),
(
"long long",
X86CType::LongLong,
"long long",
X86RustFfiType::CLonglong,
"int",
"int64",
"long",
"long",
8,
8,
),
(
"unsigned long long",
X86CType::UnsignedLongLong,
"unsigned long long",
X86RustFfiType::CUlonglong,
"int",
"uint64",
"long",
"ulong",
8,
8,
),
(
"float",
X86CType::Float,
"float",
X86RustFfiType::CFloat,
"float",
"float32",
"float",
"float",
4,
4,
),
(
"double",
X86CType::Double,
"double",
X86RustFfiType::CDouble,
"float",
"float64",
"double",
"double",
8,
8,
),
(
"long double",
X86CType::LongDouble,
"long double",
X86RustFfiType::F64,
"float",
"float64",
"double",
"double",
16,
12,
),
(
"void*",
X86CType::Pointer(Box::new(X86CType::Void)),
"void*",
X86RustFfiType::MutVoidPtr,
"int",
"uintptr",
"long",
"IntPtr",
8,
4,
),
(
"const void*",
X86CType::ConstPointer(Box::new(X86CType::Void)),
"const void*",
X86RustFfiType::ConstVoidPtr,
"int",
"uintptr",
"long",
"IntPtr",
8,
4,
),
];
for (
_name,
c_type,
cxx_type,
rust_type,
python_type,
go_type,
java_type,
dotnet_type,
size64,
size32,
) in entries
{
let wasm_t = ctype_to_wasm_type(&c_type);
table.entries.push(X86TypeCoercionEntry {
c_type,
cxx_type: cxx_type.to_string(),
rust_type,
python_type: python_type.to_string(),
go_type: go_type.to_string(),
java_type: java_type.to_string(),
dotnet_type: dotnet_type.to_string(),
wasm_type: wasm_t,
size_x86_64: size64,
size_i386: size32,
});
}
table
}
pub fn to_markdown_table(&self) -> String {
let mut out = String::new();
out.push_str(
"| C Type | C++ | Rust | Python | Go | Java | .NET | WASM | x86_64 | i386 |\n",
);
out.push_str(
"|--------|-----|------|--------|----|------|------|------|--------|------|\n",
);
for entry in &self.entries {
out.push_str(&format!(
"| {} | {} | {} | {} | {} | {} | {} | {} | {} | {} |\n",
entry.c_type.to_c_decl(),
entry.cxx_type,
entry.rust_type.to_rust_decl(),
entry.python_type,
entry.go_type,
entry.java_type,
entry.dotnet_type,
entry.wasm_type.to_wasm_text(),
entry.size_x86_64,
entry.size_i386,
));
}
out
}
}
#[derive(Debug, Clone)]
pub struct X86InteropValidator {
pub warnings: Vec<String>,
pub errors: Vec<String>,
}
impl X86InteropValidator {
pub fn new() -> Self {
Self {
warnings: Vec::new(),
errors: Vec::new(),
}
}
pub fn validate_cxx(&mut self, interop: &X86CXXInterop) {
for p in &interop.params {
if !is_c_compatible_type(&p.ty) {
self.errors.push(format!(
"Parameter '{}' has non-C-compatible type '{}'",
p.name,
p.ty.to_c_decl()
));
}
}
if interop.exception_spec == X86ExceptionSpec::None {
self.warnings.push(format!(
"Function '{}' has no exception specification. C++ exceptions may leak across C boundary.",
interop.c_name
));
}
if interop.c_name.starts_with('_')
&& interop
.c_name
.chars()
.nth(1)
.map_or(false, |c| c.is_uppercase())
{
self.warnings.push(format!(
"C name '{}' starts with underscore+uppercase (reserved).",
interop.c_name
));
}
if interop.is_variadic {
self.warnings.push(format!(
"Variadic function '{}' — callers in other languages may need special handling.",
interop.c_name
));
}
}
pub fn validate_rust(&mut self, interop: &X86RustInterop) {
if !interop.is_unsafe && interop.direction == X86RustFfiDirection::RustCallsC {
self.warnings.push(format!(
"Rust FFI import '{}' calls C but is not marked unsafe.",
interop.rust_name
));
}
let has_repr_c = interop
.attributes
.iter()
.any(|a| *a == X86RustFfiAttribute::ReprC);
if !has_repr_c && !interop.params.is_empty() {
}
}
pub fn validate_python(&mut self, interop: &X86PythonInterop) {
if interop.backend == X86PythonBackend::CPython {
if interop.method_flags == X86PyMethodFlags::VarArgs {
}
}
}
pub fn is_valid(&self) -> bool {
self.errors.is_empty()
}
pub fn all_messages(&self) -> Vec<String> {
let mut msgs = Vec::new();
for e in &self.errors {
msgs.push(format!("ERROR: {}", e));
}
for w in &self.warnings {
msgs.push(format!("WARNING: {}", w));
}
msgs
}
pub fn report(&self) -> String {
let mut out = String::new();
out.push_str("=== X86 Interop Validation Report ===\n\n");
out.push_str(&format!("Errors: {}\n", self.errors.len()));
out.push_str(&format!("Warnings: {}\n\n", self.warnings.len()));
for msg in self.all_messages() {
out.push_str(&format!(" {}\n", msg));
}
if self.is_valid() {
out.push_str("\n✓ Validation passed\n");
} else {
out.push_str("\n✗ Validation failed\n");
}
out
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_x86_interop_default() {
let interop = X86Interop::default();
assert_eq!(interop.arch, "x86_64");
assert_eq!(interop.abi, X86InteropABI::SystemV);
assert!(interop.is_64bit);
assert_eq!(interop.total_interop_count(), 0);
}
#[test]
fn test_x86_interop_new_linux_x86_64() {
let interop = X86Interop::new_linux_x86_64();
assert_eq!(interop.arch, "x86_64");
assert_eq!(interop.abi, X86InteropABI::SystemV);
assert!(interop.is_64bit);
assert_eq!(interop.pointer_size(), 8);
assert_eq!(interop.long_size(), 8);
}
#[test]
fn test_x86_interop_new_windows_x64() {
let interop = X86Interop::new_windows_x64();
assert_eq!(interop.abi, X86InteropABI::MicrosoftX64);
assert_eq!(interop.pointer_size(), 8);
assert_eq!(interop.long_size(), 4); }
#[test]
fn test_x86_interop_new_linux_i386() {
let interop = X86Interop::new_linux_i386();
assert_eq!(interop.arch, "i386");
assert!(!interop.is_64bit);
assert_eq!(interop.pointer_size(), 4);
assert_eq!(interop.long_size(), 4);
}
#[test]
fn test_x86_interop_add_entries() {
let mut interop = X86Interop::default();
let cxx = X86CXXInterop::new("foo", "foo_c", X86CType::Int);
interop.add_cxx_interop(cxx);
assert_eq!(interop.total_interop_count(), 1);
assert_eq!(interop.cxx_interops.len(), 1);
}
#[test]
fn test_x86_interop_generate_ffi_header() {
let mut interop = X86Interop::default();
let mut cxx = X86CXXInterop::new("add", "add_c", X86CType::Int);
cxx.add_param("a", X86CType::Int);
cxx.add_param("b", X86CType::Int);
interop.add_cxx_interop(cxx);
let header = interop.generate_ffi_header();
assert!(header.contains("x86_interop_ffi"));
assert!(header.contains("add_c"));
assert!(header.contains("extern \"C\""));
}
#[test]
fn test_x86_interop_generate_build_script() {
let mut interop = X86Interop::default();
let cxx = X86CXXInterop::new("foo", "foo_c", X86CType::Void);
interop.add_cxx_interop(cxx);
let _rust = X86RustInterop::new("bar", "bar_c", X86RustFfiType::I32);
interop.add_rust_interop(_rust);
let script = interop.generate_build_script();
assert!(script.contains("cxx_target_0"));
assert!(script.contains("rust_target_0"));
}
#[test]
fn test_x86_interop_generate_all_bindings() {
let mut interop = X86Interop::default();
let cxx = X86CXXInterop::new("add_ints", "add_ints", X86CType::Int);
interop.add_cxx_interop(cxx);
let mut rust = X86RustInterop::new("add", "add_ints", X86RustFfiType::CInt);
rust.add_param("a", X86RustFfiType::CInt);
rust.add_param("b", X86RustFfiType::CInt);
interop.add_rust_interop(rust);
let bindings = interop.generate_all_bindings();
assert!(bindings.contains("add_ints"));
assert!(bindings.contains("extern \"C\""));
assert!(bindings.contains("Rust FFI"));
}
#[test]
fn test_x86_interop_abi_display() {
assert_eq!(X86InteropABI::SystemV.to_string(), "sysv");
assert_eq!(X86InteropABI::MicrosoftX64.to_string(), "msx64");
assert_eq!(X86InteropABI::Cdecl.to_string(), "cdecl");
assert_eq!(X86InteropABI::Custom("x".into()).to_string(), "custom(x)");
}
#[test]
fn test_x86_interop_pointer_size() {
let linux64 = X86Interop::new_linux_x86_64();
assert_eq!(linux64.pointer_size(), 8);
let win64 = X86Interop::new_windows_x64();
assert_eq!(win64.pointer_size(), 8);
let linux32 = X86Interop::new_linux_i386();
assert_eq!(linux32.pointer_size(), 4);
}
#[test]
fn test_ctype_to_c_decl() {
assert_eq!(X86CType::Int.to_c_decl(), "int");
assert_eq!(X86CType::Void.to_c_decl(), "void");
assert_eq!(
X86CType::Pointer(Box::new(X86CType::Int)).to_c_decl(),
"int*"
);
assert_eq!(
X86CType::ConstPointer(Box::new(X86CType::Char)).to_c_decl(),
"const char*"
);
}
#[test]
fn test_ctype_size_x86_64() {
assert_eq!(X86CType::Int.size_x86_64(X86InteropABI::SystemV), 4);
assert_eq!(X86CType::Long.size_x86_64(X86InteropABI::SystemV), 8);
assert_eq!(X86CType::Long.size_x86_64(X86InteropABI::MicrosoftX64), 4);
assert_eq!(
X86CType::Pointer(Box::new(X86CType::Void)).size_x86_64(X86InteropABI::SystemV),
8
);
assert_eq!(X86CType::Double.size_x86_64(X86InteropABI::SystemV), 8);
assert_eq!(X86CType::Char.size_x86_64(X86InteropABI::SystemV), 1);
}
#[test]
fn test_ctype_size_i386() {
assert_eq!(X86CType::Int.size_i386(), 4);
assert_eq!(X86CType::Long.size_i386(), 4);
assert_eq!(X86CType::Pointer(Box::new(X86CType::Void)).size_i386(), 4);
}
#[test]
fn test_ctype_alignment_x86_64() {
assert_eq!(X86CType::Int.alignment_x86_64(), 4);
assert_eq!(X86CType::Long.alignment_x86_64(), 8);
assert_eq!(X86CType::Double.alignment_x86_64(), 8);
assert_eq!(X86CType::Char.alignment_x86_64(), 1);
}
#[test]
fn test_ctype_struct_size() {
let fields = vec![
("x".to_string(), X86CType::Int),
("y".to_string(), X86CType::Double),
];
let st = X86CType::Struct("Point".into(), fields);
assert_eq!(st.size_x86_64(X86InteropABI::SystemV), 16);
}
#[test]
fn test_ctype_union_size() {
let fields = vec![
("i".to_string(), X86CType::Int),
("d".to_string(), X86CType::Double),
];
let un = X86CType::Union("Value".into(), fields);
assert_eq!(un.size_x86_64(X86InteropABI::SystemV), 8);
}
#[test]
fn test_ctype_is_integer() {
assert!(X86CType::Int.is_integer());
assert!(X86CType::Long.is_integer());
assert!(X86CType::Char.is_integer());
assert!(!X86CType::Float.is_integer());
assert!(!X86CType::Pointer(Box::new(X86CType::Void)).is_integer());
}
#[test]
fn test_ctype_is_floating() {
assert!(X86CType::Float.is_floating());
assert!(X86CType::Double.is_floating());
assert!(!X86CType::Int.is_floating());
}
#[test]
fn test_ctype_is_register_class() {
assert!(X86CType::Int.is_register_class_int());
assert!(X86CType::Pointer(Box::new(X86CType::Void)).is_register_class_int());
assert!(X86CType::Float.is_register_class_sse());
assert!(!X86CType::Int.is_register_class_sse());
}
#[test]
fn test_ctype_memory_class_sysv() {
let big_struct = X86CType::Struct(
"Big".into(),
vec![
("a".into(), X86CType::Double),
("b".into(), X86CType::Double),
("c".into(), X86CType::Double),
],
);
assert!(big_struct.is_memory_class(X86InteropABI::SystemV));
let small_struct = X86CType::Struct(
"Small".into(),
vec![("a".into(), X86CType::Int), ("b".into(), X86CType::Int)],
);
assert!(!small_struct.is_memory_class(X86InteropABI::SystemV));
}
#[test]
fn test_cxx_interop_new() {
let interop = X86CXXInterop::new("my_func", "my_func_c", X86CType::Int);
assert_eq!(interop.cxx_name, "my_func");
assert_eq!(interop.c_name, "my_func_c");
assert_eq!(interop.return_type, X86CType::Int);
assert!(!interop.is_variadic);
assert_eq!(interop.exception_spec, X86ExceptionSpec::None);
}
#[test]
fn test_cxx_interop_new_method() {
let interop = X86CXXInterop::new_method(
"MyClass",
"do_thing",
"my_class_do_thing",
X86CType::Void,
true,
);
assert!(interop.is_method);
assert_eq!(interop.class_name.unwrap(), "MyClass");
assert!(interop.is_const);
assert_eq!(interop.params.len(), 1); }
#[test]
fn test_cxx_interop_add_param() {
let mut interop = X86CXXInterop::new("foo", "foo_c", X86CType::Void);
interop.add_param("x", X86CType::Int);
interop.add_param("y", X86CType::Double);
assert_eq!(interop.params.len(), 2);
}
#[test]
fn test_cxx_interop_to_c_declaration() {
let mut interop = X86CXXInterop::new("add", "add_c", X86CType::Int);
interop.add_param("a", X86CType::Int);
interop.add_param("b", X86CType::Int);
let decl = interop.to_c_declaration();
assert!(decl.contains("add_c"));
assert!(decl.contains("int a"));
assert!(decl.contains("int b"));
}
#[test]
fn test_cxx_interop_to_extern_c_block() {
let mut interop = X86CXXInterop::new("foo", "foo_c", X86CType::Void);
interop.add_param("n", X86CType::Int);
let block = interop.to_extern_c_block();
assert!(block.contains("extern \"C\""));
assert!(block.contains("foo_c"));
}
#[test]
fn test_cxx_interop_with_exception_spec() {
let interop = X86CXXInterop::new("foo", "foo_c", X86CType::Void)
.with_exception_spec(X86ExceptionSpec::Noexcept);
assert_eq!(interop.exception_spec, X86ExceptionSpec::Noexcept);
}
#[test]
fn test_cxx_interop_to_c_wrapper_impl() {
let interop = X86CXXInterop::new("add", "add_c", X86CType::Int);
let impl_code = interop.to_c_wrapper_impl();
assert!(impl_code.contains("add_c"));
assert!(impl_code.contains("return"));
}
#[test]
fn test_cxx_interop_to_llvm_declare() {
let mut interop = X86CXXInterop::new("add", "add_c", X86CType::Int);
interop.add_param("a", X86CType::Int);
let llvm = interop.to_llvm_declare();
assert!(llvm.contains("declare"));
assert!(llvm.contains("add_c"));
}
#[test]
fn test_cxx_interop_variadic() {
let interop =
X86CXXInterop::new("printf_wrap", "printf_wrap", X86CType::Int).with_variadic(true);
assert!(interop.is_variadic);
let decl = interop.to_c_declaration();
assert!(decl.contains("..."));
}
#[test]
fn test_cxx_interop_call_conv() {
let interop =
X86CXXInterop::new("foo", "foo_c", X86CType::Void).with_call_conv(X86CallConv::Stdcall);
assert_eq!(interop.c_call_conv, X86CallConv::Stdcall);
let decl = interop.to_c_declaration();
assert!(decl.contains("stdcall"));
}
#[test]
fn test_exception_spec_to_cxx_suffix() {
assert_eq!(X86ExceptionSpec::None.to_cxx_suffix(), "");
assert_eq!(X86ExceptionSpec::Noexcept.to_cxx_suffix(), " noexcept");
assert_eq!(X86ExceptionSpec::ThrowNone.to_cxx_suffix(), " throw()");
assert_eq!(
X86ExceptionSpec::ThrowTypes(vec!["std::bad_alloc".into()]).to_cxx_suffix(),
" throw(std::bad_alloc)"
);
assert_eq!(
X86ExceptionSpec::NoexceptConditional(true).to_cxx_suffix(),
" noexcept(true)"
);
}
#[test]
fn test_linkage_kind_display() {
assert_eq!(X86LinkageKind::External.to_string(), "external");
assert_eq!(X86LinkageKind::Internal.to_string(), "internal");
assert_eq!(X86LinkageKind::Weak.to_string(), "weak");
}
#[test]
fn test_call_conv_as_attribute() {
assert_eq!(X86CallConv::Cdecl.as_attribute(), Some("cdecl"));
assert_eq!(X86CallConv::SysVAMD64.as_attribute(), None);
}
#[test]
fn test_call_conv_integer_arg_regs() {
assert_eq!(X86CallConv::SysVAMD64.integer_arg_regs_64(), 6);
assert_eq!(X86CallConv::MicrosoftX64.integer_arg_regs_64(), 4);
assert_eq!(X86CallConv::Cdecl.integer_arg_regs_64(), 0);
}
#[test]
fn test_call_conv_sse_arg_regs() {
assert_eq!(X86CallConv::SysVAMD64.sse_arg_regs_64(), 8);
assert_eq!(X86CallConv::MicrosoftX64.sse_arg_regs_64(), 4);
}
#[test]
fn test_cattribute_to_decl() {
assert_eq!(
X86CAttribute::Aligned(16).to_decl(),
"__attribute__((aligned(16)))"
);
assert_eq!(X86CAttribute::Packed.to_decl(), "__attribute__((packed))");
assert_eq!(
X86CAttribute::Visibility(VisibilityKind::Hidden).to_decl(),
"__attribute__((visibility(\"hidden\")))"
);
assert_eq!(X86CAttribute::Dllexport.to_decl(), "__declspec(dllexport)");
}
#[test]
fn test_mangling_registry_register() {
let mut reg = X86ManglingRegistry::new();
reg.register("my_func", "_Z7my_funcv", "my_func_c");
assert_eq!(reg.len(), 1);
assert_eq!(reg.c_name_for_mangled("_Z7my_funcv"), Some("my_func_c"));
assert!(reg.is_registered_mangled("_Z7my_funcv"));
}
#[test]
fn test_mangling_registry_lookup() {
let mut reg = X86ManglingRegistry::new();
reg.register("foo", "_Z3foov", "foo_c");
assert_eq!(reg.mangle_for("foo"), Some("_Z3foov"));
let mangles = reg.mangles_for_c("foo_c").unwrap();
assert_eq!(mangles.len(), 1);
assert_eq!(mangles[0], "_Z3foov");
}
#[test]
fn test_mangling_registry_to_define_header() {
let mut reg = X86ManglingRegistry::new();
reg.register("foo", "_Z3foov", "foo_c");
let hdr = reg.to_define_header();
assert!(hdr.contains("_Z3foov"));
assert!(hdr.contains("foo_c"));
}
#[test]
fn test_rust_interop_new() {
let interop = X86RustInterop::new("add", "add_c", X86RustFfiType::CInt);
assert_eq!(interop.rust_name, "add");
assert_eq!(interop.c_name, "add_c");
assert!(interop.is_unsafe);
assert_eq!(interop.direction, X86RustFfiDirection::RustCallsC);
}
#[test]
fn test_rust_interop_new_export() {
let interop = X86RustInterop::new_export("callback", "callback_c", X86RustFfiType::Unit);
assert_eq!(interop.direction, X86RustFfiDirection::CCallsRust);
assert!(!interop.is_unsafe);
}
#[test]
fn test_rust_interop_add_param() {
let mut interop = X86RustInterop::new("foo", "foo_c", X86RustFfiType::Unit);
interop.add_param("len", X86RustFfiType::CInt);
interop.add_param("data", X86RustFfiType::MutVoidPtr);
assert_eq!(interop.params.len(), 2);
}
#[test]
fn test_rust_interop_to_import_block() {
let mut interop = X86RustInterop::new("add", "add_c", X86RustFfiType::CInt);
interop.add_param("a", X86RustFfiType::CInt);
interop.add_param("b", X86RustFfiType::CInt);
let block = interop.to_rust_import_block();
assert!(block.contains("extern \"C\""));
assert!(block.contains("add_c"));
assert!(block.contains("c_int"));
}
#[test]
fn test_rust_interop_to_export_fn() {
let mut interop =
X86RustInterop::new_export("handle_cb", "handle_cb_c", X86RustFfiType::Unit);
interop.add_param("val", X86RustFfiType::CInt);
let fn_code = interop.to_rust_export_fn();
assert!(fn_code.contains("#[no_mangle]"));
assert!(fn_code.contains("extern \"C\""));
}
#[test]
fn test_rust_interop_to_safe_wrapper() {
let mut interop = X86RustInterop::new("get_str", "get_str_c", X86RustFfiType::ConstVoidPtr);
interop.add_param("n", X86RustFfiType::CInt);
let wrapper = interop.to_rust_safe_wrapper();
assert!(wrapper.contains("pub fn get_str"));
assert!(wrapper.contains("unsafe"));
}
#[test]
fn test_rust_interop_cbingen() {
let interop = X86RustInterop::new("foo", "foo_c", X86RustFfiType::I32).with_cbingen();
assert!(interop.use_cbingen);
let out = interop.to_cbingen_output();
assert!(out.contains("cbingen"));
}
#[test]
fn test_rust_interop_autocxx() {
let interop = X86RustInterop::new("foo", "foo_c", X86RustFfiType::I32).with_autocxx();
assert!(interop.use_autocxx);
let out = interop.to_autocxx_output();
assert!(out.contains("autocxx"));
assert!(out.contains("include_cpp!"));
}
#[test]
fn test_rust_interop_attributes() {
let interop = X86RustInterop::new("foo", "foo_c", X86RustFfiType::I32)
.with_attribute(X86RustFfiAttribute::ReprC)
.with_attribute(X86RustFfiAttribute::Cold);
assert_eq!(interop.attributes.len(), 4); }
#[test]
fn test_rust_ffi_type_to_rust_decl() {
assert_eq!(X86RustFfiType::I32.to_rust_decl(), "i32");
assert_eq!(X86RustFfiType::F64.to_rust_decl(), "f64");
assert_eq!(X86RustFfiType::CInt.to_rust_decl(), "std::os::raw::c_int");
assert_eq!(
X86RustFfiType::MutVoidPtr.to_rust_decl(),
"*mut std::os::raw::c_void"
);
assert_eq!(
X86RustFfiType::Custom("MyStruct".into()).to_rust_decl(),
"MyStruct"
);
}
#[test]
fn test_rust_ffi_type_slice() {
let slice_i32 = X86RustFfiType::Slice(Box::new(X86RustFfiType::I32));
assert_eq!(slice_i32.to_rust_decl(), "&[i32]");
}
#[test]
fn test_rust_ffi_type_nullable() {
let nullable = X86RustFfiType::Nullable(Box::new(X86RustFfiType::MutVoidPtr));
assert_eq!(nullable.to_rust_decl(), "Option<*mut std::os::raw::c_void>");
}
#[test]
fn test_map_c_type_to_rust_basic() {
assert_eq!(map_c_type_to_rust(&X86CType::Int), X86RustFfiType::CInt);
assert_eq!(
map_c_type_to_rust(&X86CType::Double),
X86RustFfiType::CDouble
);
assert_eq!(map_c_type_to_rust(&X86CType::Void), X86RustFfiType::Unit);
assert_eq!(map_c_type_to_rust(&X86CType::Bool), X86RustFfiType::Bool);
}
#[test]
fn test_map_c_type_to_rust_pointer() {
assert_eq!(
map_c_type_to_rust(&X86CType::Pointer(Box::new(X86CType::Void))),
X86RustFfiType::MutVoidPtr
);
assert_eq!(
map_c_type_to_rust(&X86CType::ConstPointer(Box::new(X86CType::Char))),
X86RustFfiType::ConstVoidPtr
);
}
#[test]
fn test_rust_type_to_c() {
assert_eq!(rust_type_to_c(&X86RustFfiType::CInt), "int");
assert_eq!(rust_type_to_c(&X86RustFfiType::CDouble), "double");
assert_eq!(rust_type_to_c(&X86RustFfiType::MutVoidPtr), "void*");
assert_eq!(rust_type_to_c(&X86RustFfiType::Unit), "void");
}
#[test]
fn test_rust_struct_layout_compute() {
let fields = vec![
("a".to_string(), X86CType::Int),
("b".to_string(), X86CType::Double),
];
let layout = X86RustStructLayout::compute_x86_64("Point", &fields);
assert_eq!(layout.total_size, 16);
assert_eq!(layout.alignment, 8);
assert_eq!(layout.fields.len(), 2);
assert_eq!(layout.fields[0].offset, 0);
assert_eq!(layout.fields[1].offset, 8);
}
#[test]
fn test_rust_struct_layout_to_rust_def() {
let fields = vec![("x".to_string(), X86CType::Int)];
let layout = X86RustStructLayout::compute_x86_64("Pos", &fields);
let def = layout.to_rust_def();
assert!(def.contains("#[repr(C)]"));
assert!(def.contains("pub struct Pos"));
assert!(def.contains("pub x:"));
}
#[test]
fn test_rust_struct_layout_static_assert() {
let fields = vec![("x".to_string(), X86CType::Double)];
let layout = X86RustStructLayout::compute_x86_64("D", &fields);
let sa = layout.to_static_assert_rust();
assert!(sa.contains("std::mem::size_of::<D>()"));
let c_sa = layout.to_static_assert_c();
assert!(c_sa.contains("_Static_assert"));
}
#[test]
fn test_python_interop_new() {
let interop = X86PythonInterop::new(
X86PythonBackend::CPython,
"mymodule",
"py_add",
"add_c",
X86CType::Int,
);
assert_eq!(interop.module_name, "mymodule");
assert_eq!(interop.py_func_name, "py_add");
assert_eq!(interop.c_func_name, "add_c");
}
#[test]
fn test_python_interop_ctypes_binding() {
let mut interop = X86PythonInterop::new(
X86PythonBackend::Ctypes,
"testmod",
"py_add",
"add_c",
X86CType::Int,
);
interop.add_param("a", X86CType::Int);
interop.add_param("b", X86CType::Int);
let binding = interop.to_python_binding();
assert!(binding.contains("import ctypes"));
assert!(binding.contains("add_c"));
assert!(binding.contains("py_add"));
}
#[test]
fn test_python_interop_cffi_binding() {
let interop = X86PythonInterop::new(
X86PythonBackend::Cffi,
"testmod",
"py_func",
"c_func",
X86CType::Double,
);
let binding = interop.to_python_binding();
assert!(binding.contains("from cffi import FFI"));
assert!(binding.contains("c_func"));
}
#[test]
fn test_python_interop_pybind11_binding() {
let interop = X86PythonInterop::new(
X86PythonBackend::Pybind11,
"testmod",
"py_func",
"c_func",
X86CType::Void,
);
let binding = interop.to_python_binding();
assert!(binding.contains("pybind11"));
assert!(binding.contains("PYBIND11_MODULE"));
}
#[test]
fn test_python_interop_cython_binding() {
let interop = X86PythonInterop::new(
X86PythonBackend::Cython,
"testmod",
"py_func",
"c_func",
X86CType::Int,
);
let binding = interop.to_python_binding();
assert!(binding.contains("cython: language_level=3"));
assert!(binding.contains("cdef extern"));
}
#[test]
fn test_python_interop_numpy_binding() {
let interop = X86PythonInterop::new(
X86PythonBackend::NumPy,
"testmod",
"process",
"process_c",
X86CType::Void,
);
let binding = interop.to_python_binding();
assert!(binding.contains("numpy/arrayobject.h"));
assert!(binding.contains("PyArrayObject"));
}
#[test]
fn test_python_interop_method_flags() {
let interop =
X86PythonInterop::new(X86PythonBackend::CPython, "m", "f", "c_f", X86CType::Void)
.with_method_flags(X86PyMethodFlags::NoArgs);
assert_eq!(interop.method_flags.to_c_macro(), "METH_NOARGS");
}
#[test]
fn test_py_parse_format_char() {
assert_eq!(py_parse_format_char(&X86CType::Int), "i");
assert_eq!(py_parse_format_char(&X86CType::Double), "d");
assert_eq!(py_parse_format_char(&X86CType::Float), "f");
assert_eq!(py_parse_format_char(&X86CType::Char), "b");
}
#[test]
fn test_py_ctype_for() {
assert_eq!(py_ctype_for(&X86CType::Int), "int");
assert_eq!(py_ctype_for(&X86CType::Double), "double");
assert_eq!(py_ctype_for(&X86CType::Bool), "int");
}
#[test]
fn test_c_to_py_build() {
assert!(c_to_py_build(&X86CType::Int, "x").contains("PyLong_FromLong(x)"));
assert!(c_to_py_build(&X86CType::Double, "x").contains("PyFloat_FromDouble(x)"));
assert!(c_to_py_build(&X86CType::Bool, "x").contains("PyBool_FromLong(x)"));
}
#[test]
fn test_ctype_to_python_ctypes_type() {
assert_eq!(ctype_to_python_ctypes_type(&X86CType::Int), "ctypes.c_int");
assert_eq!(
ctype_to_python_ctypes_type(&X86CType::Double),
"ctypes.c_double"
);
assert_eq!(ctype_to_python_ctypes_type(&X86CType::Void), "None");
}
#[test]
fn test_go_interop_new() {
let interop = X86GoInterop::new("main", "Add", "add_c", X86CType::Int);
assert_eq!(interop.package_name, "main");
assert_eq!(interop.go_func_name, "Add");
assert!(!interop.is_callback);
}
#[test]
fn test_go_interop_callback() {
let interop = X86GoInterop::new_callback("main", "MyCallback", X86CType::Void);
assert!(interop.is_callback);
}
#[test]
fn test_go_interop_to_binding() {
let mut interop = X86GoInterop::new("mypackage", "Add", "add_c", X86CType::Int);
interop.add_param("a", X86CType::Int);
interop.add_param("b", X86CType::Int);
let binding = interop.to_go_binding();
assert!(binding.contains("package mypackage"));
assert!(binding.contains("import \"C\""));
assert!(binding.contains("add_c"));
}
#[test]
fn test_go_interop_cgo_directives() {
let interop = X86GoInterop::new("pkg", "Func", "c_func", X86CType::Void)
.with_cgo_directive(X86CgoDirective::CFlags("-O2 -march=x86-64".into()))
.with_cgo_directive(X86CgoDirective::LdFlags("-lm".into()));
assert_eq!(interop.cgo_directives.len(), 2);
let binding = interop.to_go_binding();
assert!(binding.contains("-O2"));
assert!(binding.contains("-lm"));
}
#[test]
fn test_ctype_to_go_type() {
assert_eq!(ctype_to_go_type(&X86CType::Int), "int32");
assert_eq!(ctype_to_go_type(&X86CType::Long), "int64");
assert_eq!(ctype_to_go_type(&X86CType::Double), "float64");
assert_eq!(ctype_to_go_type(&X86CType::Void), "");
}
#[test]
fn test_go_string_conversions() {
let conv = go_string_to_c_string("goStr", "cStr");
assert!(conv.contains("C.CString(goStr)"));
assert!(conv.contains("C.free"));
let conv2 = c_string_to_go_string("cStr", "goStr");
assert!(conv2.contains("C.GoString(cStr)"));
}
#[test]
fn test_java_interop_new() {
let interop =
X86JavaInterop::new("com/example/MyClass", "myMethod", "(II)I", X86CType::Int);
assert_eq!(interop.java_class, "com/example/MyClass");
assert!(interop.c_func_name.contains("Java_"));
assert!(!interop.is_static);
}
#[test]
fn test_java_interop_constructor() {
let interop = X86JavaInterop::new_constructor("com/example/MyClass", "()V");
assert!(interop.is_constructor);
assert_eq!(interop.java_method, "<init>");
}
#[test]
fn test_java_interop_to_binding() {
let mut interop =
X86JavaInterop::new("com/example/MyClass", "compute", "(I)I", X86CType::Int);
interop.add_param("input", X86CType::Int);
let binding = interop.to_java_binding();
assert!(binding.contains("jni.h"));
assert!(binding.contains("JNIEXPORT"));
assert!(binding.contains("JNICALL"));
}
#[test]
fn test_java_interop_signature_generation() {
let params = vec![
X86CParam::new("a", X86CType::Int),
X86CParam::new("b", X86CType::Double),
];
let sig = X86JavaInterop::generate_signature_from_params(¶ms, &X86CType::Void);
assert_eq!(sig, "(ID)V");
let sig2 = X86JavaInterop::generate_signature_from_params(¶ms, &X86CType::Int);
assert_eq!(sig2, "(ID)I");
}
#[test]
fn test_ctype_to_jni_type() {
assert_eq!(ctype_to_jni_type(&X86CType::Int), "jint");
assert_eq!(ctype_to_jni_type(&X86CType::Double), "jdouble");
assert_eq!(ctype_to_jni_type(&X86CType::Bool), "jboolean");
}
#[test]
fn test_ctype_to_jni_signature() {
assert_eq!(ctype_to_jni_signature(&X86CType::Int), "I");
assert_eq!(ctype_to_jni_signature(&X86CType::Double), "D");
assert_eq!(ctype_to_jni_signature(&X86CType::Bool), "Z");
}
#[test]
fn test_dotnet_interop_new() {
let interop = X86DotNetInterop::new(
"MyApp.Interop",
"NativeMethods",
"Add",
"add_c",
"libnative.so",
X86CType::Int,
);
assert_eq!(interop.namespace, "MyApp.Interop");
assert_eq!(interop.class_name, "NativeMethods");
assert_eq!(interop.dll_name, "libnative.so");
}
#[test]
fn test_dotnet_interop_to_binding() {
let mut interop = X86DotNetInterop::new(
"App",
"Native",
"Calculate",
"calc_c",
"libcalc.so",
X86CType::Double,
);
interop.add_param("x", X86CType::Double);
interop.add_param("n", X86CType::Int);
let binding = interop.to_dotnet_binding();
assert!(binding.contains("DllImport"));
assert!(binding.contains("libcalc.so"));
assert!(binding.contains("public static extern"));
}
#[test]
fn test_dotnet_interop_reverse_pinvoke() {
let interop = X86DotNetInterop::new(
"App",
"Native",
"Callback",
"cb_c",
"lib.so",
X86CType::Void,
);
let rev = interop.to_reverse_pinvoke();
assert!(rev.contains("UnmanagedFunctionPointer"));
assert!(rev.contains("delegate"));
}
#[test]
fn test_dotnet_interop_call_conv() {
let interop = X86DotNetInterop::new("App", "N", "F", "f_c", "lib.so", X86CType::Void)
.with_call_conv(X86DotNetCallConv::StdCall);
let binding = interop.to_dotnet_binding();
assert!(binding.contains("StdCall"));
}
#[test]
fn test_dotnet_interop_marshal() {
let interop = X86DotNetInterop::new("App", "N", "F", "f_c", "lib.so", X86CType::Void)
.with_marshal(X86DotNetMarshal::LPStr);
assert_eq!(interop.marshaling.len(), 1);
}
#[test]
fn test_ctype_to_dotnet_type() {
assert_eq!(ctype_to_dotnet_type(&X86CType::Int), "int");
assert_eq!(ctype_to_dotnet_type(&X86CType::Double), "double");
assert_eq!(
ctype_to_dotnet_type(&X86CType::Pointer(Box::new(X86CType::Void))),
"IntPtr"
);
}
#[test]
fn test_wasm_interop_new() {
let interop =
X86WASMInterop::new("mymodule", "my_func", "my_func_c", X86WASMDirection::Export);
assert_eq!(interop.module_name, "mymodule");
assert_eq!(interop.wasm_func_name, "my_func");
}
#[test]
fn test_wasm_interop_import_binding() {
let mut interop = X86WASMInterop::new("env", "log", "log_c", X86WASMDirection::Import);
interop.add_param(X86WASMType::I32);
let binding = interop.to_wasm_binding();
assert!(binding.contains("(import \"env\" \"log\""));
assert!(binding.contains("i32"));
}
#[test]
fn test_wasm_interop_export_binding() {
let mut interop = X86WASMInterop::new("math", "add", "add_c", X86WASMDirection::Export);
interop.add_param(X86WASMType::I32);
interop.add_param(X86WASMType::I32);
interop.set_result(X86WASMType::I32);
let binding = interop.to_wasm_binding();
assert!(binding.contains("(export \"add\""));
assert!(binding.contains("(func $add"));
}
#[test]
fn test_wasm_interop_emscripten() {
let config = X86EmscriptenConfig {
keepalive: true,
em_js: None,
em_asm: None,
ccall_name: Some("myFunc".into()),
use_embind: false,
exported_functions: vec!["_my_func".into()],
extra_runtime_methods: vec!["ccall".into()],
};
let mut interop = X86WASMInterop::new("mod", "func", "func_c", X86WASMDirection::Export);
interop.add_param(X86WASMType::I32);
interop.emscripten_config = Some(config);
let binding = interop.to_emscripten_binding();
assert!(binding.contains("EMSCRIPTEN_KEEPALIVE"));
assert!(binding.contains("myFunc"));
}
#[test]
fn test_wasm_interop_wasi_stub() {
let mut interop = X86WASMInterop::new(
"wasi_snapshot_preview1",
"fd_write",
"fd_write",
X86WASMDirection::Import,
);
interop.wasi_api = Some("fd_write".into());
let stub = interop.to_wasi_stub();
assert!(stub.contains("__wasi_fd_write"));
assert!(stub.contains("__WASI_ERRNO_SUCCESS"));
}
#[test]
fn test_wasm_interop_multiple_wasi() {
for api in &[
"fd_read",
"fd_close",
"fd_seek",
"proc_exit",
"random_get",
"clock_time_get",
"args_get",
"args_sizes_get",
"environ_get",
"environ_sizes_get",
] {
let mut interop = X86WASMInterop::new("wasi", api, api, X86WASMDirection::Import);
interop.wasi_api = Some(api.to_string());
let stub = interop.to_wasi_stub();
assert!(
!stub.is_empty(),
"WASI stub for {} should not be empty",
api
);
}
}
#[test]
fn test_wasm_type_to_c() {
assert_eq!(wasm_type_to_c(&X86WASMType::I32), "int32_t");
assert_eq!(wasm_type_to_c(&X86WASMType::I64), "int64_t");
assert_eq!(wasm_type_to_c(&X86WASMType::F32), "float");
assert_eq!(wasm_type_to_c(&X86WASMType::F64), "double");
}
#[test]
fn test_ctype_to_wasm_type() {
assert_eq!(ctype_to_wasm_type(&X86CType::Int), X86WASMType::I32);
assert_eq!(ctype_to_wasm_type(&X86CType::Long), X86WASMType::I64);
assert_eq!(ctype_to_wasm_type(&X86CType::Float), X86WASMType::F32);
assert_eq!(ctype_to_wasm_type(&X86CType::Double), X86WASMType::F64);
}
#[test]
fn test_cxx_class_wrapper_new() {
let wrapper = X86CXXClassWrapper::new("MyNamespace::MyClass");
assert!(wrapper.c_opaque_name.contains("Handle"));
assert_eq!(wrapper.methods.len(), 0);
}
#[test]
fn test_cxx_class_wrapper_add_method() {
let mut wrapper = X86CXXClassWrapper::new("MyClass");
let method = wrapper.add_method("doWork", X86CType::Int, true);
method.add_param("param", X86CType::Double);
assert_eq!(wrapper.methods.len(), 1);
assert_eq!(wrapper.methods[0].params.len(), 2); }
#[test]
fn test_cxx_class_wrapper_to_c_header() {
let mut wrapper = X86CXXClassWrapper::new("MyClass");
wrapper.set_constructor(vec![X86CParam::new("val", X86CType::Int)]);
wrapper.set_destructor();
let hdr = wrapper.to_c_header();
assert!(hdr.contains("Handle"));
assert!(hdr.contains("create"));
assert!(hdr.contains("destroy"));
}
#[test]
fn test_callback_type_to_c_typedef() {
let mut cb = X86CallbackType::new("MyCallback", X86CType::Void, X86CallConv::Cdecl);
cb.add_param(X86CType::Int);
cb.add_param(X86CType::Pointer(Box::new(X86CType::Void)));
let td = cb.to_c_typedef();
assert!(td.contains("typedef"));
assert!(td.contains("MyCallback"));
}
#[test]
fn test_callback_type_to_rust_type_alias() {
let mut cb = X86CallbackType::new("MyCallback", X86CType::Int, X86CallConv::Cdecl);
cb.add_param(X86CType::Double);
let alias = cb.to_rust_type_alias();
assert!(alias.contains("pub type MyCallback"));
assert!(alias.contains("extern \"C\""));
}
#[test]
fn test_rust_callback_to_code() {
let mut cb_type = X86CallbackType::new("MyCB", X86CType::Void, X86CallConv::Cdecl);
cb_type.add_param(X86CType::Int);
let cb = X86RustCallback::new("my_callback", cb_type);
let code = cb.to_rust_code();
assert!(code.contains("#[no_mangle]"));
assert!(code.contains("extern \"C\""));
}
#[test]
fn test_rust_callback_trampoline() {
let mut cb_type = X86CallbackType::new("MyCB", X86CType::Int, X86CallConv::Cdecl);
cb_type.add_param(X86CType::Int);
let mut cb = X86RustCallback::new("my_callback", cb_type);
cb.use_trampoline = true;
let code = cb.to_trampoline_code();
assert!(code.contains("trampoline"));
assert!(code.contains("transmute"));
}
#[test]
fn test_go_callback_to_code() {
let mut cb_type = X86CallbackType::new("MyCB", X86CType::Void, X86CallConv::Cdecl);
cb_type.add_param(X86CType::Int);
let mut cb = X86GoCallback::new("myCallback", cb_type);
cb.go_body = "\tfmt.Println(\"called\")\n".into();
let code = cb.to_go_code();
assert!(code.contains("//export myCallback"));
assert!(code.contains("fmt.Println"));
}
#[test]
fn test_classify_sysv_amd64_integer() {
let classes = classify_sysv_amd64(&X86CType::Int, 0);
assert_eq!(classes, vec![X86RegClass::Integer]);
}
#[test]
fn test_classify_sysv_amd64_float() {
let classes = classify_sysv_amd64(&X86CType::Float, 0);
assert_eq!(classes, vec![X86RegClass::SSE]);
}
#[test]
fn test_classify_sysv_amd64_double() {
let classes = classify_sysv_amd64(&X86CType::Double, 0);
assert_eq!(classes, vec![X86RegClass::SSE]);
}
#[test]
fn test_classify_sysv_amd64_pointer() {
let classes = classify_sysv_amd64(&X86CType::Pointer(Box::new(X86CType::Void)), 0);
assert_eq!(classes, vec![X86RegClass::Integer]);
}
#[test]
fn test_classify_sysv_amd64_long_double() {
let classes = classify_sysv_amd64(&X86CType::LongDouble, 0);
assert_eq!(classes, vec![X86RegClass::X87, X86RegClass::X87Up]);
}
#[test]
fn test_classify_msx64() {
assert_eq!(classify_msx64(&X86CType::Int), X86RegClass::Integer);
assert_eq!(classify_msx64(&X86CType::Float), X86RegClass::SSE);
assert_eq!(classify_msx64(&X86CType::Double), X86RegClass::SSE);
}
#[test]
fn test_merge_reg_class() {
use X86RegClass::*;
assert_eq!(merge_reg_class(Integer, Integer), Integer);
assert_eq!(merge_reg_class(SSE, SSE), SSE);
assert_eq!(merge_reg_class(NoClass, Integer), Integer);
assert_eq!(merge_reg_class(Integer, NoClass), Integer);
assert_eq!(merge_reg_class(Memory, Integer), Memory);
assert_eq!(merge_reg_class(Integer, SSE), Integer);
}
#[test]
fn test_parse_ctype_string() {
assert_eq!(parse_ctype_string("int"), Some(X86CType::Int));
assert_eq!(parse_ctype_string("double"), Some(X86CType::Double));
assert_eq!(parse_ctype_string("void"), Some(X86CType::Void));
assert_eq!(
parse_ctype_string("int*"),
Some(X86CType::Pointer(Box::new(X86CType::Int)))
);
}
#[test]
fn test_parse_c_function_decl() {
let result = parse_c_function_decl("int add(int a, int b)");
assert!(result.is_some());
let (name, ret, params) = result.unwrap();
assert_eq!(name, "add");
assert_eq!(ret, X86CType::Int);
assert_eq!(params.len(), 2);
}
#[test]
fn test_is_c_compatible_type() {
assert!(is_c_compatible_type(&X86CType::Int));
assert!(is_c_compatible_type(&X86CType::Double));
assert!(is_c_compatible_type(&X86CType::Pointer(Box::new(
X86CType::Void
))));
assert!(is_c_compatible_type(&X86CType::Struct("S".into(), vec![])));
}
#[test]
fn test_make_c_wrapper_name() {
let name = make_c_wrapper_name("MyNamespace::MyClass::method");
assert!(!name.contains("::"));
assert!(name.contains("MyNamespace"));
assert!(name.contains("MyClass"));
}
#[test]
fn test_make_rust_safe_name() {
assert_eq!(make_rust_safe_name("my_func_c"), "my_func_c");
}
#[test]
fn test_gen_rust_to_c_string() {
let code = gen_rust_to_c_string("s", "cs");
assert!(code.contains("CString::new(s)"));
assert!(code.contains("cs.as_ptr()"));
}
#[test]
fn test_gen_c_string_to_rust() {
let code = gen_c_string_to_rust("cs", "rs");
assert!(code.contains("CStr::from_ptr(cs)"));
assert!(code.contains("to_str()"));
}
#[test]
fn test_gen_py_string_to_c() {
let code = gen_py_string_to_c("py_obj", "c_str");
assert!(code.contains("PyUnicode_AsUTF8"));
}
#[test]
fn test_gen_c_string_to_py() {
let code = gen_c_string_to_py("c_str", "py_obj");
assert!(code.contains("PyUnicode_FromString"));
}
#[test]
fn test_gen_go_string_to_c() {
let conv = gen_go_string_to_c("g", "c");
assert!(conv.contains("C.CString(g)"));
}
#[test]
fn test_gen_c_string_to_go() {
let conv = gen_c_string_to_go("c", "g");
assert!(conv.contains("C.GoString(c)"));
}
#[test]
fn test_full_interop_workflow_cxx_to_rust() {
let mut interop = X86Interop::new_linux_x86_64();
let mut cxx = X86CXXInterop::new("compute", "compute_c", X86CType::Double);
cxx.add_param("x", X86CType::Double);
cxx.add_param("y", X86CType::Double);
interop.add_cxx_interop(cxx);
let mut rust = X86RustInterop::new("compute", "compute_c", X86RustFfiType::CDouble);
rust.add_param("x", X86RustFfiType::CDouble);
rust.add_param("y", X86RustFfiType::CDouble);
interop.add_rust_interop(rust);
assert_eq!(interop.total_interop_count(), 2);
let ffi_header = interop.generate_ffi_header();
assert!(ffi_header.contains("compute_c"));
assert!(ffi_header.contains("double"));
let rust_binding = &interop.rust_interops[0].to_rust_binding();
assert!(rust_binding.contains("compute_c"));
assert!(rust_binding.contains("c_double"));
}
#[test]
fn test_full_interop_workflow_python_ctypes() {
let mut interop = X86Interop::new_linux_x86_64();
let mut py = X86PythonInterop::new(
X86PythonBackend::Ctypes,
"mymod",
"py_add",
"add_c",
X86CType::Int,
);
py.add_param("a", X86CType::Int);
py.add_param("b", X86CType::Int);
interop.add_python_interop(py);
let all = interop.generate_all_bindings();
assert!(all.contains("ctypes"));
assert!(all.contains("py_add"));
}
#[test]
fn test_full_interop_workflow_cpp_class_to_c() {
let mut wrapper = X86CXXClassWrapper::new("Calculator");
wrapper.set_constructor(vec![X86CParam::new("mode", X86CType::Int)]);
wrapper.set_destructor();
wrapper
.add_method("add", X86CType::Int, true)
.add_param("a", X86CType::Int)
.add_param("b", X86CType::Int);
let hdr = wrapper.to_c_header();
assert!(hdr.contains("CalculatorHandle"));
assert!(hdr.contains("create"));
assert!(hdr.contains("destroy"));
assert!(hdr.contains("add"));
}
#[test]
fn test_full_interop_workflow_wasm_emscripten() {
let config = X86EmscriptenConfig {
keepalive: true,
em_js: None,
em_asm: None,
ccall_name: Some("computeResult".into()),
use_embind: false,
exported_functions: vec!["_compute".into()],
extra_runtime_methods: vec!["ccall".into(), "cwrap".into()],
};
let mut wasm = X86WASMInterop::new(
"my_module",
"compute",
"compute_c",
X86WASMDirection::Export,
);
wasm.add_param(X86WASMType::I32);
wasm.set_result(X86WASMType::F64);
wasm.emscripten_config = Some(config);
let binding = wasm.to_emscripten_binding();
assert!(binding.contains("EMSCRIPTEN_KEEPALIVE"));
assert!(binding.contains("computeResult"));
let wasm_binding = wasm.to_wasm_binding();
assert!(wasm_binding.contains("(export"));
}
#[test]
fn test_x86interop_linker_flags() {
let mut interop = X86Interop::new_linux_x86_64();
interop.add_linker_flag("-lm");
interop.add_linker_flag("-lpthread");
interop.add_compile_flag("-O2");
assert_eq!(interop.linker_flags.len(), 2);
assert_eq!(interop.compile_flags.len(), 1);
let script = interop.generate_build_script();
assert!(script.contains("-lm"));
assert!(script.contains("-O2"));
}
#[test]
fn test_empty_interop_generation() {
let interop = X86Interop::default();
assert_eq!(interop.total_interop_count(), 0);
let header = interop.generate_ffi_header();
assert!(header.contains("X86_INTEROP_FFI_H"));
}
#[test]
fn test_void_return_with_params() {
let mut interop = X86CXXInterop::new("print_val", "print_val_c", X86CType::Void);
interop.add_param("val", X86CType::Int);
let decl = interop.to_c_declaration();
assert!(decl.contains("void"));
assert!(decl.contains("int val"));
}
#[test]
fn test_function_pointer_type() {
let fptr = X86CType::FunctionPointer(
Box::new(X86CType::Void),
vec![X86CType::Int, X86CType::Pointer(Box::new(X86CType::Void))],
);
let decl = fptr.to_c_decl();
assert!(decl.contains("(*)"));
}
#[test]
fn test_array_type() {
let arr = X86CType::Array(Box::new(X86CType::Int), 10);
assert_eq!(arr.to_c_decl(), "int[10]");
assert_eq!(arr.size_x86_64(X86InteropABI::SystemV), 40);
}
#[test]
fn test_typedef_type() {
let td = X86CType::Typedef("my_int".into(), Box::new(X86CType::Int));
assert_eq!(td.to_c_decl(), "my_int");
assert_eq!(td.size_x86_64(X86InteropABI::SystemV), 4);
}
#[test]
fn test_enum_type() {
let e = X86CType::Enum(
"Color".into(),
vec![("Red".into(), 0), ("Green".into(), 1), ("Blue".into(), 2)],
);
assert_eq!(e.to_c_decl(), "enum Color");
assert_eq!(e.size_x86_64(X86InteropABI::SystemV), 4);
}
#[test]
fn test_parse_ctype_const_pointer() {
let ct = parse_ctype_string("const char*");
assert!(ct.is_some());
}
#[test]
fn test_simd_type_m128() {
let m128 = X86SIMDType::M128;
assert_eq!(m128.size(), 16);
assert_eq!(m128.alignment(), 16);
assert_eq!(m128.to_intel_intrinsic(), "__m128");
}
#[test]
fn test_simd_type_m256() {
let m256 = X86SIMDType::M256;
assert_eq!(m256.size(), 32);
assert_eq!(m256.alignment(), 32);
}
#[test]
fn test_simd_type_m512() {
let m512 = X86SIMDType::M512;
assert_eq!(m512.size(), 64);
assert_eq!(m512.alignment(), 64);
}
#[test]
fn test_simd_type_to_c_decl() {
assert_eq!(X86SIMDType::M128.to_c_decl(), "__m128");
assert_eq!(X86SIMDType::M128d.to_c_decl(), "__m128d");
assert_eq!(X86SIMDType::M128i.to_c_decl(), "__m128i");
assert_eq!(X86SIMDType::M256.to_c_decl(), "__m256");
assert_eq!(X86SIMDType::M256d.to_c_decl(), "__m256d");
assert_eq!(X86SIMDType::M512.to_c_decl(), "__m512");
}
#[test]
fn test_simd_rust_mapping() {
assert_eq!(
X86SIMDType::M128.to_rust_type(),
"std::arch::x86_64::__m128"
);
assert_eq!(
X86SIMDType::M256.to_rust_type(),
"std::arch::x86_64::__m256"
);
}
#[test]
fn test_simd_call_conv_mapping() {
let mapping = X86SIMDCallConvMapping::default();
let conv = mapping.for_simd_type(&X86SIMDType::M128);
assert_eq!(conv, X86CallConv::Vectorcall);
}
#[test]
fn test_simd_boundary_bridge() {
let bridge = X86SIMDBoundaryBridge::new(
"vec4_add",
X86SIMDType::M128,
X86SIMDType::M128,
vec![X86SIMDType::M128, X86SIMDType::M128],
);
let c_decl = bridge.to_c_declaration();
assert!(c_decl.contains("vec4_add"));
assert!(c_decl.contains("__m128"));
}
#[test]
fn test_zero_copy_layout_compute() {
let fields = vec![
(
"data".to_string(),
X86CType::Pointer(Box::new(X86CType::Char)),
),
("len".to_string(), X86CType::Long),
("cap".to_string(), X86CType::Long),
];
let layout = X86ZeroCopyLayout::compute("Buf", &fields);
assert_eq!(layout.fields.len(), 3);
assert!(layout.is_pod);
}
#[test]
fn test_zero_copy_to_rust_def() {
let fields = vec![(
"ptr".to_string(),
X86CType::Pointer(Box::new(X86CType::Void)),
)];
let layout = X86ZeroCopyLayout::compute("Ptr", &fields);
let rust = layout.to_rust_zero_copy_def();
assert!(rust.contains("#[repr(C)]"));
assert!(rust.contains("unsafe"));
assert!(rust.contains("impl"));
}
#[test]
fn test_zero_copy_validation() {
let fields = vec![("x".to_string(), X86CType::Int)];
let layout = X86ZeroCopyLayout::compute("S", &fields);
assert!(layout.validate::<i32>());
}
#[test]
fn test_async_ffi_bridge_new() {
let bridge = X86AsyncFfiBridge::new(
"async_op",
X86CType::Int,
vec![X86CParam::new("timeout_ms", X86CType::Int)],
);
assert_eq!(bridge.c_name, "async_op");
assert_eq!(bridge.params.len(), 1);
}
#[test]
fn test_async_ffi_to_rust_async() {
let bridge = X86AsyncFfiBridge::new(
"read_async",
X86CType::Int,
vec![X86CParam::new("fd", X86CType::Int)],
);
let rust = bridge.to_rust_async_wrapper();
assert!(rust.contains("async fn"));
assert!(rust.contains("tokio::task::spawn_blocking"));
}
#[test]
fn test_async_callback_pattern() {
let pattern = X86AsyncCallbackPattern::new(
"on_data",
X86CType::Pointer(Box::new(X86CType::Char)),
vec![X86CParam::new("len", X86CType::Int)],
);
let code = pattern.to_c_pattern();
assert!(code.contains("typedef"));
assert!(code.contains("void* user_data"));
let rust = pattern.to_rust_pattern();
assert!(rust.contains("unsafe extern \"C\""));
}
#[test]
fn test_python_numpy_array_creation() {
let spec = X86NumPyArraySpec::new_f64("data", vec![100, 100]);
assert_eq!(spec.dtype, X86NumPyDtype::Float64);
assert_eq!(spec.shape, vec![100, 100]);
let c_code = spec.to_c_creation_code();
assert!(c_code.contains("PyArray_SimpleNew"));
assert!(c_code.contains("npy_intp"));
}
#[test]
fn test_python_numpy_contiguous_check() {
let spec = X86NumPyArraySpec::new_i32("arr", vec![10]);
let check = spec.to_contiguous_check("input");
assert!(check.contains("PyArray_ISCONTIGUOUS"));
}
#[test]
fn test_cython_full_pyx() {
let mut def = X86CythonModule::new("my_module");
def.add_function(
"add",
"add_c",
X86CType::Int,
vec![
X86CParam::new("a", X86CType::Int),
X86CParam::new("b", X86CType::Int),
],
);
def.add_cdef_class(
"MyClass",
vec![("x".into(), X86CType::Int), ("y".into(), X86CType::Double)],
);
let pyx = def.to_pyx_file();
assert!(pyx.contains("cdef extern"));
assert!(pyx.contains("def add"));
assert!(pyx.contains("cdef class MyClass"));
}
#[test]
fn test_python_object_lifetime() {
let lifetime = X86PyObjectLifetime::new("obj");
let inc = lifetime.to_incref();
assert!(inc.contains("Py_INCREF"));
let dec = lifetime.to_decref();
assert!(dec.contains("Py_DECREF"));
}
#[test]
fn test_python_exception_translation() {
let xlator = X86PyExceptionTranslator::new();
let exc_map = xlator.add_mapping(
"std::invalid_argument",
"ValueError",
"Invalid argument provided",
);
let code = exc_map.to_catch_block();
assert!(code.contains("PyErr_SetString"));
assert!(code.contains("ValueError"));
}
#[test]
fn test_jni_string_conversion() {
let conv = X86JNIStringConversion::new("java_str", "c_str");
let j_to_c = conv.java_to_c();
assert!(j_to_c.contains("GetStringUTFChars"));
assert!(j_to_c.contains("ReleaseStringUTFChars"));
let c_to_j = conv.c_to_java();
assert!(c_to_j.contains("NewStringUTF"));
}
#[test]
fn test_jni_array_operations() {
let arr = X86JNIArrayOps::new_int_array("arr", 10);
let get = arr.get_elements("env", "arr");
assert!(get.contains("GetIntArrayElements"));
let release = arr.release_elements("env", "arr", "elems", "0");
assert!(release.contains("ReleaseIntArrayElements"));
}
#[test]
fn test_jni_global_ref() {
let gref = X86JNIGlobalRef::new("obj", "com/example/MyClass");
let create = gref.create();
assert!(create.contains("NewGlobalRef"));
let delete = gref.delete();
assert!(delete.contains("DeleteGlobalRef"));
}
#[test]
fn test_jni_exception_checking() {
let check = X86JNIExceptionCheck::new();
let code = check.to_check_block();
assert!(code.contains("ExceptionCheck"));
assert!(code.contains("ExceptionDescribe"));
assert!(code.contains("ExceptionClear"));
}
#[test]
fn test_dotnet_safe_handle() {
let sh = X86DotNetSafeHandle::new("MySafeHandle", "IntPtr");
let cs = sh.to_cs_class();
assert!(cs.contains("SafeHandle"));
assert!(cs.contains("ReleaseHandle"));
assert!(cs.contains("IsInvalid"));
}
#[test]
fn test_dotnet_struct_layout() {
let layout = X86DotNetStructLayout::new("Point", X86DotNetLayoutKind::Sequential);
let fields = vec![
("x".to_string(), X86CType::Int),
("y".to_string(), X86CType::Int),
];
let cs = layout.to_cs_struct(&fields);
assert!(cs.contains("StructLayout"));
assert!(cs.contains("LayoutKind.Sequential"));
assert!(cs.contains("public int x"));
}
#[test]
fn test_dotnet_blittable_check() {
assert!(is_dotnet_blittable(&X86CType::Int));
assert!(is_dotnet_blittable(&X86CType::Double));
assert!(!is_dotnet_blittable(&X86CType::Opaque(
"std::string".into()
)));
}
#[test]
fn test_dotnet_com_interop() {
let com =
X86DotNetComInterop::new("{12345678-1234-1234-1234-123456789ABC}", "IMyInterface");
let binding = com.to_pinvoke_declaration();
assert!(binding.contains("ComImport"));
assert!(binding.contains("Guid"));
}
#[test]
fn test_wasm_component_model() {
let component = X86WASMComponentModel::new("my:component/iface");
let wit = component.to_wit_file();
assert!(wit.contains("interface"));
assert!(wit.contains("my:component/iface"));
}
#[test]
fn test_wasm_memory_model() {
let mem = X86WASMMemoryModel::new(256, Some(512));
let wat = mem.to_wat_memory();
assert!(wat.contains("(memory"));
assert!(wat.contains("256"));
assert!(wat.contains("512"));
assert_eq!(mem.initial_pages, 256);
assert_eq!(mem.max_pages, Some(512));
}
#[test]
fn test_wasm_table_interop() {
let table = X86WASMTable::new(10, Some(100), X86WASMTableType::FuncRef);
let wat = table.to_wat_declaration();
assert!(wat.contains("(table"));
assert!(wat.contains("funcref"));
}
#[test]
fn test_wasm_global_interop() {
let global = X86WASMGlobal::new_i32("counter", 0);
let wat = global.to_wat_declaration();
assert!(wat.contains("(global"));
assert!(wat.contains("counter"));
assert!(wat.contains("i32"));
}
#[test]
fn test_wasi_preview2_stubs() {
let stubs = X86WASIPreview2Stubs::generate_all();
assert!(stubs.contains("wasi:io/streams"));
assert!(stubs.contains("wasi:cli/run"));
assert!(stubs.contains("wasi:filesystem/types"));
}
#[test]
fn test_emscripten_embind() {
let embind = X86EmscriptenEmbind::new("MyModule");
let binding = embind
.add_function("add", "add_c")
.add_param("a", "int")
.add_param("b", "int")
.returns("int")
.build();
assert!(binding.contains("EMSCRIPTEN_BINDINGS"));
assert!(binding.contains("function"));
assert!(binding.contains("add_c"));
}
#[test]
fn test_x86_error_code_enum() {
let ec = X86ErrorCode::new(0, "Success");
assert_eq!(ec.code, 0);
assert_eq!(ec.message, "Success");
assert!(!ec.is_error());
let err = X86ErrorCode::new(-1, "Failure");
assert!(err.is_error());
}
#[test]
fn test_x86_error_registry() {
let mut registry = X86ErrorRegistry::new();
registry.register(0, "OK");
registry.register(-1, "ERROR");
assert_eq!(registry.lookup(0), Some(&"OK".to_string()));
assert_eq!(registry.lookup(-1), Some(&"ERROR".to_string()));
assert_eq!(registry.lookup(99), None);
}
#[test]
fn test_x86_error_translation_rust() {
let reg = X86ErrorRegistry::with_common_errors();
let rs = reg.to_rust_error_enum();
assert!(rs.contains("pub enum FfiError"));
assert!(rs.contains("Success"));
assert!(rs.contains("impl std::fmt::Display"));
assert!(rs.contains("impl std::error::Error"));
}
#[test]
fn test_x86_error_translation_python() {
let reg = X86ErrorRegistry::with_common_errors();
let py = reg.to_python_exception_classes();
assert!(py.contains("class FfiError(Exception)"));
assert!(py.contains("class FfiSuccess(FfiError)"));
}
#[test]
fn test_x86_error_translation_go() {
let reg = X86ErrorRegistry::with_common_errors();
let go = reg.to_go_error_type();
assert!(go.contains("type FfiError int"));
assert!(go.contains("func (e FfiError) Error() string"));
}
#[test]
fn test_thread_safety_annotation() {
let annotation = X86ThreadSafety::new_send_sync("MyStruct");
let rust = annotation.to_rust_impl();
assert!(rust.contains("unsafe impl Send"));
assert!(rust.contains("unsafe impl Sync"));
let c = annotation.to_c_comment();
assert!(c.contains("Thread-safe"));
}
#[test]
fn test_thread_local_storage() {
let tls = X86ThreadLocalStorage::new("my_tls_var", X86CType::Int);
let c_decl = tls.to_c_declaration();
assert!(c_decl.contains("_Thread_local"));
let rust_decl = tls.to_rust_declaration();
assert!(rust_decl.contains("std::cell::RefCell"));
assert!(rust_decl.contains("thread_local!"));
}
#[test]
fn test_debug_info_annotation() {
let di = X86DebugInfoAnnotation::new("my_function", "my_file.c", 100);
let rust = di.to_rust_debug_macro();
assert!(rust.contains("#[debug_handler]"));
let py = di.to_python_decorator();
assert!(py.contains("@ffi_debug"));
}
#[test]
fn test_shared_memory_layout() {
let fields = vec![
("counter".to_string(), X86CType::Int),
("ready".to_string(), X86CType::Bool),
];
let shm = X86SharedMemoryLayout::new("ShmBlock", &fields);
assert_eq!(shm.total_size, 8); let c_code = shm.to_c_header();
assert!(c_code.contains("alignas"));
assert!(c_code.contains("volatile"));
}
#[test]
fn test_atomic_interop() {
let atomic = X86AtomicInterop::new("counter", X86CType::Int);
let c_ops = atomic.to_c_atomic_ops();
assert!(c_ops.contains("atomic_fetch_add"));
assert!(c_ops.contains("atomic_load"));
let rust_ops = atomic.to_rust_atomic_ops();
assert!(rust_ops.contains("AtomicI32"));
assert!(rust_ops.contains("fetch_add"));
}
#[test]
fn test_packed_struct_layout() {
let fields = vec![
("a".to_string(), X86CType::Char),
("b".to_string(), X86CType::Int),
("c".to_string(), X86CType::Char),
];
let layout = X86PackedStructLayout::compute("Packed", &fields);
assert_eq!(layout.total_size, 6); let packed = layout.to_c_definition();
assert!(packed.contains("__attribute__((packed))"));
}
#[test]
fn test_bitfield_layout() {
let fields = vec![
("flag".to_string(), 1u32),
("count".to_string(), 7u32),
("type_id".to_string(), 8u32),
];
let bf = X86BitfieldLayout::compute("Flags", &fields);
assert_eq!(bf.total_bytes, 4);
let c_code = bf.to_c_definition();
assert!(c_code.contains("flag : 1"));
assert!(c_code.contains("count : 7"));
}
#[test]
fn test_varargs_wrapper_pattern() {
let va = X86VarargsWrapper::new("log_msg", X86CType::Void);
let c_code = va.to_c_wrapper();
assert!(c_code.contains("va_list"));
assert!(c_code.contains("va_start"));
assert!(c_code.contains("va_end"));
let rust = va.to_rust_wrapper();
assert!(rust.contains("std::ffi::VaList"));
}
#[test]
fn test_dllexport_annotation() {
let exp = X86ExportAnnotation::new("my_func");
let c_linux = exp.to_linux_visibility();
assert!(c_linux.contains("__attribute__((visibility(\"default\")))"));
let c_win = exp.to_windows_dllexport();
assert!(c_win.contains("__declspec(dllexport)"));
let rust = exp.to_rust_cfg_attr();
assert!(rust.contains("#[cfg_attr(target_os = \"windows\")]"));
}
#[test]
fn test_dllimport_annotation() {
let imp = X86ImportAnnotation::new("external_func");
let c_code = imp.to_windows_dllimport();
assert!(c_code.contains("__declspec(dllimport)"));
}
#[test]
fn test_sysv_amd64_arg_classification() {
let mut classifier = X86SysVArgClassifier::new();
let result = classifier.classify_args(&[
X86CType::Int,
X86CType::Double,
X86CType::Int,
X86CType::Float,
]);
assert_eq!(result.int_regs_used, 2);
assert_eq!(result.sse_regs_used, 2);
assert!(!result.needs_stack);
}
#[test]
fn test_sysv_amd64_struct_hfa() {
let hfa = X86CType::Struct(
"Vec2".into(),
vec![("x".into(), X86CType::Float), ("y".into(), X86CType::Float)],
);
let classifier = X86SysVArgClassifier::new();
let result = classifier.classify_args(&[hfa]);
assert!(result.is_hfa);
assert_eq!(result.sse_regs_used, 1);
}
#[test]
fn test_msx64_shadow_space() {
let shadow = X86MSX64ShadowSpace::default();
assert_eq!(shadow.size, 32); let code = shadow.to_asm_comment();
assert!(code.contains("shadow space"));
}
#[test]
fn test_linux_specific_linker_script() {
let ls = X86PlatformLinker::linux_gnu();
let script = ls.to_linker_script("mylib");
assert!(script.contains("SECTIONS"));
assert!(script.contains(".interp"));
}
#[test]
fn test_macos_specific_two_level_namespace() {
let mac = X86PlatformLinker::macos();
let flags = mac.to_ld_flags();
assert!(flags.contains("-twolevel_namespace"));
}
#[test]
fn test_windows_specific_def_file() {
let win = X86PlatformDef::windows("mylib");
let def = win.to_def_file(&["func1", "func2"]);
assert!(def.contains("LIBRARY"));
assert!(def.contains("EXPORTS"));
assert!(def.contains("func1"));
}
#[test]
fn test_bench_ffi_call_overhead_model() {
let model = X86FfiOverheadModel::new(X86InteropABI::SystemV);
let overhead = model.estimate_direct_call_ns();
assert!(overhead > 0.0 && overhead < 100.0); }
#[test]
fn test_bench_marshal_overhead() {
let model = X86FfiOverheadModel::new(X86InteropABI::SystemV);
let marshal_ns = model.estimate_marshal_overhead_ns(&X86CType::Struct(
"S".into(),
vec![("a".into(), X86CType::Int), ("b".into(), X86CType::Int)],
));
assert!(marshal_ns > 0.0);
}
#[test]
fn test_versioned_struct() {
let vs = X86VersionedStruct::new("Config", 1);
let c_code = vs.to_c_definition();
assert!(c_code.contains("version"));
assert!(c_code.contains("uint32_t"));
let rust = vs.to_rust_definition();
assert!(rust.contains("version: u32"));
}
#[test]
fn test_version_migration() {
let v1 = X86VersionedStruct::new("Config", 1);
let v2 = X86VersionedStruct::new("Config", 2).add_field_v2("timeout_ms", X86CType::Int, 1);
let migration = v1.generate_migration_code(&v2);
assert!(migration.contains("Migration"));
assert!(migration.contains("version"));
}
#[test]
fn test_symbol_version_script() {
let svs = X86SymbolVersionScript::new("libmylib.so");
let script = svs
.add_version("LIB_1.0", &["func1", "func2"])
.add_version("LIB_2.0", &["func3"])
.build();
assert!(script.contains("LIB_1.0"));
assert!(script.contains("LIB_2.0"));
assert!(script.contains("local:"));
assert!(script.contains("*;"));
}
#[test]
fn test_weak_symbol_interop() {
let weak = X86WeakSymbol::new("optional_func", X86CType::Int);
let c_code = weak.to_c_declaration();
assert!(c_code.contains("__attribute__((weak))"));
let rust = weak.to_rust_declaration();
assert!(rust.contains("#[link_name"));
}
#[test]
fn test_multi_language_pipeline() {
let mut interop = X86Interop::new_linux_x86_64();
let mut cxx = X86CXXInterop::new("ComputeEngine::process", "process_c", X86CType::Int);
cxx.add_param("data", X86CType::Pointer(Box::new(X86CType::Char)));
cxx.add_param("len", X86CType::Long);
interop.add_cxx_interop(cxx);
let mut rust = X86RustInterop::new("process", "process_c", X86RustFfiType::CInt);
rust.add_param("data", X86RustFfiType::MutVoidPtr);
rust.add_param("len", X86RustFfiType::CLong);
interop.add_rust_interop(rust);
let mut py = X86PythonInterop::new(
X86PythonBackend::Ctypes,
"engine",
"process",
"process_c",
X86CType::Int,
);
py.add_param("data", X86CType::Pointer(Box::new(X86CType::Char)));
py.add_param("len", X86CType::Long);
interop.add_python_interop(py);
let mut go = X86GoInterop::new("engine", "Process", "process_c", X86CType::Int);
go.add_param("data", X86CType::Pointer(Box::new(X86CType::Char)));
go.add_param("len", X86CType::Long);
interop.add_go_interop(go);
let mut java =
X86JavaInterop::new("com/engine/Engine", "process", "([BJ)Z", X86CType::Bool);
java.add_param("data", X86CType::Pointer(Box::new(X86CType::Char)));
java.add_param("len", X86CType::Long);
interop.add_java_interop(java);
assert_eq!(interop.total_interop_count(), 5);
let all = interop.generate_all_bindings();
assert!(all.contains("process_c"));
}
#[test]
fn test_ctype_roundtrip() {
let test_types = vec![
"void",
"char",
"int",
"long",
"float",
"double",
"_Bool",
"long long",
"unsigned int",
"unsigned long",
];
for t in &test_types {
let parsed = parse_ctype_string(t);
assert!(parsed.is_some(), "Failed to parse '{}'", t);
let back = parsed.unwrap().to_c_decl();
assert!(!back.is_empty(), "Empty string for '{}'", t);
}
}
#[test]
fn test_size_consistency() {
assert_eq!(X86CType::Char.size_x86_64(X86InteropABI::SystemV), 1);
assert_eq!(X86CType::Short.size_x86_64(X86InteropABI::SystemV), 2);
assert_eq!(X86CType::Int.size_x86_64(X86InteropABI::SystemV), 4);
assert_eq!(X86CType::Long.size_x86_64(X86InteropABI::SystemV), 8);
assert_eq!(X86CType::LongLong.size_x86_64(X86InteropABI::SystemV), 8);
assert_eq!(X86CType::Float.size_x86_64(X86InteropABI::SystemV), 4);
assert_eq!(X86CType::Double.size_x86_64(X86InteropABI::SystemV), 8);
assert_eq!(X86CType::LongDouble.size_x86_64(X86InteropABI::SystemV), 16);
}
#[test]
fn test_alignment_consistency() {
assert!(
X86CType::Int.alignment_x86_64() <= X86CType::Int.size_x86_64(X86InteropABI::SystemV)
);
assert!(
X86CType::Long.alignment_x86_64() <= X86CType::Long.size_x86_64(X86InteropABI::SystemV)
);
assert!(
X86CType::Double.alignment_x86_64()
<= X86CType::Double.size_x86_64(X86InteropABI::SystemV)
);
}
#[test]
fn test_c_name_to_rust_snake() {
assert_eq!(c_name_to_rust_snake("MyFunctionName"), "my_function_name");
assert_eq!(c_name_to_rust_snake("already_snake"), "already_snake");
assert_eq!(c_name_to_rust_snake("XMLParser"), "xml_parser");
}
#[test]
fn test_rust_name_to_camel_case() {
assert_eq!(rust_name_to_camel("my_struct"), "MyStruct");
assert_eq!(rust_name_to_camel("xml_parser"), "XmlParser");
assert_eq!(rust_name_to_camel("URL_handler"), "URLHandler");
}
#[test]
fn test_reserved_keyword_escaping() {
assert_eq!(escape_rust_keyword("type"), "r#type");
assert_eq!(escape_rust_keyword("match"), "r#match");
assert_eq!(escape_rust_keyword("normal"), "normal");
assert_eq!(escape_c_keyword("class"), "_class");
assert_eq!(escape_c_keyword("int"), "_int");
assert_eq!(escape_c_keyword("ok"), "ok");
}
#[test]
fn test_abi_compatibility_matrix() {
let matrix = X86ABICompatibilityMatrix::default();
assert!(matrix.is_compatible(X86InteropABI::SystemV, X86InteropABI::SystemV));
assert!(matrix.is_compatible(X86InteropABI::MicrosoftX64, X86InteropABI::MicrosoftX64));
assert!(!matrix.is_compatible(X86InteropABI::SystemV, X86InteropABI::MicrosoftX64));
}
#[test]
fn test_abi_compatibility_warnings() {
let matrix = X86ABICompatibilityMatrix::default();
let warnings =
matrix.compatibility_warnings(X86InteropABI::SystemV, X86InteropABI::MicrosoftX64);
assert!(!warnings.is_empty());
}
#[test]
fn test_generate_markdown_docs() {
let mut interop = X86Interop::new_linux_x86_64();
let cxx = X86CXXInterop::new("foo", "foo_c", X86CType::Int);
interop.add_cxx_interop(cxx);
let docs = generate_interop_markdown_docs(&interop);
assert!(docs.contains("# X86 Interop Documentation"));
assert!(docs.contains("foo_c"));
}
#[test]
fn test_generate_doxygen_comments() {
let interop = X86CXXInterop::new("my_func", "my_func_c", X86CType::Void);
let doxy = to_doxygen_comment(&interop);
assert!(doxy.contains("/**"));
assert!(doxy.contains("@brief"));
assert!(doxy.contains("my_func_c"));
}
#[test]
fn test_memory_ownership_doc_comment() {
assert!(X86MemoryOwnership::CallerAllocated
.to_doc_comment()
.contains("Caller allocates"));
assert!(X86MemoryOwnership::CalleeAllocated
.to_doc_comment()
.contains("Callee allocates"));
assert!(X86MemoryOwnership::Static
.to_doc_comment()
.contains("statically"));
}
#[test]
fn test_memory_ownership_c_annotation() {
let ann = X86MemoryOwnership::CallerAllocated.to_c_annotation("my_func");
assert!(ann.contains("my_func"));
assert!(ann.contains("caller-allocated"));
}
#[test]
fn test_memory_ownership_rust_annotation() {
let ann = X86MemoryOwnership::CalleeAllocated.to_rust_annotation("create");
assert!(ann.contains("Safety"));
assert!(ann.contains("free"));
}
#[test]
fn test_allocator_pair_rust() {
let pair = X86AllocatorPair::new("my_alloc", "my_free");
let rust = pair.to_rust_allocator();
assert!(rust.contains("GlobalAlloc"));
assert!(rust.contains("my_alloc"));
assert!(rust.contains("my_free"));
}
#[test]
fn test_allocator_pair_c_header() {
let pair = X86AllocatorPair::new("custom_malloc", "custom_free");
let hdr = pair.to_c_header();
assert!(hdr.contains("custom_malloc"));
assert!(hdr.contains("custom_free"));
}
#[test]
fn test_resource_handle_rust_drop() {
let handle = X86ResourceHandle::new("MyHandle", "my_handle_destroy");
let rust = handle.to_rust_drop_impl();
assert!(rust.contains("impl Drop"));
assert!(rust.contains("my_handle_destroy"));
assert!(rust.contains("unsafe impl Send"));
}
#[test]
fn test_resource_handle_python_del() {
let handle = X86ResourceHandle::new("PyHandle", "destroy");
let py = handle.to_python_del();
assert!(py.contains("__del__"));
assert!(py.contains("destroy"));
}
#[test]
fn test_resource_handle_go_finalizer() {
let handle = X86ResourceHandle::new("GoHandle", "go_destroy");
let go = handle.to_go_finalizer();
assert!(go.contains("SetFinalizer"));
assert!(go.contains("go_destroy"));
}
#[test]
fn test_type_coercion_table_build() {
let table = X86TypeCoercionTable::build_comprehensive();
assert!(!table.entries.is_empty());
assert!(table.entries.len() >= 15);
}
#[test]
fn test_type_coercion_table_markdown() {
let table = X86TypeCoercionTable::build_comprehensive();
let md = table.to_markdown_table();
assert!(md.contains("| C Type |"));
assert!(md.contains("| int |"));
assert!(md.contains("| double |"));
}
#[test]
fn test_type_coercion_consistency() {
let table = X86TypeCoercionTable::build_comprehensive();
for entry in &table.entries {
if entry.c_type == X86CType::Long {
assert_eq!(entry.size_x86_64, 8);
}
if entry.c_type == X86CType::Int {
assert_eq!(entry.size_x86_64, 4);
assert_eq!(entry.size_i386, 4);
}
}
}
#[test]
fn test_validator_new() {
let v = X86InteropValidator::new();
assert!(v.is_valid());
assert!(v.errors.is_empty());
assert!(v.warnings.is_empty());
}
#[test]
fn test_validator_cxx_noexcept() {
let mut v = X86InteropValidator::new();
let interop = X86CXXInterop::new("foo", "foo_c", X86CType::Int);
v.validate_cxx(&interop);
assert!(!v.warnings.is_empty());
assert!(v.is_valid());
}
#[test]
fn test_validator_cxx_with_noexcept() {
let mut v = X86InteropValidator::new();
let interop = X86CXXInterop::new("foo", "foo_c", X86CType::Int)
.with_exception_spec(X86ExceptionSpec::Noexcept);
v.validate_cxx(&interop);
let has_noexcept_warn = v
.warnings
.iter()
.any(|w| w.contains("exception specification"));
assert!(!has_noexcept_warn);
}
#[test]
fn test_validator_reserved_name() {
let mut v = X86InteropValidator::new();
let interop = X86CXXInterop::new("foo", "_BadName", X86CType::Void)
.with_exception_spec(X86ExceptionSpec::Noexcept);
v.validate_cxx(&interop);
let has_reserved = v.warnings.iter().any(|w| w.contains("reserved"));
assert!(has_reserved);
}
#[test]
fn test_validator_variadic() {
let mut v = X86InteropValidator::new();
let interop = X86CXXInterop::new("printf", "printf_c", X86CType::Int)
.with_variadic(true)
.with_exception_spec(X86ExceptionSpec::Noexcept);
v.validate_cxx(&interop);
let has_variadic = v.warnings.iter().any(|w| w.contains("Variadic"));
assert!(has_variadic);
}
#[test]
fn test_validator_report() {
let mut v = X86InteropValidator::new();
let interop = X86CXXInterop::new("foo", "foo_c", X86CType::Void);
v.validate_cxx(&interop);
let report = v.report();
assert!(report.contains("Validation Report"));
assert!(report.contains("Warnings"));
}
#[test]
fn test_validator_python() {
let mut v = X86InteropValidator::new();
let py = X86PythonInterop::new(X86PythonBackend::CPython, "m", "f", "c_f", X86CType::Void);
v.validate_python(&py);
assert!(v.is_valid());
}
#[test]
fn test_simd_requires_vectorcall() {
assert!(!X86SIMDType::M128.requires_vectorcall());
assert!(X86SIMDType::M256.requires_vectorcall());
assert!(X86SIMDType::M512.requires_vectorcall());
}
#[test]
fn test_simd_numpy_dtype() {
assert_eq!(X86SIMDType::M128.to_numpy_dtype(), "np.float32");
assert_eq!(X86SIMDType::M128d.to_numpy_dtype(), "np.float64");
}
#[test]
fn test_numpy_dtype_to_npy() {
assert_eq!(X86NumPyDtype::Float64.to_npy_type(), "NPY_FLOAT64");
assert_eq!(X86NumPyDtype::Int32.to_npy_type(), "NPY_INT32");
assert_eq!(X86NumPyDtype::Complex128.to_npy_type(), "NPY_COMPLEX128");
}
#[test]
fn test_numpy_array_spec_3d() {
let spec = X86NumPyArraySpec::new_f64("volume", vec![16, 16, 16]);
assert_eq!(spec.ndim, 3);
let code = spec.to_c_creation_code();
assert!(code.contains("16, 16, 16"));
assert!(code.contains("PyArray_SimpleNew"));
}
#[test]
fn test_cython_module_empty() {
let m = X86CythonModule::new("empty");
let pyx = m.to_pyx_file();
assert!(pyx.contains("cython: language_level=3"));
}
#[test]
fn test_cython_module_with_class() {
let mut m = X86CythonModule::new("geom");
m.add_cdef_class(
"Point",
vec![
("x".into(), X86CType::Double),
("y".into(), X86CType::Double),
],
);
let pyx = m.to_pyx_file();
assert!(pyx.contains("cdef class Point"));
assert!(pyx.contains("cdef public double x"));
}
#[test]
fn test_shared_memory_c_header() {
let fields = vec![
("ready".to_string(), X86CType::Bool),
("value".to_string(), X86CType::Int),
];
let shm = X86SharedMemoryLayout::new("Shm", &fields);
let hdr = shm.to_c_header();
assert!(hdr.contains("volatile"));
assert!(hdr.contains("_Static_assert"));
}
#[test]
fn test_atomic_interop_rust_i64() {
let atomic = X86AtomicInterop::new("count", X86CType::Long);
let rust = atomic.to_rust_atomic_ops();
assert!(rust.contains("Atomic"));
}
#[test]
fn test_versioned_struct_c_def() {
let vs = X86VersionedStruct::new("Config", 1).add_field_v2("timeout", X86CType::Int, 2);
let c = vs.to_c_definition();
assert!(c.contains("version"));
assert!(c.contains("timeout"));
}
#[test]
fn test_versioned_struct_rust_def() {
let vs = X86VersionedStruct::new("Config", 1);
let rust = vs.to_rust_definition();
assert!(rust.contains("#[repr(C)]"));
assert!(rust.contains("version: u32"));
}
#[test]
fn test_versioned_migration_code() {
let v1 = X86VersionedStruct::new("Config", 1);
let v2 = X86VersionedStruct::new("Config", 2);
let migration = v1.generate_migration_code(&v2);
assert!(migration.contains("migrate"));
assert!(migration.contains("unimplemented"));
}
#[test]
fn test_symbol_version_script_build() {
let svs = X86SymbolVersionScript::new("libtest.so");
let script = svs
.add_version("LIBTEST_1.0", &["func_a", "func_b"])
.build();
assert!(script.contains("LIBTEST_1.0"));
assert!(script.contains("func_a"));
assert!(script.contains("local:"));
}
#[test]
fn test_weak_symbol_c_declaration() {
let weak = X86WeakSymbol::new("maybe_available", X86CType::Int);
let decl = weak.to_c_declaration();
assert!(decl.contains("__attribute__((weak))"));
}
#[test]
fn test_weak_symbol_rust_declaration() {
let weak = X86WeakSymbol::new("optional", X86CType::Void);
let decl = weak.to_rust_declaration();
assert!(decl.contains("link_name"));
}
#[test]
fn test_markdown_docs_with_content() {
let mut interop = X86Interop::new_linux_x86_64();
let cxx = X86CXXInterop::new("calc", "calc_c", X86CType::Double);
interop.add_cxx_interop(cxx);
let mut rust = X86RustInterop::new("calc", "calc_c", X86RustFfiType::CDouble);
rust.add_param("x", X86RustFfiType::CDouble);
interop.add_rust_interop(rust);
let docs = generate_interop_markdown_docs(&interop);
assert!(docs.contains("# X86 Interop Documentation"));
assert!(docs.contains("calc_c"));
assert!(docs.contains("## Rust FFI"));
}
#[test]
fn test_doxygen_comment_with_params() {
let mut interop = X86CXXInterop::new("calc", "calc_c", X86CType::Int);
interop.add_param("x", X86CType::Double);
interop.add_param("n", X86CType::Int);
let doxy = to_doxygen_comment(&interop);
assert!(doxy.contains("@param x"));
assert!(doxy.contains("@param n"));
assert!(doxy.contains("@return int"));
}
#[test]
fn test_mangling_registry_duplicate() {
let mut reg = X86ManglingRegistry::new();
reg.register("func", "_Z4funcv", "func_c");
reg.register("other", "_Z5otherv", "func_c");
let mangles = reg.mangles_for_c("func_c").unwrap();
assert_eq!(mangles.len(), 2);
}
#[test]
fn test_mangling_registry_empty_lookup() {
let reg = X86ManglingRegistry::new();
assert!(reg.c_name_for_mangled("nonexistent").is_none());
assert!(reg.mangles_for_c("nonexistent").is_none());
assert!(reg.mangle_for("nonexistent").is_none());
assert!(!reg.is_registered_mangled("nonexistent"));
}
#[test]
fn test_ffi_overhead_direct_call() {
let model = X86FfiOverheadModel::new(X86InteropABI::SystemV);
let ns = model.estimate_direct_call_ns();
assert!(ns < 10.0);
let ms = X86FfiOverheadModel::new(X86InteropABI::MicrosoftX64);
assert!(ms.estimate_direct_call_ns() > model.estimate_direct_call_ns());
}
#[test]
fn test_ffi_marshal_overhead_linear() {
let model = X86FfiOverheadModel::new(X86InteropABI::SystemV);
let small = X86CType::Int;
let big = X86CType::Struct(
"Big".into(),
vec![
("a".into(), X86CType::Double),
("b".into(), X86CType::Double),
("c".into(), X86CType::Double),
("d".into(), X86CType::Double),
],
);
assert!(
model.estimate_marshal_overhead_ns(&big) > model.estimate_marshal_overhead_ns(&small)
);
}
#[test]
fn test_jni_constructor_binding() {
let interop = X86JavaInterop::new_constructor("com/example/Data", "(II)V");
assert!(interop.is_constructor);
let binding = interop.to_java_binding();
assert!(binding.contains("Construct object"));
}
#[test]
fn test_jni_static_method() {
let interop = X86JavaInterop::new(
"com/example/MyClass",
"create",
"()Lcom/example/MyClass;",
X86CType::Opaque("jobject".into()),
)
.with_static();
assert!(interop.is_static);
let binding = interop.to_java_binding();
assert!(binding.contains("jclass cls"));
}
#[test]
fn test_dotnet_layout_explicit() {
let layout = X86DotNetStructLayout::new("ExplicitStruct", X86DotNetLayoutKind::Explicit);
let cs = layout.to_cs_struct(&[("x".into(), X86CType::Int)]);
assert!(cs.contains("LayoutKind.Explicit"));
}
#[test]
fn test_dotnet_blittable_all_primitives() {
let types = vec![
X86CType::Char,
X86CType::Int,
X86CType::Long,
X86CType::Float,
X86CType::Double,
X86CType::Bool,
X86CType::Pointer(Box::new(X86CType::Void)),
X86CType::Enum("E".into(), vec![]),
];
for t in &types {
assert!(is_dotnet_blittable(t), "{:?} should be blittable", t);
}
}
}