use lamina_platform::TargetOperatingSystem;
use std::collections::HashMap;
use std::env;
pub struct BuiltinLibrary {
target_os: TargetOperatingSystem,
overrides: HashMap<String, String>,
}
impl BuiltinLibrary {
pub fn from_env(target_os: TargetOperatingSystem) -> Self {
let overrides = builtin_overrides_from_env();
Self {
target_os,
overrides,
}
}
pub fn resolve(&self, name: &str) -> Option<String> {
self.overrides
.get(name)
.cloned()
.or_else(|| self.default_symbol(name))
}
fn default_symbol(&self, name: &str) -> Option<String> {
match name {
"print" => Some(get_printf_symbol(self.target_os).to_string()),
"malloc" => Some(get_malloc_symbol(self.target_os).to_string()),
"dealloc" => Some(get_free_symbol(self.target_os).to_string()),
_ => None,
}
}
}
fn builtin_overrides_from_env() -> HashMap<String, String> {
let mut overrides = HashMap::new();
if let Ok(value) = env::var("LAMINA_BUILTINS") {
for entry in value.split(',') {
let mut parts = entry.splitn(2, '=');
let name = parts.next().unwrap_or("").trim();
let symbol = parts.next().unwrap_or("").trim();
if !name.is_empty() && !symbol.is_empty() {
overrides.insert(name.to_string(), symbol.to_string());
}
}
}
overrides
}
pub trait Abi {
fn target_os(&self) -> TargetOperatingSystem;
fn mangle_function_name(&self, name: &str) -> String;
fn builtin_library(&self) -> BuiltinLibrary {
BuiltinLibrary::from_env(self.target_os())
}
fn call_stub(&self, name: &str) -> Option<String> {
self.builtin_library().resolve(name)
}
}
pub fn mangle_macos_name(name: &str) -> String {
if name == "main" {
"_main".to_string()
} else {
format!("_{}", name)
}
}
pub fn get_printf_symbol(target_os: TargetOperatingSystem) -> &'static str {
match target_os {
TargetOperatingSystem::MacOS => "_printf",
_ => "printf",
}
}
pub fn get_malloc_symbol(target_os: TargetOperatingSystem) -> &'static str {
match target_os {
TargetOperatingSystem::MacOS => "_malloc",
_ => "malloc",
}
}
pub fn get_free_symbol(target_os: TargetOperatingSystem) -> &'static str {
match target_os {
TargetOperatingSystem::MacOS => "_free",
_ => "free",
}
}
pub fn common_call_stub(name: &str, target_os: TargetOperatingSystem) -> Option<String> {
BuiltinLibrary::from_env(target_os).resolve(name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mangle_macos_name() {
assert_eq!(mangle_macos_name("main"), "_main");
assert_eq!(mangle_macos_name("foo"), "_foo");
assert_eq!(mangle_macos_name("my_function"), "_my_function");
}
#[test]
fn test_get_printf_symbol() {
assert_eq!(get_printf_symbol(TargetOperatingSystem::MacOS), "_printf");
assert_eq!(get_printf_symbol(TargetOperatingSystem::Linux), "printf");
assert_eq!(get_printf_symbol(TargetOperatingSystem::Windows), "printf");
}
#[test]
fn test_get_malloc_symbol() {
assert_eq!(get_malloc_symbol(TargetOperatingSystem::MacOS), "_malloc");
assert_eq!(get_malloc_symbol(TargetOperatingSystem::Linux), "malloc");
assert_eq!(get_malloc_symbol(TargetOperatingSystem::Windows), "malloc");
}
#[test]
fn test_get_free_symbol() {
assert_eq!(get_free_symbol(TargetOperatingSystem::MacOS), "_free");
assert_eq!(get_free_symbol(TargetOperatingSystem::Linux), "free");
assert_eq!(get_free_symbol(TargetOperatingSystem::Windows), "free");
}
#[test]
fn test_builtin_library_default_symbols() {
let lib_macos = BuiltinLibrary::from_env(TargetOperatingSystem::MacOS);
assert_eq!(lib_macos.resolve("print"), Some("_printf".to_string()));
assert_eq!(lib_macos.resolve("malloc"), Some("_malloc".to_string()));
assert_eq!(lib_macos.resolve("dealloc"), Some("_free".to_string()));
let lib_linux = BuiltinLibrary::from_env(TargetOperatingSystem::Linux);
assert_eq!(lib_linux.resolve("print"), Some("printf".to_string()));
assert_eq!(lib_linux.resolve("malloc"), Some("malloc".to_string()));
assert_eq!(lib_linux.resolve("dealloc"), Some("free".to_string()));
}
#[test]
fn test_builtin_library_unknown_symbol() {
let lib = BuiltinLibrary::from_env(TargetOperatingSystem::Linux);
assert_eq!(lib.resolve("unknown_function"), None);
}
#[test]
fn test_common_call_stub() {
assert_eq!(
common_call_stub("print", TargetOperatingSystem::MacOS),
Some("_printf".to_string())
);
assert_eq!(
common_call_stub("print", TargetOperatingSystem::Linux),
Some("printf".to_string())
);
assert_eq!(
common_call_stub("malloc", TargetOperatingSystem::MacOS),
Some("_malloc".to_string())
);
assert_eq!(
common_call_stub("dealloc", TargetOperatingSystem::Linux),
Some("free".to_string())
);
assert_eq!(
common_call_stub("unknown", TargetOperatingSystem::Linux),
None
);
}
}