# ts-native 架构设计

## 编译流程

```
┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│  TypeScript   │───▶│  SWC Parser  │───▶│  HIR 生成    │───▶│  Cranelift   │───▶│  COFF 生成   │
│  源码         │    │  (swc.rs)    │    │ (swc_transform│    │  代码生成    │    │  (coff.rs)   │
└──────────────┘    └──────────────┘    │    .rs)      │    │ (codegen.rs) │    └──────┬───────┘
                                        └──────────────┘    └──────────────┘           │
                                                                                    ▼
                                        ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
                                        │  可执行文件   │◀───│  链接        │◀───│  Runtime     │
                                        │  (.exe)      │    │(standalone   │    │  (runtime/)  │
                                        └──────────────┘    │  _gen.rs)    │    └──────────────┘
                                                            └──────────────┘
```

## 模块职责

### 核心编译管线

| 模块 | 职责 | 关键数据结构 |
|------|------|-------------|
| `swc_transform` | SWC AST → HIR 转换（含 CJS→HIR） | `HirExpr` 枚举 |
| `codegen` | HIR → Cranelift IR → 机器码 | `CodeGen`, `CompileStats` |
| `coff` | 机器码 → COFF 目标文件 | `CoffObject`, `Section` |
| `standalone_gen` | COFF → 独立可执行文件（含 addon DLL 加载） | `StandaloneGenerator`, `ModuleData` |

### 项目管理

| 模块 | 职责 | 关键数据结构 |
|------|------|-------------|
| `project` | 项目发现、模块解析、shim/addon/js-engine 配置 | `Project`, `ProjectConfig`, `SourceFile` |
| `dep_graph` | 依赖图构建、拓扑排序（含 shim 标记） | `DepGraph`, `DepNode`, `ModuleNode` |
| `project_compiler` | 项目级编译、报告生成 | `ProjectCompiler`, `CompileReport` |

### 接口层

| 模块 | 职责 |
|------|------|
| `tsn_ffi` | FFI 导出接口（供外部 loader 调用，含 addon DLL 信息） |
| `native_linker` | 原生 PE 链接器（feature gate，实验性） |

## HIR（中间表示）

HIR 是 ts-native 的核心数据结构，定义在 `codegen.rs` 中：

```rust
pub enum HirExpr {
    // 字面量
    Number(f64),
    String(String),
    Boolean(bool),
    Null,
    Undefined,
    Array(Vec<HirExpr>),
    Object(Vec<(String, HirExpr)>),

    // 表达式
    Identifier(String),
    BinaryOp { op: BinOp, left: Box<HirExpr>, right: Box<HirExpr> },
    UnaryOp { op: UnaryOp, operand: Box<HirExpr> },
    Call { callee: Box<HirExpr>, args: Vec<HirExpr> },
    MemberAccess { object: Box<HirExpr>, property: String },
    IndexAccess { object: Box<HirExpr>, index: Box<HirExpr> },
    Assignment { target: Box<HirExpr>, value: Box<HirExpr> },
    CompoundAssignment { op: BinOp, target: Box<HirExpr>, value: Box<HirExpr> },
    TemplateString(Vec<HirExpr>),
    Ternary { condition: Box<HirExpr>, then_expr: Box<HirExpr>, else_expr: Box<HirExpr> },

    // 语句
    Function { name: String, params: Vec<String>, body: Vec<HirExpr> },
    Return(Box<HirExpr>),
    If { condition: Box<HirExpr>, then_body: Vec<HirExpr>, else_body: Vec<HirExpr> },
    While { condition: Box<HirExpr>, body: Vec<HirExpr> },
    For { init: Option<Box<HirExpr>>, condition: Option<Box<HirExpr>>, update: Option<Box<HirExpr>>, body: Vec<HirExpr> },
    ForOf { variable: String, iterable: Box<HirExpr>, body: Vec<HirExpr> },
    Break,
    VarDecl { name: String, mutable: bool, init: Option<Box<HirExpr>> },
    ExprStmt(Box<HirExpr>),
    Block(Vec<HirExpr>),

    // 模块
    ImportBinding { local: String, module: String, imported: String },
    Export(Box<HirExpr>),
}
```

