ts-native 0.1.2

A TypeScript to native executable compiler using Cranelift
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;
}
"#
}