use anyhow::Result;
use std::process::Command;
pub struct Linker {
output_name: String,
}
impl Linker {
pub fn new(output_name: &str) -> Self {
Self {
output_name: output_name.to_string(),
}
}
pub fn link(&self, object_files: &[&str]) -> Result<Vec<u8>> {
#[cfg(target_os = "windows")]
{
self.link_windows(object_files)
}
#[cfg(target_os = "linux")]
{
self.link_linux(object_files)
}
#[cfg(target_os = "macos")]
{
self.link_macos(object_files)
}
}
#[cfg(target_os = "windows")]
fn link_windows(&self, object_files: &[&str]) -> Result<Vec<u8>> {
let mut link_args = vec![
"/NOLOGO".to_string(),
"/ENTRY:main".to_string(),
"/SUBSYSTEM:CONSOLE".to_string(),
format!("/OUT:{}", self.output_name),
];
for obj in object_files {
link_args.push((*obj).to_string());
}
let output = Command::new("link.exe")
.args(&link_args)
.output()?;
if !output.status.success() {
anyhow::bail!("Link failed: {}", String::from_utf8_lossy(&output.stderr));
}
std::fs::read(&self.output_name)
.map_err(|e| anyhow::anyhow!("Failed to read output: {}", e))
}
#[cfg(target_os = "linux")]
fn link_linux(&self, object_files: &[&str]) -> Result<Vec<u8>> {
let mut ld_args = vec![
"-o".to_string(),
self.output_name.clone(),
];
for obj in object_files {
ld_args.push((*obj).to_string());
}
ld_args.push("-lc".to_string());
let output = Command::new("ld")
.args(&ld_args)
.output()?;
if !output.status.success() {
anyhow::bail!("Link failed: {}", String::from_utf8_lossy(&output.stderr));
}
std::fs::read(&self.output_name)
.map_err(|e| anyhow::anyhow!("Failed to read output: {}", e))
}
#[cfg(target_os = "macos")]
fn link_macos(&self, object_files: &[&str]) -> Result<Vec<u8>> {
let mut ld_args = vec![
"-o".to_string(),
self.output_name.clone(),
];
for obj in object_files {
ld_args.push((*obj).to_string());
}
ld_args.push("-lSystem".to_string());
let output = Command::new("ld")
.args(&ld_args)
.output()?;
if !output.status.success() {
anyhow::bail!("Link failed: {}", String::from_utf8_lossy(&output.stderr));
}
std::fs::read(&self.output_name)
.map_err(|e| anyhow::anyhow!("Failed to read output: {}", e))
}
pub fn link_with_gcc(&self, object_files: &[&str]) -> Result<Vec<u8>> {
let mut gcc_args = vec![
"-o".to_string(),
self.output_name.clone(),
];
for obj in object_files {
gcc_args.push((*obj).to_string());
}
let output = Command::new("gcc")
.args(&gcc_args)
.output()?;
if !output.status.success() {
anyhow::bail!("GCC link failed: {}", String::from_utf8_lossy(&output.stderr));
}
std::fs::read(&self.output_name)
.map_err(|e| anyhow::anyhow!("Failed to read output: {}", e))
}
pub fn link_with_clang(&self, object_files: &[&str]) -> Result<Vec<u8>> {
let clang_path = "C:\\Program Files\\Microsoft Visual Studio\\18\\Community\\VC\\Tools\\Llvm\\x64\\bin\\clang.exe";
let mut clang_args = vec![
"-Wl,/ENTRY:_start".to_string(),
"-o".to_string(),
self.output_name.clone(),
];
for obj in object_files {
clang_args.push((*obj).to_string());
}
clang_args.push("-lkernel32".to_string());
let output = Command::new(clang_path)
.args(&clang_args)
.output()?;
if !output.status.success() {
anyhow::bail!("Clang link failed: {}", String::from_utf8_lossy(&output.stderr));
}
std::fs::read(&self.output_name)
.map_err(|e| anyhow::anyhow!("Failed to read output: {}", e))
}
}
pub fn create_c_runtime() -> &'static str {
r#"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
// NaN-boxing 常量
#define UNDEFINED 0x7FFF800000000001ULL
#define NULL_VAL 0x7FFF800000000002ULL
#define TRUE 0x7FFF000000000001ULL
#define FALSE 0x7FFF000000000000ULL
#define STRING_TAG 0x7FFC000000000000ULL
#define ARRAY_TAG 0x7FFB000000000000ULL
// 字符串结构
typedef struct {
uint32_t len;
uint32_t hash;
char data[];
} JsString;
// 数组结构
typedef struct {
uint32_t len;
uint32_t capacity;
uint64_t data[];
} JsArray;
// 创建字符串
uint64_t js_string_new(const char* s, uint32_t len) {
JsString* str = malloc(sizeof(JsString) + len);
str->len = len;
str->hash = 0;
for (uint32_t i = 0; i < len; i++) {
str->data[i] = s[i];
str->hash = str->hash * 31 + s[i];
}
return STRING_TAG | (uint64_t)str;
}
// 创建数组
uint64_t js_array_new(uint32_t capacity) {
if (capacity == 0) capacity = 8;
JsArray* arr = malloc(sizeof(JsArray) + capacity * sizeof(uint64_t));
arr->len = 0;
arr->capacity = capacity;
return ARRAY_TAG | (uint64_t)arr;
}
// 数组 push
void js_array_push(uint64_t arr_val, uint64_t value) {
JsArray* arr = (JsArray*)(arr_val & 0x0000FFFFFFFFFFFFULL);
if (arr->len >= arr->capacity) {
arr->capacity *= 2;
arr = realloc(arr, sizeof(JsArray) + arr->capacity * sizeof(uint64_t));
}
arr->data[arr->len++] = value;
}
// 打印值
void js_print(uint64_t val) {
if (val == UNDEFINED) {
printf("undefined\n");
} else if (val == NULL_VAL) {
printf("null\n");
} else if (val == TRUE) {
printf("true\n");
} else if (val == FALSE) {
printf("false\n");
} else if ((val >> 48) == (STRING_TAG >> 48)) {
JsString* str = (JsString*)(val & 0x0000FFFFFFFFFFFFULL);
printf("%.*s\n", str->len, str->data);
} else {
double d = *(double*)&val;
printf("%g\n", d);
}
}
// 数学函数
uint64_t js_add(uint64_t a, uint64_t b) {
double da = *(double*)&a;
double db = *(double*)&b;
double result = da + db;
return *(uint64_t*)&result;
}
uint64_t js_sub(uint64_t a, uint64_t b) {
double da = *(double*)&a;
double db = *(double*)&b;
double result = da - db;
return *(uint64_t*)&result;
}
uint64_t js_mul(uint64_t a, uint64_t b) {
double da = *(double*)&a;
double db = *(double*)&b;
double result = da * db;
return *(uint64_t*)&result;
}
uint64_t js_div(uint64_t a, uint64_t b) {
double da = *(double*)&a;
double db = *(double*)&b;
double result = da / db;
return *(uint64_t*)&result;
}
"#
}