## NaN-boxing 值表示

ts-native 使用 NaN-boxing 在 64 位空间中统一表示所有 JavaScript 值：

```
┌─────────────────────────────────────────────────────────┐
│  IEEE 754 Double (64-bit)                               │
├─────────────────────────────────────────────────────────┤
│  Tagged Pointer (非 NaN 时为 Number)                    │
│  ┌──────────┬──────────────────────────────────────────┐│
│  │ Tag (4b) │ Payload (48-bit pointer or value)        ││
│  └──────────┴──────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘
```

| Tag | 类型 | Payload |
|-----|------|---------|
| `0x7FF1` | String | 堆指针 |
| `0x7FF2` | Object | 堆指针 |
| `0x7FF7` | Array | 堆指针 |
| `0x7FF6` | Function | 函数指针/索引 |
| `0x7FF3` | Boolean | 0/1 |
| `0x7FF4` | Undefined | — |
| `0x7FF5` | Null | — |
| 其他 | Number | IEEE 754 double |

## 模块解析

### 完整解析优先级链

```
import { foo } from "bar"
        │
        ▼
resolve_import(from, "bar")
        │
        ├─ 1. 相对路径（./ 或 ../）→ 基于源文件目录解析
        │
        ├─ 2. 用户模块 → src/<name>.ts 或 src/<name>/index.ts
        │
        ├─ 3. Shim → ts-native.toml [shims] 配置
        │     返回 __shim__/<name> 虚拟路径，标记为 shim 模块
        │
        ├─ 4. node_modules → 向上查找 node_modules/<name>/package.json
        │     解析 exports > main > index.js > index.ts
        │     支持 .ts / .js / .mjs 文件
        │
        └─ 5. 内置模块 → 返回 None，由 codegen 生成 js_xxx runtime 调用
```

### Shim 机制

Shim 为常用 npm 包提供原生替代实现，在编译期替换为等价的 runtime 函数调用。

**配置**（`ts-native.toml`）：

```toml
[shims]
lodash = "ts_native_shim_lodash"
dayjs = "ts_native_shim_dayjs"
uuid = "ts_native_shim_uuid"
```

**编译流程**：

```
import { debounce } from "lodash"
        │
        ▼
resolve_import() 检查 shims 配置 → 找到 lodash
        │
        ▼
路径设为 __shim__/lodash，标记 is_shim = true
        │
        ▼
codegen 检测 __shim__/ 前缀
        │
        ▼
生成外部函数调用: ts_native_shim_lodash_debounce(...)
```

**原则**：优先使用 Rust 生态成熟 crate（如 uuid、chrono），而非手写实现。

### node_modules 解析

支持解析 npm 包的 `node_modules` 目录，将纯 JS 包源码纳入编译队列。

```rust
fn resolve_node_modules(from: &Path, import_path: &str) -> Option<PathBuf> {
    // 从 from 文件向上查找 node_modules/
    let mut dir = from.parent()?;
    loop {
        let nm = dir.join("node_modules").join(import_path);
        if let Some(entry) = self.resolve_package_entry(&nm) {
            return Some(entry);
        }
        dir = dir.parent()?;
        if dir == self.root || dir == Path::new("/") {
            break;
        }
    }
    None
}
```

**package.json 解析优先级**：`exports` > `main` > `index.js` > `index.ts`

### CJS → HIR 转换

`swc_transform.rs` 将 CommonJS 模块语法转换为 HIR：

| CJS 语法 | HIR 转换 |
|----------|---------|
| `const x = require("mod")` | `VarDecl { init: ImportBinding { module: "mod" } }` |
| `module.exports = fn` | `Function { name: "default" }` |
| `exports.foo = fn` | `Function { name: "foo" }` |

## 函数调用解析优先级

当编译 `foo(args)` 时，按以下优先级解析：

1. **import_bindings（Shim）** — 如果 `foo` 来自 `__shim__/` 模块，生成 `{shim_prefix}_{fn}` 外部调用
2. **import_bindings（Addon）** — 如果 `foo` 来自 native addon 配置，生成 `addon_{pkg}_{fn}` 外部调用
3. **import_bindings（用户模块）** — 如果 `foo` 是从其他模块导入的，转为 `module.foo(args)`
4. **module_symbols** — 如果 `foo` 在其他模块中定义，声明为外部函数
5. **function_ids** — 如果 `foo` 是本模块函数，直接调用
6. **硬编码匹配** — `console.log`, `Math.sin`, `parseInt` 等
7. **QuickJS fallback** — 如果模块在 `[js-engine] fallback` 列表中，生成 `js_qjs_call(mod, fn, args)`
8. **通用回退** — `to_runtime_fn_name("foo")` → `js_foo`，声明为 runtime 导入

## FFI 桥接（Native Addon DLL）

支持调用 C/C++ 编写的 Node.js native addon DLL。

**配置**（`ts-native.toml`）：

```toml
[native_addons.myaddon]
dll = "myaddon.dll"

[native_addons.myaddon.functions]
process = "native_process"
transform = "native_transform"
```

**运行时加载流程**：

```
编译时：
  import { process } from "myaddon"
  → declare_function("addon_myaddon_process", Linkage::Import)

运行时（standalone_gen.rs）：
  1. LoadLibrary("tsn_runtime.dll")
  2. LoadLibrary("myaddon.dll")  // 搜索: exe 目录 → 当前目录
  3. GetProcAddress("addon_myaddon_process")
  4. 符号解析: addon_ 前缀 → addon DLL，js_/__ → runtime DLL
```

## QuickJS 嵌入（JS 引擎兜底）

当 npm 包无法原生编译时，通过嵌入的 QuickJS 引擎执行其 JS 源码。

**配置**（`ts-native.toml`）：

```toml
[js-engine]
enabled = true
fallback = ["express", "axios", "koa"]
```

**调用流程**：

```
import { get } from "axios"
        │
        ▼
编译期：检测到 axios 在 fallback 列表中
        │
        ▼
生成代码：js_qjs_call("axios", "get", args_array)
        │
        ▼
运行时：
  1. QuickJS 加载 axios 的 JS 源码
  2. 在 QuickJS 中执行 axios.get(url, config)
  3. 结果转换：QuickJS 值 ↔ NaN-tagged f64
```

**Runtime API**（`#[cfg(feature = "quickjs")]`）：

| 函数 | 说明 |
|------|------|
| `js_qjs_init()` | 初始化 QuickJS 引擎 |
| `js_qjs_eval(code)` | 执行 JS 代码字符串 |
| `js_qjs_call(mod, fn, args)` | 调用指定模块的函数 |
| `js_qjs_is_available()` | 检查 QuickJS 是否可用 |

## 依赖图

`dep_graph.rs` 负责构建模块间依赖关系：

1. 遍历所有源文件，解析 import/export
2. Shim 路径（`__shim__/`）记录在 import_map 中，但不加入依赖图
3. 构建依赖关系图
4. Kahn 算法拓扑排序，确定编译顺序
5. 计算每个入口的依赖闭包

## 可观测性

`project_compiler.rs` 提供编译报告：

| 指标 | 说明 |
|------|------|
| `total_duration_ms` | 总编译耗时 |
| `memory_bytes` | 每个阶段的内存使用（Windows RSS） |
| `functions_compiled/total` | 函数覆盖率 |
| `total_code_size` | 代码段总大小 |
| `total_data_size` | 数据段总大小 |
| `phase_timings` | 各阶段耗时和内存 |

## Feature Flags

| Feature | 说明 |
|---------|------|
| `native-linker` | 启用 `native_linker` 模块（原生 PE 链接器） |
| `coff` | 启用 COFF 相关功能 |
| `quickjs` | 启用 QuickJS 嵌入引擎（runtime crate） |

## Runtime

运行时以 Rust 实现（`runtime/` crate，编译为 cdylib → `tsn_runtime.dll`），提供：

### 核心 FFI 函数

- 字符串操作：创建、拼接、比较、切片
- 数组操作：创建、扩容、索引读写
- 对象操作：创建、属性读写
- 类型转换：toString, toNumber, toBoolean
- I/O：console.log（通过 `js_print` 系列函数）
- 数学运算：Math 函数

### Native Shim 函数

为常用 npm 包提供原生替代实现，遵循 `pub extern "C" fn {prefix}_{fn}(...) -> f64` 签名。

| 包 | Shim 前缀 | 实现方式 | 函数数量 |
|----|----------|---------|---------|
| lodash | `ts_native_shim_lodash_` | 纯 Rust 实现 | 45 |
| dayjs | `ts_native_shim_dayjs_` | chrono crate 包装 | 2 |
| uuid | `ts_native_shim_uuid_` | uuid crate 包装 | 2 |
| clsx | `ts_native_shim_clsx_` | 纯 Rust 实现 | 1 |
| chalk | `ts_native_shim_chalk_` | 纯 Rust 实现 | 7 |

**原则**：优先使用 Rust 生态成熟 crate（uuid→uuid crate，dayjs→chrono crate），而非手写实现。

### QuickJS 引擎（可选）

通过 `#[cfg(feature = "quickjs")]` 条件编译，零开销（未启用时不编译、不链接）。

Runtime 通过 FFI 与编译后的代码交互，所有参数和返回值使用 NaN-boxed 64 位值。

---

## Event Loop + Promise 系统（已实现）

### 当前实现状态

```
async function main() {
    let result = await somePromise();
    console.log(result);
}
main();

执行流程：
┌──────────────────────────────────────────────────┐
│ main()                                            │
│   → asyncFunc(ctx_handle, args)                   │
│       → 状态机执行段落 0                           │
│       → 遇到 await → js_schedule(ctx, 下一状态, 0) │
│       → return_ (函数返回，让出控制权)              │
│   → js_run_event_loop()                            │
│       → js_tick() 检测到调度的上下文                │
│       → 通过 func_ptr 重新调用 asyncFunc           │
│       → 状态机进入段落 1                           │
│       → ...                                        │
│       → 最后一段返回最终值                         │
└──────────────────────────────────────────────────┘
```

### 复用现有基础设施

```
现有组件                     实际使用
─────────────               ──────────────────
js_async_ctx_new            创建 async 上下文
js_async_ctx_get_state      状态机 resume 时获取状态
js_async_ctx_set_state      await 前设置下一状态
js_async_ctx_get_local      跨 await 变量恢复
js_async_ctx_set_local      跨 await 变量保存
js_async_ctx_get_result     获取 Promise resolve 值
js_async_ctx_set_result     保存 await 表达式结果
js_async_ctx_set_func       存储函数指针（事件循环可使用 func_ptr 恢复调用）
compile_async_function      状态机不变
split_body                  保持 await 分割策略
```

### 已实现的 Runtime 组件

| 数据集 | 类型 | 说明 |
|--------|------|------|
| `ASYNC_CTX_POOL` | `Cell<Vec<AsyncContext>>` | async 上下文池（含 state/locals/result/func_ptr） |
| `PROMISE_POOL` | `Cell<Vec<PromiseSlot>>` | Promise 池（resolved 标志 + value） |
| `SCHEDULED_CTXS` | `Cell<Vec<(f64,f64,f64)>>` | 待恢复上下文队列（ctx_handle, state, ready_at_ms） |
| `TIMER_POOL` | `Cell<Vec<(f64,f64,bool)>>` | setTimeout 定时器池（callback, ready_at, cancelled） |

### 已实现的 Runtime 函数

| 函数 | 说明 |
|------|------|
| `js_promise_new()` | 创建 Promise，返回 promise_id |
| `js_promise_resolve(promise_id, value)` | 解析 Promise |
| `js_schedule(ctx_handle, state, delay_ms)` | 调度 async 上下文恢复 |
| `js_async_ctx_set_func(ctx_handle, func_ptr)` | 存储函数指针（codegen 在入口调用） |
| `js_tick()` → bool | 事件循环单步：检查 SCHEDULED_CTXS + TIMER_POOL |
| `js_run_event_loop()` | 循环 js_tick() 直到无 pending 任务 |
| `js_set_timeout(callback, ms)` → id | 定时执行回调 |
| `js_clear_timeout(id)` | 取消定时器 |
| `js_await(promise)` → promise_id | await 表达式（返回 promise_id，状态机负责调度） |

### Codegen 修改

| 位置 | 修改 |
|------|------|
| `compile_async_function` 入口 | 调用 `js_async_ctx_set_func(ctx, func_ptr)` 存函数指针 |
| `compile_async_function` 非末段 | 改为 `js_schedule` + `return_`（不再 `jump dispatch`） |
| `compile_function` 末尾 | 若函数名为 `main`/`default`，添加 `js_run_event_loop()` |
| 内置函数映射 | 添加 `setTimeout` → `js_set_timeout`，`clearTimeout` → `js_clear_timeout` |

### 实现说明

- **async 函数调用检测**：codegen 通过 `expected_params == args.len() + 1` 判断是否为 async 函数
- **状态机让出**：非末段不再跳回 dispatch，改为 `js_schedule` + `return_`
- **事件循环恢复**：`js_tick` 通过 `SCHEDULED_CTXS` 找到待恢复上下文，调用 `func_ptr` 重新进入状态机
- **setTimeout**：根据 `TAG_FUNCTION` 识别函数值，提取地址调用（`extern "C" fn() -> f64`）
- **`js_await`**：当前返回 promise_id（占位），状态机通过 `js_schedule` 实现暂停/恢复

---

## 架构设计方案：GC / 内存管理（待实现）

### 现状

- 三个全局池：`STRING_POOL`（`Vec<String>`）、`ARRAY_POOL`（`Vec<JsArray>`）、`OBJECT_POOL`（`Vec<JsObject>`）
- 池**只增不减**，从不释放
- 使用 `static mut` 全局变量（unsafe）
- NaN-tagged 值的 payload 是 pool index：`TAG | idx`

### 设计目标

1. 复用现有 pool 架构，不引入 tracing GC
2. 支持引用计数（RC）实现自动回收
3. 保持 NaN-tagging 的兼容性
4. 最小化对现有 codegen 的修改

### 复用现有基础设施

```
现有组件                    设计方案
─────────────              ──────────────────
STRING_POOL                保留，改为 Vec<(String, RefCount)>
ARRAY_POOL                 保留，改为 Vec<(JsArray, RefCount)>
OBJECT_POOL                保留，改为 Vec<(JsObject, RefCount)>
TAG_STRING / TAG_OBJECT    保持 NaN-tagging 不变
store_string/array/object   改为返回 (idx, ref_count = 1)
get_string/array/object     读访问不变
```

### 引用计数（Reference Counting）

```
分配时：ref_count = 1
  obj = store_object(new_obj)  // ref_count = 1

赋值时：ref_count++
  let b = a;          // a 指向的对象的 ref_count++
  obj.x = val;        // val 指向的对象的 ref_count++

函数传参：ref_count++（调用时），ref_count--（返回时）
  foo(val);           // 进入函数时 val.ref_count++
                       // 函数返回时 val.ref_count--

作用域退出：ref_count--
  { let x = obj; }    // x 离开作用域，obj.ref_count--

ref_count == 0 时：回收
  pool 中 slot 标记为 free（free-list）
  新分配优先使用 free list
```

### 新增 Runtime 函数

```rust
fn retain(idx: usize, pool_tag: u64)  // ref_count++
fn release(idx: usize, pool_tag: u64)  // ref_count--，为 0 时回收
```

### Codegen 修改

1. **变量作用域管理**：在变量赋值和离开作用域时插入 `retain`/`release` 调用
2. **函数调用**：参数传递时 `retain`，返回时 `release`
3. **属性设置**：`js_object_set` 内部对旧值 `release`，对新值 `retain`
4. **数组操作**：`js_array_push` 对元素 `retain`，`js_array_pop` 对元素 `release`
5. **临时值**：表达式临时值在语句结束后 `release`

### Pool 实现（free-list + 压缩）

```rust
struct PoolSlot<T> {
    value: T,
    ref_count: u32,
    is_free: bool,
}

struct Pool<T> {
    slots: Vec<PoolSlot<T>>,
    free_list: Vec<usize>,  // 回收的空闲 slot 索引
}

impl<T> Pool<T> {
    fn alloc(&mut self, value: T) -> (usize, u32) {
        if let Some(idx) = self.free_list.pop() {
            self.slots[idx] = PoolSlot { value, ref_count: 1, is_free: false };
            (idx, 1)
        } else {
            let idx = self.slots.len();
            self.slots.push(PoolSlot { value, ref_count: 1, is_free: false });
            (idx, 1)
        }
    }
    
    fn release(&mut self, idx: usize) {
        self.slots[idx].ref_count -= 1;
        if self.slots[idx].ref_count == 0 {
            self.slots[idx].is_free = true;
            self.free_list.push(idx);
        }
    }
}
```

### 实现步骤（当前状态）

| 步骤 | 状态 | 说明 |
|------|------|------|
| 1. 替换 pool 实现 | ✅ 已完成 | `Vec<T>` → `Pool<T>`（带 RC + free-list）|
| 2. 添加 retain/release FFI | ✅ 已完成 | `js_retain`/`js_release` C ABI 函数（按 tag 分发）|
| 3a. Codegen: 函数退出释放 | ✅ 已完成 | 函数隐式返回和显式 return 前释放所有局部变量 |
| 3b. Codegen: 变量赋值释放旧值 | ✅ 已完成 | `a = newVal` 时 `js_release(oldVal)` |
| 3c. Codegen: 变量引用 retain | ✅ 已完成 | `let a = b` 时 `js_retain(b)` |
| 3d. Codegen: 函数传参 retain/release | ⬜ 待实现 | 调用方 retain 参数，返回后 release |
| 3e. Runtime: 数组/对象内置 retain | ✅ 已完成 | `js_array_push`/`js_object_set` 内部已有 |
| 4. 循环引用处理 | ⬜ 暂不支持 | RC 无法处理，后续考虑 weak ref |

### Codegen GC 实现细节

**函数退出释放**（`compile_function` + async/generator 末段）：
- 隐式 return：在返回值前对所有非参数变量调用 `js_release`
- 显式 `return x`：跳过被返回的变量（所有权转移），释放其他局部变量
- async 末段：在 `return undefined` 前调用 `emit_release_locals`
- generator 末段：通过 `call_import_direct` 遍历 `seg_vars` 释放

**变量赋值**（`compile_stmt` + `compile_expr` 的 Assign/CompoundAssign）：
- `a = val`：先 `js_release(old_val)`，再 `builder.def_var(var, val)`
- `let a = b`（从变量初始化）：`js_retain(b)`（两个变量共享引用，跳过 `__` 前缀的内部变量）

**辅助函数**：
- `emit_release_var(ctx, builder, var)` — 对单个 Variable 调用 `js_release`
- `emit_release_locals(ctx, builder, skip_name)` — 释放所有非参数变量（可选跳过返回值）

### 不支持的场景（第一版）

- **循环引用**：`a.x = a` 会导致内存泄漏（RC 无法处理）
- **跨线程**：单线程无需考虑并发
- **Weak 引用**：不实现 `Weak<T>`，保持简单
- **压缩**：free list 可能导致碎片，但 pool 规模不大（典型程序 < 1000 对象）

### 预期效果

| 场景 | 当前 | 实现后 |
|------|------|--------|
| 创建对象后释放 | 内存泄漏 | 正确回收 |
| 函数内创建临时数组 | 内存泄漏 | 函数返回时释放 |
| 循环中创建字符串 | 内存持续增长 | 每次迭代释放 |
| 对象属性覆盖 | 旧值泄漏 | 旧值 release |
