feat: プラグインパスをOS非依存に更新(.so拡張子削除)
- nyash.tomlからすべての.so拡張子を削除 - plugin_loader_v2のresolve_library_pathが自動的に適切な拡張子を追加 - Linux: .so - Windows: .dll - macOS: .dylib - クロスプラットフォーム対応の準備完了
This commit is contained in:
@ -141,13 +141,15 @@ python3 -m http.server 8010
|
|||||||
- **Rust→WASM**: Nyashインタープリター自体をブラウザで動かす(フル機能)
|
- **Rust→WASM**: Nyashインタープリター自体をブラウザで動かす(フル機能)
|
||||||
- **Nyash→WASM**: Nyashプログラムを単体WASMに変換(限定機能)
|
- **Nyash→WASM**: Nyashプログラムを単体WASMに変換(限定機能)
|
||||||
|
|
||||||
#### 3️⃣ **Nyash→AOT/Native(将来実装予定)**
|
#### 3️⃣ **Nyash→AOT/Native(Cranelift必要)**
|
||||||
```bash
|
```bash
|
||||||
# NyashコードをネイティブバイナリにAOTコンパイル(現在開発中)
|
# NyashコードをネイティブバイナリにAOTコンパイル(現在開発中)
|
||||||
./target/release/nyash --compile-native program.nyash -o program.exe
|
cargo build --release --features cranelift-jit
|
||||||
|
./target/release/nyash --backend vm --compile-native program.nyash -o program.exe
|
||||||
# または
|
# または
|
||||||
./target/release/nyash --aot program.nyash -o program.exe
|
./target/release/nyash --aot program.nyash -o program.exe
|
||||||
```
|
```
|
||||||
|
Note: --compile-native は Cranelift JIT を必要とします(`--features cranelift-jit`)。
|
||||||
|
|
||||||
### 🔧 JIT-direct(独立JIT)運用メモ(最小)
|
### 🔧 JIT-direct(独立JIT)運用メモ(最小)
|
||||||
- 方針: 当面は read-only(書き込み命令はjit-directで拒否)
|
- 方針: 当面は read-only(書き込み命令はjit-directで拒否)
|
||||||
|
|||||||
22
README.ja.md
22
README.ja.md
@ -26,10 +26,10 @@
|
|||||||
**2025年8月29日** - 誕生からわずか20日で、Nyashがネイティブ実行ファイルへのコンパイルを実現!
|
**2025年8月29日** - 誕生からわずか20日で、Nyashがネイティブ実行ファイルへのコンパイルを実現!
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Nyashソースからネイティブバイナリへ
|
# Nyashソースからネイティブバイナリへ(Craneliftが必要)
|
||||||
./target/release/nyash --backend vm program.nyash # JITコンパイル
|
cargo build --release --features cranelift-jit
|
||||||
./tools/build_aot.sh program.nyash -o app # ネイティブEXE
|
./tools/build_aot.sh program.nyash -o app # ネイティブEXE
|
||||||
./app # スタンドアロン実行!
|
./app # スタンドアロン実行!
|
||||||
```
|
```
|
||||||
|
|
||||||
**20日間で達成したこと:**
|
**20日間で達成したこと:**
|
||||||
@ -113,6 +113,9 @@ NYASH_JIT_EXEC=1 ./target/release/nyash --backend vm program.nyash
|
|||||||
|
|
||||||
### 4. **ネイティブバイナリ** (配布用)
|
### 4. **ネイティブバイナリ** (配布用)
|
||||||
```bash
|
```bash
|
||||||
|
# 事前ビルド(Cranelift)
|
||||||
|
cargo build --release --features cranelift-jit
|
||||||
|
|
||||||
./tools/build_aot.sh program.nyash -o myapp
|
./tools/build_aot.sh program.nyash -o myapp
|
||||||
./myapp # スタンドアロン実行!
|
./myapp # スタンドアロン実行!
|
||||||
```
|
```
|
||||||
@ -120,8 +123,14 @@ NYASH_JIT_EXEC=1 ./target/release/nyash --backend vm program.nyash
|
|||||||
- 最高性能
|
- 最高性能
|
||||||
- 簡単配布
|
- 簡単配布
|
||||||
|
|
||||||
|
簡易スモークテスト(VM と EXE の出力一致確認):
|
||||||
|
```bash
|
||||||
|
tools/smoke_aot_vs_vm.sh examples/aot_min_string_len.nyash
|
||||||
|
```
|
||||||
|
|
||||||
### 5. **WebAssembly** (ブラウザ用)
|
### 5. **WebAssembly** (ブラウザ用)
|
||||||
```bash
|
```bash
|
||||||
|
cargo build --release --features wasm-backend
|
||||||
./target/release/nyash --compile-wasm program.nyash
|
./target/release/nyash --compile-wasm program.nyash
|
||||||
```
|
```
|
||||||
- ブラウザで実行
|
- ブラウザで実行
|
||||||
@ -237,6 +246,11 @@ echo 'print("Hello Nyash!")' > hello.nyash
|
|||||||
cargo install cargo-xwin
|
cargo install cargo-xwin
|
||||||
cargo xwin build --target x86_64-pc-windows-msvc --release
|
cargo xwin build --target x86_64-pc-windows-msvc --release
|
||||||
# target/x86_64-pc-windows-msvc/release/nyash.exe を使用
|
# target/x86_64-pc-windows-msvc/release/nyash.exe を使用
|
||||||
|
|
||||||
|
# WindowsでのネイティブEXE(AOT)ビルド(Cranelift と MSYS2/WSL が必要)
|
||||||
|
cargo build --release --features cranelift-jit
|
||||||
|
powershell -ExecutionPolicy Bypass -File tools\build_aot.ps1 -Input examples\aot_min_string_len.nyash -Out app.exe
|
||||||
|
./app.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -338,4 +352,4 @@ MIT ライセンス - プロジェクトで自由に使用してください!
|
|||||||
|
|
||||||
**🚀 Nyash - すべてがBoxであり、Boxがネイティブコードにコンパイルされる場所!**
|
**🚀 Nyash - すべてがBoxであり、Boxがネイティブコードにコンパイルされる場所!**
|
||||||
|
|
||||||
*❤️、🤖 AIコラボレーション、そしてプログラミング言語は思考の速度で作れるという信念で構築*
|
*❤️、🤖 AIコラボレーション、そしてプログラミング言語は思考の速度で作れるという信念で構築*
|
||||||
|
|||||||
22
README.md
22
README.md
@ -26,10 +26,10 @@ No installation needed - experience Nyash instantly in your web browser!
|
|||||||
**August 29, 2025** - Just 20 days after inception, Nyash can now compile to native executables!
|
**August 29, 2025** - Just 20 days after inception, Nyash can now compile to native executables!
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# From Nyash source to native binary
|
# From Nyash source to native binary (Cranelift required)
|
||||||
./target/release/nyash --backend vm program.nyash # JIT compilation
|
cargo build --release --features cranelift-jit
|
||||||
./tools/build_aot.sh program.nyash -o app # Native EXE
|
./tools/build_aot.sh program.nyash -o app # Native EXE
|
||||||
./app # Standalone execution!
|
./app # Standalone execution!
|
||||||
```
|
```
|
||||||
|
|
||||||
**What we achieved in 20 days:**
|
**What we achieved in 20 days:**
|
||||||
@ -113,6 +113,9 @@ NYASH_JIT_EXEC=1 ./target/release/nyash --backend vm program.nyash
|
|||||||
|
|
||||||
### 4. **Native Binary** (Distribution)
|
### 4. **Native Binary** (Distribution)
|
||||||
```bash
|
```bash
|
||||||
|
# Build once (Cranelift)
|
||||||
|
cargo build --release --features cranelift-jit
|
||||||
|
|
||||||
./tools/build_aot.sh program.nyash -o myapp
|
./tools/build_aot.sh program.nyash -o myapp
|
||||||
./myapp # Standalone executable!
|
./myapp # Standalone executable!
|
||||||
```
|
```
|
||||||
@ -120,8 +123,14 @@ NYASH_JIT_EXEC=1 ./target/release/nyash --backend vm program.nyash
|
|||||||
- Maximum performance
|
- Maximum performance
|
||||||
- Easy distribution
|
- Easy distribution
|
||||||
|
|
||||||
|
Quick smoke test (VM vs EXE):
|
||||||
|
```bash
|
||||||
|
tools/smoke_aot_vs_vm.sh examples/aot_min_string_len.nyash
|
||||||
|
```
|
||||||
|
|
||||||
### 5. **WebAssembly** (Browser)
|
### 5. **WebAssembly** (Browser)
|
||||||
```bash
|
```bash
|
||||||
|
cargo build --release --features wasm-backend
|
||||||
./target/release/nyash --compile-wasm program.nyash
|
./target/release/nyash --compile-wasm program.nyash
|
||||||
```
|
```
|
||||||
- Run in browsers
|
- Run in browsers
|
||||||
@ -237,6 +246,11 @@ echo 'print("Hello Nyash!")' > hello.nyash
|
|||||||
cargo install cargo-xwin
|
cargo install cargo-xwin
|
||||||
cargo xwin build --target x86_64-pc-windows-msvc --release
|
cargo xwin build --target x86_64-pc-windows-msvc --release
|
||||||
# Use target/x86_64-pc-windows-msvc/release/nyash.exe
|
# Use target/x86_64-pc-windows-msvc/release/nyash.exe
|
||||||
|
|
||||||
|
# Native EXE (AOT) on Windows (requires Cranelift and MSYS2/WSL toolchain for linking)
|
||||||
|
cargo build --release --features cranelift-jit
|
||||||
|
powershell -ExecutionPolicy Bypass -File tools\build_aot.ps1 -Input examples\aot_min_string_len.nyash -Out app.exe
|
||||||
|
./app.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -338,4 +352,4 @@ MIT License - Use freely in your projects!
|
|||||||
|
|
||||||
**🚀 Nyash - Where Everything is a Box, and Boxes Compile to Native Code!**
|
**🚀 Nyash - Where Everything is a Box, and Boxes Compile to Native Code!**
|
||||||
|
|
||||||
*Built with ❤️, 🤖 AI collaboration, and the belief that programming languages can be created at the speed of thought*
|
*Built with ❤️, 🤖 AI collaboration, and the belief that programming languages can be created at the speed of thought*
|
||||||
|
|||||||
@ -109,8 +109,8 @@ pub extern "C" fn main() -> i32 {
|
|||||||
}
|
}
|
||||||
// SAFETY: if not linked, calling will be an unresolved symbol at link-time; we rely on link step to include ny_main.
|
// SAFETY: if not linked, calling will be an unresolved symbol at link-time; we rely on link step to include ny_main.
|
||||||
let v = ny_main();
|
let v = ny_main();
|
||||||
// Print minimal observation
|
// Print standardized result line for golden comparisons
|
||||||
println!("ny_main() returned: {}", v);
|
println!("Result: {}", v);
|
||||||
0
|
v as i32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# 🎯 CURRENT TASK - 2025-08-29(Phase 10.1 革新的転換)
|
# 🎯 CURRENT TASK - 2025-08-29(Phase 10.5 転回:JIT分離=EXE専用)
|
||||||
|
|
||||||
Phase 10.10 は完了(DoD確認済)。**重大な発見**:プラグインシステムを活用したJIT→EXE実現の道を発見!
|
Phase 10.10 は完了(DoD確認済)。アーキテクチャ転回:JITは「EXE/AOT生成専用コンパイラ」、実行はVM一本に統一。
|
||||||
|
|
||||||
## 🚀 革新的発見:プラグインBox統一化
|
## 🚀 革新的発見:プラグインBox統一化
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ Phase 10.10 は完了(DoD確認済)。**重大な発見**:プラグイン
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2025-08-29 PM3 再起動スナップショット(Strict前倒し版)
|
## 2025-08-29 PM3 再起動スナップショット(Strict/分離確定版)
|
||||||
|
|
||||||
### 現在の着地(Strict準備済み)
|
### 現在の着地(Strict準備済み)
|
||||||
- InvokePolicy/Observe を導入し、Lowerer の分岐をスリム化
|
- InvokePolicy/Observe を導入し、Lowerer の分岐をスリム化
|
||||||
@ -57,13 +57,13 @@ Phase 10.10 は完了(DoD確認済)。**重大な発見**:プラグイン
|
|||||||
- 特殊コメント(最小)
|
- 特殊コメント(最小)
|
||||||
- `// @env KEY=VALUE`, `// @jit-debug`, `// @plugin-builtins`, `// @jit-strict`
|
- `// @env KEY=VALUE`, `// @jit-debug`, `// @plugin-builtins`, `// @jit-strict`
|
||||||
|
|
||||||
### Strict モード(Fail-Fast / ノーフォールバック)
|
### Strict/分離(Fail-Fast / ノーフォールバック)
|
||||||
- 目的: 「VM=仕様 / JIT=高速実装」。JITで動かない=JITのバグを即可視化
|
- 目的: 「VM=仕様 / JIT=コンパイル」。JITで未対応/フォールバックがあれば即コンパイル失敗
|
||||||
- 有効化: `// @jit-strict`(または `NYASH_JIT_STRICT=1`)
|
- 有効化: 実行はVM固定、JITは `--compile-native`(AOT)でのみ使用
|
||||||
- 仕様(現状)
|
- 仕様(現状)
|
||||||
- Lowerer/Engine: unsupported>0 がある関数はコンパイル中止(fail-fast)
|
- Lowerer/Engine: unsupported>0 または compile-phase fallback>0 でコンパイル中止
|
||||||
- 実行: `NYASH_JIT_ONLY=1` と併用でフォールバック禁止(エラー)
|
- 実行: JITディスパッチ既定OFF(VMのみ)。StrictはJITを常時JIT-only/handle-only相当で動かす
|
||||||
- シム: 受け手解決は HandleRegistry 優先(`NYASH_JIT_ARGS_HANDLE_ONLY=1` 自動ON)
|
- シム: 受け手解決は HandleRegistry 優先(`NYASH_JIT_ARGS_HANDLE_ONLY=1`)
|
||||||
|
|
||||||
### 再起動チェックリスト
|
### 再起動チェックリスト
|
||||||
- Build(Cranelift有効): `cargo build --release -j32 --features cranelift-jit`
|
- Build(Cranelift有効): `cargo build --release -j32 --features cranelift-jit`
|
||||||
@ -164,17 +164,19 @@ cat jit_events.jsonl
|
|||||||
- InvokePolicyPass(新規): `src/jit/policy/invoke.rs` — plugin/hostcall/ANY の経路選択を一元化(Lowerer から分離)
|
- InvokePolicyPass(新規): `src/jit/policy/invoke.rs` — plugin/hostcall/ANY の経路選択を一元化(Lowerer から分離)
|
||||||
- Observe(新規): `src/jit/observe.rs` — compile/runtime/trace 出力の統一(ガード/出力先/JSONスキーマ)
|
- Observe(新規): `src/jit/observe.rs` — compile/runtime/trace 出力の統一(ガード/出力先/JSONスキーマ)
|
||||||
|
|
||||||
### 今後のToDo(優先度順)
|
### 今後のToDo(優先度順:分離/AOT)
|
||||||
1) InvokePolicyPass の導入
|
1) 実行モード分離(CLI/Runner)
|
||||||
- 目的: Lowerer 内の分岐を薄くし、経路選択を一箇所に固定(read-only/allowlist/ANY fallbackを明確化)
|
- 目的: `nyash file.nyash` は常にVM実行。`--compile-native -o app` でEXE生成。
|
||||||
- DoD: length()/push/get/set の経路が policy 設定で一意に決まる(compile/runtimeのイベント差異が「設定」由来で説明可能)
|
- DoD: VM内のJITディスパッチは既定OFF。StrictはJIT=AOTで常時Fail-Fast。
|
||||||
2) Observe の導入
|
2) AOTパイプライン確立(obj→exe)
|
||||||
- 目的: runtime/trace の出力有無を一箇所で制御、`NYASH_JIT_EVENTS(_COMPILE/_RUNTIME)` の挙動を統一
|
- 目的: Lower→CLIF→OBJ→`ny_main`+`libnyrt.a`リンクの一発通し
|
||||||
- DoD: `NYASH_JIT_EVENTS=1` で compile/runtime が必ず出る。PATH 指定時はJSONLに確実追記
|
- DoD: `tools/build_aot.sh` の内製依存をCLIサブコマンド化。Windows/macOSは後段。
|
||||||
3) String/Array の誤ラベル最終解消
|
3) AOT箱の追加
|
||||||
- 型不明時は `Any.length` としてcompile-phaseに出す/または plugin_invoke の type_id を runtime で必ず記録
|
- AotConfigBox: 出力先/ターゲット/リンクフラグ/プラグイン探索を管理し、apply()でenv同期
|
||||||
4) f64戻り/ハンドル返却(tag=8)の仕上げ
|
- AotCompilerBox: `compile(file, out)` でOBJ/EXEを生成、events/結果文字列を返す
|
||||||
- `NYASH_JIT_PLUGIN_F64` なしの自動選択、handle返却シムの導入(tag=8)
|
4) 観測の統一
|
||||||
|
- 目的: `NYASH_JIT_EVENTS=1` で compile/runtime が必ず出力。PATH指定はJSONL追記
|
||||||
|
- DoD: `jit::observe` 経由へ集約
|
||||||
|
|
||||||
### 受け入れ条件(DoD)
|
### 受け入れ条件(DoD)
|
||||||
- compile-phase: `plugin:*` のイベントが関数ごとに安定
|
- compile-phase: `plugin:*` のイベントが関数ごとに安定
|
||||||
@ -192,10 +194,10 @@ cat jit_events.jsonl
|
|||||||
- GC Switchable Runtime(GcConfigBox)/ Unified Debug(DebugConfigBox)
|
- GC Switchable Runtime(GcConfigBox)/ Unified Debug(DebugConfigBox)
|
||||||
- JitPolicyBox(allowlist/presets)/ HostCallのRO運用(events連携)
|
- JitPolicyBox(allowlist/presets)/ HostCallのRO運用(events連携)
|
||||||
- CIスモーク導入(runtime/compile-events)/ 代表サンプル整備
|
- CIスモーク導入(runtime/compile-events)/ 代表サンプル整備
|
||||||
- 🔧 Doing(Phase 10.1 新計画)
|
- 🔧 Doing(Phase 10.5 分離/AOT)
|
||||||
- NewBox→birthのJIT lowering(String/Integer、handleベース)
|
- VM実行の既定固定(JITディスパッチは既定OFF)
|
||||||
- AOT最小EXE: libnyrt.aシム + ny_main ドライバ + build_aot.sh 整備
|
- AOT最小EXE: libnyrt.aシム + ny_main ドライバ + build_aot.sh → CLI化
|
||||||
- リファクタリング作業は継続(core_hostcall.rs完了)
|
- リファクタリング継続(core_hostcall.rs→observe/policy統合)
|
||||||
- ⏭️ Next(Phase 10.1 実装)
|
- ⏭️ Next(Phase 10.1 実装)
|
||||||
- Week1: 主要ビルトインBoxの移行(RO中心)
|
- Week1: 主要ビルトインBoxの移行(RO中心)
|
||||||
- Week2: 静的同梱基盤の設計(type_id→nyplug_*_invoke ディスパッチ)
|
- Week2: 静的同梱基盤の設計(type_id→nyplug_*_invoke ディスパッチ)
|
||||||
|
|||||||
@ -1,21 +1,29 @@
|
|||||||
# Phase 10.5 – Python ネイティブ統合(Embedding & FFI)/ JIT Strict 化の前倒し
|
# Phase 10.5 – Python ネイティブ統合(Embedding & FFI)/ JIT分離(EXE専用化)
|
||||||
*(旧10.1の一部を後段フェーズに再編。Everything is Plugin/AOTの基盤上で実現)*
|
*(旧10.1の一部を後段フェーズに再編。Everything is Plugin/AOTの基盤上で実現)*
|
||||||
|
|
||||||
NyashとPythonを双方向に“ネイティブ”接続する前に、JITの開発・検証効率を最大化するため、VM=仕様/JIT=高速実装 という原則に沿った「JIT Strict モード」を前倒し導入し、フォールバック起因の複雑性を排除する。
|
本フェーズでは方針を明確化する:実行はVMが唯一の基準系、JITは「EXE/AOT生成専用のコンパイラ」として分離運用する。
|
||||||
|
|
||||||
|
アーキテクチャの整理(決定)
|
||||||
|
- 開発/デバッグ: MIR → VM(完全実行)
|
||||||
|
- 本番/配布: MIR → JIT(CLIF)→ OBJ → EXE(完全コンパイル)
|
||||||
|
|
||||||
|
ポイント
|
||||||
|
- フォールバック不要/禁止: JITが未対応ならコンパイルエラー。VMへは落とさない。
|
||||||
|
- 役割分担の明確化: VM=仕様/挙動の唯一の基準、JIT=ネイティブ生成器。
|
||||||
|
- プラグイン整合: VM/EXEとも同一のBID/FFIプラグインを利用(Everything is Plugin)。
|
||||||
|
|
||||||
## 📂 サブフェーズ構成(10.5a → 10.5e)
|
## 📂 サブフェーズ構成(10.5a → 10.5e)
|
||||||
|
|
||||||
先行タスク(最優先)
|
先行タスク(最優先)
|
||||||
- 10.5s JIT Strict モード導入(Fail-Fast / ノーフォールバック)
|
- 10.5s JIT Strict/分離の確定(Fail-Fast / ノーフォールバック)
|
||||||
- 目的: 「VMで動く=正。JITで動かない=JITのバグ」を可視化、開発ループを短縮
|
- 目的: 「VM=実行・JIT=コンパイル」の二系統で混在を排除し、検証を単純化
|
||||||
- 仕様:
|
- 仕様:
|
||||||
- // @jit-strict または NYASH_JIT_STRICT=1 で有効化
|
- JITは実行経路から外し、`--compile-native`(AOT)でのみ使用
|
||||||
- Lowerer: unsupported>0 の場合はコンパイルを中止(診断を返す)
|
- Lowerer/Engine: unsupported>0 または fallback判定>0 でコンパイル中止(Fail-Fast)
|
||||||
- 実行: JIT_ONLY と併用時はフォールバック禁止(失敗は明示エラー)
|
- 実行: VMのみ。フォールバックという概念自体を削除
|
||||||
- シム: 受け手解決は HandleRegistry 優先。param-index 互換経路は無効化
|
|
||||||
- DoD:
|
- DoD:
|
||||||
- Array/Map の代表ケースで Strict 実行時に compile/runtime/シムイベントの整合が取れる
|
- CLIに `--compile-native` を追加し、OBJ/EXE生成が一発で通る
|
||||||
- VM=JIT の差が発生したときに即座に落ち、原因特定がしやすい(フォールバックに逃げない)
|
- VM実行は常にVMのみ(JITディスパッチ既定OFF)。
|
||||||
|
|
||||||
### 10.5a 設計・ABI整合(1–2日)
|
### 10.5a 設計・ABI整合(1–2日)
|
||||||
- ルート選択:
|
- ルート選択:
|
||||||
@ -38,9 +46,10 @@ NyashとPythonを双方向に“ネイティブ”接続する前に、JITの開
|
|||||||
- エラーハンドリング: 例外は文字列化(tag=6)でNyashに返却、またはResult化
|
- エラーハンドリング: 例外は文字列化(tag=6)でNyashに返却、またはResult化
|
||||||
|
|
||||||
### 10.5d JIT/AOT 統合(3–5日)
|
### 10.5d JIT/AOT 統合(3–5日)
|
||||||
- JIT: `emit_plugin_invoke` で Pythonメソッド呼びを許可(ROから開始)
|
- AOTパイプライン固定: Lower→CLIF→OBJ出力→`ny_main`+`libnyrt.a`リンク→EXE
|
||||||
- AOT: libnyrt.a に `nyash.python.*` シム(birth_hなど)を追加し、ObjectModuleの未解決を解決
|
- CLI: `nyash --compile-native file.nyash -o app` を追加(失敗は非ゼロ終了)
|
||||||
- 静的同梱経路: `nyrt` に type_id→`nyplug_python_invoke` ディスパッチテーブルを実装(または各プラグイン名ごとのルータ)。第一段は動的ロード優先
|
- libnyrt: `nyash.python.*` 等のシムを提供し、未解決シンボル解決
|
||||||
|
- ディスパッチ: type_id→`nyplug_*_invoke` の静的/動的ルート(第一段は動的優先)
|
||||||
|
|
||||||
### 10.5e サンプル/テスト/ドキュメント(1週間)
|
### 10.5e サンプル/テスト/ドキュメント(1週間)
|
||||||
- サンプル: `py.eval("'hello' * 3").str()`、`numpy`の軽量ケース(import/shape参照などRO中心)
|
- サンプル: `py.eval("'hello' * 3").str()`、`numpy`の軽量ケース(import/shape参照などRO中心)
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
// @jit-strict
|
||||||
|
// @jit-debug
|
||||||
|
// @plugin-builtins
|
||||||
|
|
||||||
// JIT plugin_invoke smoke: no NewBox in JITed function
|
// JIT plugin_invoke smoke: no NewBox in JITed function
|
||||||
// Requires: array plugin built + nyash.toml configured
|
// Requires: array plugin built + nyash.toml configured
|
||||||
// Build plugins:
|
// Build plugins:
|
||||||
|
|||||||
92
nyash.toml
92
nyash.toml
@ -3,23 +3,23 @@
|
|||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# ライブラリ定義(1つのプラグインで複数のBox型を提供可能)
|
# ライブラリ定義(1つのプラグインで複数のBox型を提供可能)
|
||||||
[libraries."libnyash_filebox_plugin.so"]
|
[libraries."libnyash_filebox_plugin"]
|
||||||
boxes = ["FileBox"]
|
boxes = ["FileBox"]
|
||||||
path = "./plugins/nyash-filebox-plugin/target/release/libnyash_filebox_plugin.so"
|
path = "./plugins/nyash-filebox-plugin/target/release/libnyash_filebox_plugin"
|
||||||
|
|
||||||
[libraries."libnyash_counter_plugin.so"]
|
[libraries."libnyash_counter_plugin"]
|
||||||
boxes = ["CounterBox"]
|
boxes = ["CounterBox"]
|
||||||
path = "./plugins/nyash-counter-plugin/target/release/libnyash_counter_plugin.so"
|
path = "./plugins/nyash-counter-plugin/target/release/libnyash_counter_plugin"
|
||||||
|
|
||||||
[libraries."libnyash_net_plugin.so"]
|
[libraries."libnyash_net_plugin"]
|
||||||
boxes = ["HttpServerBox", "HttpClientBox", "HttpResponseBox", "HttpRequestBox", "SocketServerBox", "SocketClientBox", "SocketConnBox"]
|
boxes = ["HttpServerBox", "HttpClientBox", "HttpResponseBox", "HttpRequestBox", "SocketServerBox", "SocketClientBox", "SocketConnBox"]
|
||||||
path = "./plugins/nyash-net-plugin/target/release/libnyash_net_plugin.so"
|
path = "./plugins/nyash-net-plugin/target/release/libnyash_net_plugin"
|
||||||
|
|
||||||
# FileBoxの型情報定義
|
# FileBoxの型情報定義
|
||||||
[libraries."libnyash_filebox_plugin.so".FileBox]
|
[libraries."libnyash_filebox_plugin".FileBox]
|
||||||
type_id = 6
|
type_id = 6
|
||||||
|
|
||||||
[libraries."libnyash_filebox_plugin.so".FileBox.methods]
|
[libraries."libnyash_filebox_plugin".FileBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
open = { method_id = 1, args = ["path", "mode"] }
|
open = { method_id = 1, args = ["path", "mode"] }
|
||||||
read = { method_id = 2 }
|
read = { method_id = 2 }
|
||||||
@ -29,21 +29,21 @@ fini = { method_id = 4294967295 }
|
|||||||
copyFrom = { method_id = 7, args = [ { kind = "box", category = "plugin" } ] }
|
copyFrom = { method_id = 7, args = [ { kind = "box", category = "plugin" } ] }
|
||||||
cloneSelf = { method_id = 8 }
|
cloneSelf = { method_id = 8 }
|
||||||
|
|
||||||
[libraries."libnyash_counter_plugin.so".CounterBox]
|
[libraries."libnyash_counter_plugin".CounterBox]
|
||||||
type_id = 7
|
type_id = 7
|
||||||
singleton = true
|
singleton = true
|
||||||
|
|
||||||
[libraries."libnyash_counter_plugin.so".CounterBox.methods]
|
[libraries."libnyash_counter_plugin".CounterBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
inc = { method_id = 1 }
|
inc = { method_id = 1 }
|
||||||
get = { method_id = 2 }
|
get = { method_id = 2 }
|
||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
|
|
||||||
# HttpServerBox
|
# HttpServerBox
|
||||||
[libraries."libnyash_net_plugin.so".HttpServerBox]
|
[libraries."libnyash_net_plugin".HttpServerBox]
|
||||||
type_id = 20
|
type_id = 20
|
||||||
|
|
||||||
[libraries."libnyash_net_plugin.so".HttpServerBox.methods]
|
[libraries."libnyash_net_plugin".HttpServerBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
start = { method_id = 1, args = ["port"], returns_result = true }
|
start = { method_id = 1, args = ["port"], returns_result = true }
|
||||||
stop = { method_id = 2, returns_result = true }
|
stop = { method_id = 2, returns_result = true }
|
||||||
@ -51,20 +51,20 @@ accept = { method_id = 3, returns_result = true }
|
|||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
|
|
||||||
# HttpClientBox
|
# HttpClientBox
|
||||||
[libraries."libnyash_net_plugin.so".HttpClientBox]
|
[libraries."libnyash_net_plugin".HttpClientBox]
|
||||||
type_id = 23
|
type_id = 23
|
||||||
|
|
||||||
[libraries."libnyash_net_plugin.so".HttpClientBox.methods]
|
[libraries."libnyash_net_plugin".HttpClientBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
get = { method_id = 1, args = ["url"], returns_result = true }
|
get = { method_id = 1, args = ["url"], returns_result = true }
|
||||||
post = { method_id = 2, args = ["url", "body"], returns_result = true }
|
post = { method_id = 2, args = ["url", "body"], returns_result = true }
|
||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
|
|
||||||
# HttpResponseBox
|
# HttpResponseBox
|
||||||
[libraries."libnyash_net_plugin.so".HttpResponseBox]
|
[libraries."libnyash_net_plugin".HttpResponseBox]
|
||||||
type_id = 22
|
type_id = 22
|
||||||
|
|
||||||
[libraries."libnyash_net_plugin.so".HttpResponseBox.methods]
|
[libraries."libnyash_net_plugin".HttpResponseBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
setStatus = { method_id = 1, args = ["status"] }
|
setStatus = { method_id = 1, args = ["status"] }
|
||||||
setHeader = { method_id = 2, args = ["key", "value"] }
|
setHeader = { method_id = 2, args = ["key", "value"] }
|
||||||
@ -75,10 +75,10 @@ getHeader = { method_id = 6, args = ["key"] }
|
|||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
|
|
||||||
# HttpRequestBox
|
# HttpRequestBox
|
||||||
[libraries."libnyash_net_plugin.so".HttpRequestBox]
|
[libraries."libnyash_net_plugin".HttpRequestBox]
|
||||||
type_id = 21
|
type_id = 21
|
||||||
|
|
||||||
[libraries."libnyash_net_plugin.so".HttpRequestBox.methods]
|
[libraries."libnyash_net_plugin".HttpRequestBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
path = { method_id = 1 }
|
path = { method_id = 1 }
|
||||||
readBody = { method_id = 2 }
|
readBody = { method_id = 2 }
|
||||||
@ -86,20 +86,20 @@ respond = { method_id = 3, args = [{ kind = "box", category = "plugin" }] }
|
|||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
|
|
||||||
# SocketServerBox
|
# SocketServerBox
|
||||||
[libraries."libnyash_net_plugin.so".SocketServerBox]
|
[libraries."libnyash_net_plugin".SocketServerBox]
|
||||||
type_id = 30
|
type_id = 30
|
||||||
|
|
||||||
[libraries."libnyash_net_plugin.so".SocketServerBox.methods]
|
[libraries."libnyash_net_plugin".SocketServerBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
bind = { method_id = 1, args = ["port"] }
|
bind = { method_id = 1, args = ["port"] }
|
||||||
accept = { method_id = 2 }
|
accept = { method_id = 2 }
|
||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
|
|
||||||
# SocketClientBox
|
# SocketClientBox
|
||||||
[libraries."libnyash_net_plugin.so".SocketClientBox]
|
[libraries."libnyash_net_plugin".SocketClientBox]
|
||||||
type_id = 32
|
type_id = 32
|
||||||
|
|
||||||
[libraries."libnyash_net_plugin.so".SocketClientBox.methods]
|
[libraries."libnyash_net_plugin".SocketClientBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
connect = { method_id = 1, args = ["host", "port"] }
|
connect = { method_id = 1, args = ["host", "port"] }
|
||||||
send = { method_id = 2, args = ["data"] }
|
send = { method_id = 2, args = ["data"] }
|
||||||
@ -108,10 +108,10 @@ close = { method_id = 4 }
|
|||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
|
|
||||||
# SocketConnBox
|
# SocketConnBox
|
||||||
[libraries."libnyash_net_plugin.so".SocketConnBox]
|
[libraries."libnyash_net_plugin".SocketConnBox]
|
||||||
type_id = 31
|
type_id = 31
|
||||||
|
|
||||||
[libraries."libnyash_net_plugin.so".SocketConnBox.methods]
|
[libraries."libnyash_net_plugin".SocketConnBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
send = { method_id = 1, args = ["data"] }
|
send = { method_id = 1, args = ["data"] }
|
||||||
recv = { method_id = 2 }
|
recv = { method_id = 2 }
|
||||||
@ -128,28 +128,28 @@ search_paths = [
|
|||||||
"/usr/local/lib/nyash/plugins",
|
"/usr/local/lib/nyash/plugins",
|
||||||
"~/.nyash/plugins"
|
"~/.nyash/plugins"
|
||||||
]
|
]
|
||||||
[libraries."libnyash_array_plugin.so"]
|
[libraries."libnyash_array_plugin"]
|
||||||
boxes = ["ArrayBox"]
|
boxes = ["ArrayBox"]
|
||||||
path = "./plugins/nyash-array-plugin/target/release/libnyash_array_plugin.so"
|
path = "./plugins/nyash-array-plugin/target/release/libnyash_array_plugin"
|
||||||
|
|
||||||
[libraries."libnyash_array_plugin.so".ArrayBox]
|
[libraries."libnyash_array_plugin".ArrayBox]
|
||||||
type_id = 10
|
type_id = 10
|
||||||
|
|
||||||
[libraries."libnyash_array_plugin.so".ArrayBox.methods]
|
[libraries."libnyash_array_plugin".ArrayBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
length = { method_id = 1 }
|
length = { method_id = 1 }
|
||||||
get = { method_id = 2, args = ["index"] }
|
get = { method_id = 2, args = ["index"] }
|
||||||
push = { method_id = 3, args = ["value"] }
|
push = { method_id = 3, args = ["value"] }
|
||||||
set = { method_id = 4, args = ["index", "value"] }
|
set = { method_id = 4, args = ["index", "value"] }
|
||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
[libraries."libnyash_map_plugin.so"]
|
[libraries."libnyash_map_plugin"]
|
||||||
boxes = ["MapBox"]
|
boxes = ["MapBox"]
|
||||||
path = "./plugins/nyash-map-plugin/target/release/libnyash_map_plugin.so"
|
path = "./plugins/nyash-map-plugin/target/release/libnyash_map_plugin"
|
||||||
|
|
||||||
[libraries."libnyash_map_plugin.so".MapBox]
|
[libraries."libnyash_map_plugin".MapBox]
|
||||||
type_id = 11
|
type_id = 11
|
||||||
|
|
||||||
[libraries."libnyash_map_plugin.so".MapBox.methods]
|
[libraries."libnyash_map_plugin".MapBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
size = { method_id = 1 }
|
size = { method_id = 1 }
|
||||||
get = { method_id = 2, args = ["key"] }
|
get = { method_id = 2, args = ["key"] }
|
||||||
@ -158,28 +158,28 @@ set = { method_id = 4, args = ["key", "value"] }
|
|||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
|
|
||||||
# IntegerBox plugin (basic numeric box)
|
# IntegerBox plugin (basic numeric box)
|
||||||
[libraries."libnyash_integer_plugin.so"]
|
[libraries."libnyash_integer_plugin"]
|
||||||
boxes = ["IntegerBox"]
|
boxes = ["IntegerBox"]
|
||||||
path = "./plugins/nyash-integer-plugin/target/release/libnyash_integer_plugin.so"
|
path = "./plugins/nyash-integer-plugin/target/release/libnyash_integer_plugin"
|
||||||
|
|
||||||
[libraries."libnyash_integer_plugin.so".IntegerBox]
|
[libraries."libnyash_integer_plugin".IntegerBox]
|
||||||
type_id = 12
|
type_id = 12
|
||||||
|
|
||||||
[libraries."libnyash_integer_plugin.so".IntegerBox.methods]
|
[libraries."libnyash_integer_plugin".IntegerBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
get = { method_id = 1 }
|
get = { method_id = 1 }
|
||||||
set = { method_id = 2, args = ["value"] }
|
set = { method_id = 2, args = ["value"] }
|
||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
|
|
||||||
# StringBox plugin (read-only methods first)
|
# StringBox plugin (read-only methods first)
|
||||||
[libraries."libnyash_string_plugin.so"]
|
[libraries."libnyash_string_plugin"]
|
||||||
boxes = ["StringBox"]
|
boxes = ["StringBox"]
|
||||||
path = "./plugins/nyash-string-plugin/target/release/libnyash_string_plugin.so"
|
path = "./plugins/nyash-string-plugin/target/release/libnyash_string_plugin"
|
||||||
|
|
||||||
[libraries."libnyash_string_plugin.so".StringBox]
|
[libraries."libnyash_string_plugin".StringBox]
|
||||||
type_id = 13
|
type_id = 13
|
||||||
|
|
||||||
[libraries."libnyash_string_plugin.so".StringBox.methods]
|
[libraries."libnyash_string_plugin".StringBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
length = { method_id = 1 }
|
length = { method_id = 1 }
|
||||||
is_empty = { method_id = 2 }
|
is_empty = { method_id = 2 }
|
||||||
@ -189,14 +189,14 @@ fromUtf8 = { method_id = 5, args = ["data"] }
|
|||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
|
|
||||||
# Python plugin (Phase 10.5 – Embedding & FFI, initial scaffold)
|
# Python plugin (Phase 10.5 – Embedding & FFI, initial scaffold)
|
||||||
[libraries."libnyash_python_plugin.so"]
|
[libraries."libnyash_python_plugin"]
|
||||||
boxes = ["PyRuntimeBox", "PyObjectBox"]
|
boxes = ["PyRuntimeBox", "PyObjectBox"]
|
||||||
path = "./plugins/nyash-python-plugin/target/release/libnyash_python_plugin.so"
|
path = "./plugins/nyash-python-plugin/target/release/libnyash_python_plugin"
|
||||||
|
|
||||||
[libraries."libnyash_python_plugin.so".PyRuntimeBox]
|
[libraries."libnyash_python_plugin".PyRuntimeBox]
|
||||||
type_id = 40
|
type_id = 40
|
||||||
|
|
||||||
[libraries."libnyash_python_plugin.so".PyRuntimeBox.methods]
|
[libraries."libnyash_python_plugin".PyRuntimeBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
eval = { method_id = 1, args = ["code"] }
|
eval = { method_id = 1, args = ["code"] }
|
||||||
import = { method_id = 2, args = ["name"] }
|
import = { method_id = 2, args = ["name"] }
|
||||||
@ -204,10 +204,10 @@ fini = { method_id = 4294967295 }
|
|||||||
evalR = { method_id = 11, args = ["code"], returns_result = true }
|
evalR = { method_id = 11, args = ["code"], returns_result = true }
|
||||||
importR= { method_id = 12, args = ["name"], returns_result = true }
|
importR= { method_id = 12, args = ["name"], returns_result = true }
|
||||||
|
|
||||||
[libraries."libnyash_python_plugin.so".PyObjectBox]
|
[libraries."libnyash_python_plugin".PyObjectBox]
|
||||||
type_id = 41
|
type_id = 41
|
||||||
|
|
||||||
[libraries."libnyash_python_plugin.so".PyObjectBox.methods]
|
[libraries."libnyash_python_plugin".PyObjectBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
getattr = { method_id = 1, args = ["name"] }
|
getattr = { method_id = 1, args = ["name"] }
|
||||||
call = { method_id = 2, args = ["args"] }
|
call = { method_id = 2, args = ["args"] }
|
||||||
|
|||||||
@ -303,6 +303,30 @@ impl BuiltinBoxFactory {
|
|||||||
Ok(Box::new(crate::boxes::jit_policy_box::JitPolicyBox::new()))
|
Ok(Box::new(crate::boxes::jit_policy_box::JitPolicyBox::new()))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// JitStrictBox (strict-mode toggles & counters)
|
||||||
|
self.register("JitStrictBox", |args| {
|
||||||
|
if !args.is_empty() {
|
||||||
|
return Err(RuntimeError::InvalidOperation {
|
||||||
|
message: format!("JitStrictBox constructor expects 0 arguments, got {}", args.len()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(Box::new(crate::boxes::jit_strict_box::JitStrictBox::new()))
|
||||||
|
});
|
||||||
|
|
||||||
|
// AOT configuration and compiler boxes
|
||||||
|
self.register("AotConfigBox", |args| {
|
||||||
|
if !args.is_empty() {
|
||||||
|
return Err(RuntimeError::InvalidOperation { message: format!("AotConfigBox constructor expects 0 arguments, got {}", args.len()) });
|
||||||
|
}
|
||||||
|
Ok(Box::new(crate::boxes::aot_config_box::AotConfigBox::new()))
|
||||||
|
});
|
||||||
|
self.register("AotCompilerBox", |args| {
|
||||||
|
if !args.is_empty() {
|
||||||
|
return Err(RuntimeError::InvalidOperation { message: format!("AotCompilerBox constructor expects 0 arguments, got {}", args.len()) });
|
||||||
|
}
|
||||||
|
Ok(Box::new(crate::boxes::aot_compiler_box::AotCompilerBox::new()))
|
||||||
|
});
|
||||||
|
|
||||||
// DebugConfigBox (runtime debug/observability switches)
|
// DebugConfigBox (runtime debug/observability switches)
|
||||||
self.register("DebugConfigBox", |args| {
|
self.register("DebugConfigBox", |args| {
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
|
|||||||
53
src/boxes/aot_compiler_box.rs
Normal file
53
src/boxes/aot_compiler_box.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AotCompilerBox { base: BoxBase }
|
||||||
|
|
||||||
|
impl AotCompilerBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
|
||||||
|
|
||||||
|
impl BoxCore for AotCompilerBox {
|
||||||
|
fn box_id(&self) -> u64 { self.base.id }
|
||||||
|
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||||
|
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "AotCompilerBox") }
|
||||||
|
fn as_any(&self) -> &dyn Any { self }
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NyashBox for AotCompilerBox {
|
||||||
|
fn to_string_box(&self) -> StringBox { StringBox::new("AotCompilerBox".to_string()) }
|
||||||
|
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<AotCompilerBox>()) }
|
||||||
|
fn type_name(&self) -> &'static str { "AotCompilerBox" }
|
||||||
|
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||||
|
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AotCompilerBox {
|
||||||
|
/// Compile a .nyash file into native EXE by re-invoking current binary with --compile-native.
|
||||||
|
/// Returns combined stdout/stderr as String.
|
||||||
|
pub fn compile(&self, file: &str, out: &str) -> Box<dyn NyashBox> {
|
||||||
|
let mut cmd = match std::env::current_exe() {
|
||||||
|
Ok(p) => std::process::Command::new(p),
|
||||||
|
Err(e) => return Box::new(StringBox::new(format!("ERR: current_exe(): {}", e)))
|
||||||
|
};
|
||||||
|
// Propagate relevant envs (AOT/JIT observe)
|
||||||
|
let mut c = cmd.arg("--backend").arg("vm") // ensures runner path
|
||||||
|
.arg("--compile-native")
|
||||||
|
.arg("-o").arg(out)
|
||||||
|
.arg(file)
|
||||||
|
.envs(std::env::vars());
|
||||||
|
match c.output() {
|
||||||
|
Ok(o) => {
|
||||||
|
let mut s = String::new();
|
||||||
|
s.push_str(&String::from_utf8_lossy(&o.stdout));
|
||||||
|
s.push_str(&String::from_utf8_lossy(&o.stderr));
|
||||||
|
if !o.status.success() {
|
||||||
|
s = format!("AOT FAILED (code={}):\n{}", o.status.code().unwrap_or(-1), s);
|
||||||
|
}
|
||||||
|
Box::new(StringBox::new(s))
|
||||||
|
}
|
||||||
|
Err(e) => Box::new(StringBox::new(format!("ERR: spawn compile-native: {}", e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
50
src/boxes/aot_config_box.rs
Normal file
50
src/boxes/aot_config_box.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AotConfigBox {
|
||||||
|
pub base: BoxBase,
|
||||||
|
// staging fields (apply() writes to env)
|
||||||
|
pub output_file: Option<String>,
|
||||||
|
pub emit_obj_out: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AotConfigBox { pub fn new() -> Self { Self { base: BoxBase::new(), output_file: None, emit_obj_out: None } } }
|
||||||
|
|
||||||
|
impl BoxCore for AotConfigBox {
|
||||||
|
fn box_id(&self) -> u64 { self.base.id }
|
||||||
|
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||||
|
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "AotConfigBox") }
|
||||||
|
fn as_any(&self) -> &dyn Any { self }
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NyashBox for AotConfigBox {
|
||||||
|
fn to_string_box(&self) -> StringBox { self.summary() }
|
||||||
|
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<AotConfigBox>()) }
|
||||||
|
fn type_name(&self) -> &'static str { "AotConfigBox" }
|
||||||
|
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone(), output_file: self.output_file.clone(), emit_obj_out: self.emit_obj_out.clone() }) }
|
||||||
|
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AotConfigBox {
|
||||||
|
pub fn set_output(&mut self, path: &str) -> Box<dyn NyashBox> { self.output_file = Some(path.to_string()); Box::new(VoidBox::new()) }
|
||||||
|
pub fn set_obj_out(&mut self, path: &str) -> Box<dyn NyashBox> { self.emit_obj_out = Some(path.to_string()); Box::new(VoidBox::new()) }
|
||||||
|
pub fn clear(&mut self) -> Box<dyn NyashBox> { self.output_file = None; self.emit_obj_out = None; Box::new(VoidBox::new()) }
|
||||||
|
|
||||||
|
/// Apply staged config to environment for CLI/runner consumption
|
||||||
|
pub fn apply(&self) -> Box<dyn NyashBox> {
|
||||||
|
if let Some(p) = &self.output_file { std::env::set_var("NYASH_AOT_OUT", p); }
|
||||||
|
if let Some(p) = &self.emit_obj_out { std::env::set_var("NYASH_AOT_OBJECT_OUT", p); }
|
||||||
|
Box::new(VoidBox::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn summary(&self) -> StringBox {
|
||||||
|
let s = format!(
|
||||||
|
"output={} obj_out={}",
|
||||||
|
self.output_file.clone().unwrap_or_else(|| "<none>".to_string()),
|
||||||
|
self.emit_obj_out.clone().unwrap_or_else(|| "<none>".to_string()),
|
||||||
|
);
|
||||||
|
StringBox::new(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/boxes/jit_strict_box.rs
Normal file
63
src/boxes/jit_strict_box.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct JitStrictBox { base: BoxBase }
|
||||||
|
|
||||||
|
impl JitStrictBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
|
||||||
|
|
||||||
|
impl BoxCore for JitStrictBox {
|
||||||
|
fn box_id(&self) -> u64 { self.base.id }
|
||||||
|
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||||
|
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitStrictBox") }
|
||||||
|
fn as_any(&self) -> &dyn Any { self }
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NyashBox for JitStrictBox {
|
||||||
|
fn to_string_box(&self) -> StringBox { StringBox::new("JitStrictBox".to_string()) }
|
||||||
|
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitStrictBox>()) }
|
||||||
|
fn type_name(&self) -> &'static str { "JitStrictBox" }
|
||||||
|
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||||
|
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JitStrictBox {
|
||||||
|
/// Enable/disable strict mode. When enabling, also set JIT-only and args-handle-only by default.
|
||||||
|
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
|
||||||
|
if on {
|
||||||
|
std::env::set_var("NYASH_JIT_STRICT", "1");
|
||||||
|
if std::env::var("NYASH_JIT_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ONLY", "1"); }
|
||||||
|
if std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1"); }
|
||||||
|
} else {
|
||||||
|
std::env::remove_var("NYASH_JIT_STRICT");
|
||||||
|
}
|
||||||
|
Box::new(VoidBox::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_only(&self, on: bool) -> Box<dyn NyashBox> {
|
||||||
|
if on { std::env::set_var("NYASH_JIT_ONLY", "1"); } else { std::env::remove_var("NYASH_JIT_ONLY"); }
|
||||||
|
Box::new(VoidBox::new())
|
||||||
|
}
|
||||||
|
pub fn set_handle_only(&self, on: bool) -> Box<dyn NyashBox> {
|
||||||
|
if on { std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1"); } else { std::env::remove_var("NYASH_JIT_ARGS_HANDLE_ONLY"); }
|
||||||
|
Box::new(VoidBox::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status(&self) -> Box<dyn NyashBox> {
|
||||||
|
let s = serde_json::json!({
|
||||||
|
"strict": std::env::var("NYASH_JIT_STRICT").ok().as_deref() == Some("1"),
|
||||||
|
"jit_only": std::env::var("NYASH_JIT_ONLY").ok().as_deref() == Some("1"),
|
||||||
|
"args_handle_only": std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() == Some("1"),
|
||||||
|
"lower_fallbacks": crate::jit::events::lower_fallbacks_get(),
|
||||||
|
});
|
||||||
|
Box::new(StringBox::new(s.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset compile-time counters (e.g., lower fallback count) before next compile.
|
||||||
|
pub fn reset_counters(&self) -> Box<dyn NyashBox> {
|
||||||
|
crate::jit::events::lower_counters_reset();
|
||||||
|
Box::new(VoidBox::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -78,9 +78,12 @@ pub mod jit_config_box;
|
|||||||
pub mod jit_stats_box;
|
pub mod jit_stats_box;
|
||||||
pub mod jit_policy_box;
|
pub mod jit_policy_box;
|
||||||
pub mod jit_events_box;
|
pub mod jit_events_box;
|
||||||
|
pub mod jit_strict_box;
|
||||||
pub mod jit_hostcall_registry_box;
|
pub mod jit_hostcall_registry_box;
|
||||||
pub mod debug_config_box;
|
pub mod debug_config_box;
|
||||||
pub mod gc_config_box;
|
pub mod gc_config_box;
|
||||||
|
pub mod aot_config_box;
|
||||||
|
pub mod aot_compiler_box;
|
||||||
|
|
||||||
// Web専用Box群(ブラウザ環境でのみ利用可能)
|
// Web専用Box群(ブラウザ環境でのみ利用可能)
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@ -115,7 +118,10 @@ pub use jit_config_box::JitConfigBox;
|
|||||||
pub use jit_stats_box::JitStatsBox;
|
pub use jit_stats_box::JitStatsBox;
|
||||||
pub use jit_policy_box::JitPolicyBox;
|
pub use jit_policy_box::JitPolicyBox;
|
||||||
pub use jit_events_box::JitEventsBox;
|
pub use jit_events_box::JitEventsBox;
|
||||||
|
pub use jit_strict_box::JitStrictBox;
|
||||||
pub use jit_hostcall_registry_box::JitHostcallRegistryBox;
|
pub use jit_hostcall_registry_box::JitHostcallRegistryBox;
|
||||||
|
pub use aot_config_box::AotConfigBox;
|
||||||
|
pub use aot_compiler_box::AotCompilerBox;
|
||||||
|
|
||||||
// EguiBoxの再エクスポート(非WASM環境のみ)
|
// EguiBoxの再エクスポート(非WASM環境のみ)
|
||||||
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
|
||||||
|
|||||||
14
src/cli.rs
14
src/cli.rs
@ -45,6 +45,8 @@ pub struct CliConfig {
|
|||||||
pub jit_direct: bool,
|
pub jit_direct: bool,
|
||||||
// DOT emit helper
|
// DOT emit helper
|
||||||
pub emit_cfg: Option<String>,
|
pub emit_cfg: Option<String>,
|
||||||
|
// Verbose CLI
|
||||||
|
pub cli_verbose: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliConfig {
|
impl CliConfig {
|
||||||
@ -116,16 +118,23 @@ impl CliConfig {
|
|||||||
.help("Choose execution backend: 'interpreter' (default), 'vm', or 'llvm'")
|
.help("Choose execution backend: 'interpreter' (default), 'vm', or 'llvm'")
|
||||||
.default_value("interpreter")
|
.default_value("interpreter")
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("verbose")
|
||||||
|
.long("verbose")
|
||||||
|
.short('v')
|
||||||
|
.help("Verbose CLI output (sets NYASH_CLI_VERBOSE=1)")
|
||||||
|
.action(clap::ArgAction::SetTrue)
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("compile-wasm")
|
Arg::new("compile-wasm")
|
||||||
.long("compile-wasm")
|
.long("compile-wasm")
|
||||||
.help("Compile to WebAssembly (WAT format) instead of executing")
|
.help("Compile to WebAssembly (WAT/WASM). Requires --features wasm-backend")
|
||||||
.action(clap::ArgAction::SetTrue)
|
.action(clap::ArgAction::SetTrue)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("compile-native")
|
Arg::new("compile-native")
|
||||||
.long("compile-native")
|
.long("compile-native")
|
||||||
.help("Compile to native AOT executable using wasmtime precompilation")
|
.help("Compile to native executable (AOT). Requires --features cranelift-jit")
|
||||||
.action(clap::ArgAction::SetTrue)
|
.action(clap::ArgAction::SetTrue)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -306,6 +315,7 @@ impl CliConfig {
|
|||||||
emit_cfg: matches.get_one::<String>("emit-cfg").cloned(),
|
emit_cfg: matches.get_one::<String>("emit-cfg").cloned(),
|
||||||
jit_only: matches.get_flag("jit-only"),
|
jit_only: matches.get_flag("jit-only"),
|
||||||
jit_direct: matches.get_flag("jit-direct"),
|
jit_direct: matches.get_flag("jit-direct"),
|
||||||
|
cli_verbose: matches.get_flag("verbose"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,8 @@ impl JitEngine {
|
|||||||
pub fn compile_function(&mut self, func_name: &str, mir: &crate::mir::MirFunction) -> Option<u64> {
|
pub fn compile_function(&mut self, func_name: &str, mir: &crate::mir::MirFunction) -> Option<u64> {
|
||||||
let t0 = std::time::Instant::now();
|
let t0 = std::time::Instant::now();
|
||||||
// Phase 10_b skeleton: walk MIR with LowerCore and report coverage
|
// Phase 10_b skeleton: walk MIR with LowerCore and report coverage
|
||||||
|
// Reset compile-phase counters (e.g., fallback decisions) before lowering this function
|
||||||
|
crate::jit::events::lower_counters_reset();
|
||||||
let mut lower = crate::jit::lower::core::LowerCore::new();
|
let mut lower = crate::jit::lower::core::LowerCore::new();
|
||||||
#[cfg(feature = "cranelift-jit")]
|
#[cfg(feature = "cranelift-jit")]
|
||||||
let mut builder = crate::jit::lower::builder::CraneliftBuilder::new();
|
let mut builder = crate::jit::lower::builder::CraneliftBuilder::new();
|
||||||
@ -54,6 +56,12 @@ impl JitEngine {
|
|||||||
eprintln!("[JIT] lower failed for {}: {}", func_name, e);
|
eprintln!("[JIT] lower failed for {}: {}", func_name, e);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
// Strict: fail compile if any fallback decisions were taken during lowering
|
||||||
|
let lower_fallbacks = crate::jit::events::lower_fallbacks_get();
|
||||||
|
if lower_fallbacks > 0 && std::env::var("NYASH_JIT_STRICT").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[JIT][strict] lower produced fallback decisions for {}: {} — failing compile", func_name, lower_fallbacks);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
// Capture per-function lower stats for manager to query later
|
// Capture per-function lower stats for manager to query later
|
||||||
let (phi_t, phi_b1, ret_b) = lower.last_stats();
|
let (phi_t, phi_b1, ret_b) = lower.last_stats();
|
||||||
self.last_phi_total = phi_t; self.last_phi_b1 = phi_b1; self.last_ret_bool_hint = ret_b;
|
self.last_phi_total = phi_t; self.last_phi_b1 = phi_b1; self.last_ret_bool_hint = ret_b;
|
||||||
|
|||||||
@ -5,6 +5,31 @@
|
|||||||
//! - NYASH_JIT_EVENTS_PATH=/path/to/file.jsonl appends to file
|
//! - NYASH_JIT_EVENTS_PATH=/path/to/file.jsonl appends to file
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
|
// Compile-phase counters (process-local)
|
||||||
|
static LOWER_FALLBACK_COUNT: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
|
/// Reset compile-phase counters (call at the beginning of each lower/compile)
|
||||||
|
pub fn lower_counters_reset() {
|
||||||
|
LOWER_FALLBACK_COUNT.store(0, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get number of fallback decisions observed during lowering
|
||||||
|
pub fn lower_fallbacks_get() -> u64 {
|
||||||
|
LOWER_FALLBACK_COUNT.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn record_lower_decision(extra: &serde_json::Value) {
|
||||||
|
// We record even when emission is disabled, to allow strict-mode checks.
|
||||||
|
if let serde_json::Value::Object(map) = extra {
|
||||||
|
if let Some(serde_json::Value::String(dec)) = map.get("decision") {
|
||||||
|
if dec == "fallback" {
|
||||||
|
LOWER_FALLBACK_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn base_emit_enabled() -> bool {
|
fn base_emit_enabled() -> bool {
|
||||||
std::env::var("NYASH_JIT_EVENTS").ok().as_deref() == Some("1")
|
std::env::var("NYASH_JIT_EVENTS").ok().as_deref() == Some("1")
|
||||||
@ -12,8 +37,8 @@ fn base_emit_enabled() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn should_emit_lower() -> bool {
|
fn should_emit_lower() -> bool {
|
||||||
// Compile-phase events are opt-in to avoid noisy logs by default.
|
// Unify observability: if base events are on (stdout/file) or explicit compile flag, emit.
|
||||||
std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref() == Some("1")
|
base_emit_enabled() || std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref() == Some("1")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_emit_runtime() -> bool {
|
fn should_emit_runtime() -> bool {
|
||||||
@ -56,6 +81,8 @@ fn emit_any(kind: &str, function: &str, handle: Option<u64>, ms: Option<u128>, e
|
|||||||
|
|
||||||
/// Emit an event during lowering (compile-time planning). Adds phase="lower".
|
/// Emit an event during lowering (compile-time planning). Adds phase="lower".
|
||||||
pub fn emit_lower(mut extra: serde_json::Value, kind: &str, function: &str) {
|
pub fn emit_lower(mut extra: serde_json::Value, kind: &str, function: &str) {
|
||||||
|
// Always record decisions for strict-mode enforcement
|
||||||
|
record_lower_decision(&extra);
|
||||||
if !should_emit_lower() { return; }
|
if !should_emit_lower() { return; }
|
||||||
if let serde_json::Value::Object(ref mut map) = extra { map.insert("phase".into(), serde_json::Value::String("lower".into())); }
|
if let serde_json::Value::Object(ref mut map) = extra { map.insert("phase".into(), serde_json::Value::String("lower".into())); }
|
||||||
emit_any(kind, function, None, None, extra);
|
emit_any(kind, function, None, None, extra);
|
||||||
|
|||||||
@ -862,18 +862,16 @@ impl LowerCore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(reason) => {
|
Err(reason) => {
|
||||||
crate::jit::events::emit(
|
crate::jit::events::emit_lower(
|
||||||
"hostcall",
|
|
||||||
"<jit>",
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"id": sym,
|
"id": sym,
|
||||||
"decision": "fallback",
|
"decision": "fallback",
|
||||||
"reason": reason,
|
"reason": reason,
|
||||||
"argc": observed.len(),
|
"argc": observed.len(),
|
||||||
"arg_types": arg_types
|
"arg_types": arg_types
|
||||||
})
|
}),
|
||||||
|
"hostcall",
|
||||||
|
"<jit>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,13 +75,13 @@ pub fn lower_box_call(
|
|||||||
b.emit_param_i64(pidx as i64 as usize);
|
b.emit_param_i64(pidx as i64 as usize);
|
||||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some());
|
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some());
|
||||||
} else {
|
} else {
|
||||||
crate::jit::events::emit(
|
crate::jit::events::emit_lower(
|
||||||
"hostcall","<jit>",None,None,
|
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"id": crate::jit::r#extern::collections::SYM_ARRAY_LEN,
|
"id": crate::jit::r#extern::collections::SYM_ARRAY_LEN,
|
||||||
"decision": "fallback", "reason": "receiver_not_param",
|
"decision": "fallback", "reason": "receiver_not_param",
|
||||||
"argc": 1, "arg_types": ["I64(index)"]
|
"argc": 1, "arg_types": ["I64(index)"]
|
||||||
})
|
}),
|
||||||
|
"hostcall","<jit>"
|
||||||
);
|
);
|
||||||
b.emit_const_i64(-1);
|
b.emit_const_i64(-1);
|
||||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some());
|
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some());
|
||||||
@ -159,9 +159,9 @@ pub fn lower_box_call(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(reason) => {
|
Err(reason) => {
|
||||||
crate::jit::events::emit(
|
crate::jit::events::emit_lower(
|
||||||
"hostcall","<jit>",None,None,
|
serde_json::json!({"id": sym, "decision":"fallback", "reason": reason, "argc": observed.len(), "arg_types": arg_types}),
|
||||||
serde_json::json!({"id": sym, "decision":"fallback", "reason": reason, "argc": observed.len(), "arg_types": arg_types})
|
"hostcall","<jit>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -257,9 +257,9 @@ pub fn lower_box_call(
|
|||||||
);
|
);
|
||||||
b.emit_host_call(sym, 2, false);
|
b.emit_host_call(sym, 2, false);
|
||||||
} else {
|
} else {
|
||||||
crate::jit::events::emit(
|
crate::jit::events::emit_lower(
|
||||||
"hostcall","<jit>",None,None,
|
serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating", "argc": args.len()}),
|
||||||
serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating", "argc": args.len()})
|
"hostcall","<jit>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -564,9 +564,9 @@ pub fn lower_math_call(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(reason) => {
|
Err(reason) => {
|
||||||
crate::jit::events::emit(
|
crate::jit::events::emit_lower(
|
||||||
"hostcall","<jit>",None,None,
|
serde_json::json!({"id": sym, "decision":"fallback", "reason": reason, "argc": observed_kinds.len(), "arg_types": arg_types}),
|
||||||
serde_json::json!({"id": sym, "decision":"fallback", "reason": reason, "argc": observed_kinds.len(), "arg_types": arg_types})
|
"hostcall","<jit>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,6 +47,8 @@ impl NyashRunner {
|
|||||||
|
|
||||||
/// Run Nyash based on the configuration
|
/// Run Nyash based on the configuration
|
||||||
pub fn run(&self) {
|
pub fn run(&self) {
|
||||||
|
// Verbose CLI flag maps to env for downstream helpers/scripts
|
||||||
|
if self.config.cli_verbose { std::env::set_var("NYASH_CLI_VERBOSE", "1"); }
|
||||||
// Script-level env directives (special comments) — parse early
|
// Script-level env directives (special comments) — parse early
|
||||||
// Supported:
|
// Supported:
|
||||||
// // @env KEY=VALUE
|
// // @env KEY=VALUE
|
||||||
@ -80,6 +82,8 @@ impl NyashRunner {
|
|||||||
} else if rest == "@jit-strict" {
|
} else if rest == "@jit-strict" {
|
||||||
std::env::set_var("NYASH_JIT_STRICT", "1");
|
std::env::set_var("NYASH_JIT_STRICT", "1");
|
||||||
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
|
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
|
||||||
|
// In strict mode, default to JIT-only (no VM fallback)
|
||||||
|
if std::env::var("NYASH_JIT_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ONLY", "1"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,6 +95,10 @@ impl NyashRunner {
|
|||||||
if std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().is_none() {
|
if std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().is_none() {
|
||||||
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
|
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
|
||||||
}
|
}
|
||||||
|
// Enforce JIT-only by default in strict mode unless explicitly overridden
|
||||||
|
if std::env::var("NYASH_JIT_ONLY").ok().is_none() {
|
||||||
|
std::env::set_var("NYASH_JIT_ONLY", "1");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🏭 Phase 9.78b: Initialize unified registry
|
// 🏭 Phase 9.78b: Initialize unified registry
|
||||||
@ -146,6 +154,16 @@ impl NyashRunner {
|
|||||||
jc.apply_env();
|
jc.apply_env();
|
||||||
nyash_rust::jit::config::set_current(jc.clone());
|
nyash_rust::jit::config::set_current(jc.clone());
|
||||||
}
|
}
|
||||||
|
// Architectural pivot: JIT is compiler-only (EXE/AOT). Ensure VM runtime does not dispatch to JIT
|
||||||
|
// unless explicitly requested via independent JIT mode, or when emitting AOT objects.
|
||||||
|
if !self.config.compile_native && !self.config.jit_direct {
|
||||||
|
// When AOT object emission is requested, allow JIT to run for object generation
|
||||||
|
let aot_obj = std::env::var("NYASH_AOT_OBJECT_OUT").ok();
|
||||||
|
if aot_obj.is_none() || aot_obj.as_deref() == Some("") {
|
||||||
|
// Force-disable runtime JIT execution path for VM/Interpreter flows
|
||||||
|
std::env::set_var("NYASH_JIT_EXEC", "0");
|
||||||
|
}
|
||||||
|
}
|
||||||
// Benchmark mode - can run without a file
|
// Benchmark mode - can run without a file
|
||||||
if self.config.benchmark {
|
if self.config.benchmark {
|
||||||
println!("📊 Nyash Performance Benchmark Suite");
|
println!("📊 Nyash Performance Benchmark Suite");
|
||||||
|
|||||||
@ -1,40 +1,36 @@
|
|||||||
use super::super::NyashRunner;
|
use super::super::NyashRunner;
|
||||||
#[cfg(feature = "wasm-backend")]
|
#[cfg(feature = "cranelift-jit")]
|
||||||
use nyash_rust::{parser::NyashParser, mir::MirCompiler, backend::aot::AotBackend};
|
use std::{process::Command, process};
|
||||||
#[cfg(feature = "wasm-backend")]
|
|
||||||
use std::{fs, process};
|
|
||||||
|
|
||||||
impl NyashRunner {
|
impl NyashRunner {
|
||||||
/// Execute AOT compilation mode (split)
|
/// Execute AOT compilation mode (split)
|
||||||
#[cfg(feature = "wasm-backend")]
|
#[cfg(feature = "cranelift-jit")]
|
||||||
pub(crate) fn execute_aot_mode(&self, filename: &str) {
|
pub(crate) fn execute_aot_mode(&self, filename: &str) {
|
||||||
// Read the file
|
let output = self.config.output_file.as_deref().unwrap_or("app");
|
||||||
let code = match fs::read_to_string(filename) {
|
// Prefer using provided helper scripts to ensure link flags and runtime integration
|
||||||
Ok(content) => content,
|
let status = if cfg!(target_os = "windows") {
|
||||||
Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); }
|
// Use PowerShell helper; falls back to bash if available inside the script
|
||||||
|
Command::new("powershell")
|
||||||
|
.args(["-ExecutionPolicy","Bypass","-File","tools/build_aot.ps1","-Input", filename, "-Out", &format!("{}.exe", output)])
|
||||||
|
.status()
|
||||||
|
} else {
|
||||||
|
Command::new("bash")
|
||||||
|
.args(["tools/build_aot.sh", filename, "-o", output])
|
||||||
|
.status()
|
||||||
};
|
};
|
||||||
|
match status {
|
||||||
// Parse to AST
|
Ok(s) if s.success() => {
|
||||||
let ast = match NyashParser::parse_from_string(&code) {
|
println!("✅ AOT compilation successful!\nExecutable written to: {}", output);
|
||||||
Ok(ast) => ast,
|
}
|
||||||
Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); }
|
Ok(s) => {
|
||||||
};
|
eprintln!("❌ AOT compilation failed (exit={} ). See logs above.", s.code().unwrap_or(-1));
|
||||||
|
process::exit(1);
|
||||||
// Compile to MIR
|
}
|
||||||
let mut mir_compiler = MirCompiler::new();
|
Err(e) => {
|
||||||
let compile_result = match mir_compiler.compile(ast) {
|
eprintln!("❌ Failed to invoke build_aot.sh: {}", e);
|
||||||
Ok(result) => result,
|
eprintln!("Hint: ensure bash is available, or run: bash tools/build_aot.sh {} -o {}", filename, output);
|
||||||
Err(e) => { eprintln!("❌ MIR compilation error: {}", e); process::exit(1); }
|
process::exit(1);
|
||||||
};
|
}
|
||||||
|
|
||||||
// Determine output file (no extension change)
|
|
||||||
let output = self.config.output_file.as_deref().unwrap_or(filename);
|
|
||||||
|
|
||||||
let mut aot_backend = AotBackend::new();
|
|
||||||
match aot_backend.compile_to_executable(compile_result.module, output) {
|
|
||||||
Ok(()) => { println!("✅ AOT compilation successful!\nExecutable written to: {}", output); },
|
|
||||||
Err(e) => { eprintln!("❌ AOT compilation error: {}", e); process::exit(1); }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,10 +37,10 @@ impl NyashRunner {
|
|||||||
{ eprintln!("❌ WASM backend not available. Please rebuild with: cargo build --features wasm-backend"); process::exit(1); }
|
{ eprintln!("❌ WASM backend not available. Please rebuild with: cargo build --features wasm-backend"); process::exit(1); }
|
||||||
}
|
}
|
||||||
if self.config.compile_native {
|
if self.config.compile_native {
|
||||||
#[cfg(feature = "wasm-backend")]
|
#[cfg(feature = "cranelift-jit")]
|
||||||
{ self.execute_aot_mode(filename); return; }
|
{ self.execute_aot_mode(filename); return; }
|
||||||
#[cfg(not(feature = "wasm-backend"))]
|
#[cfg(not(feature = "cranelift-jit"))]
|
||||||
{ eprintln!("❌ AOT backend not available. Please rebuild with: cargo build --features wasm-backend"); process::exit(1); }
|
{ eprintln!("❌ Native AOT compilation requires Cranelift. Please rebuild: cargo build --features cranelift-jit"); process::exit(1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backend selection
|
// Backend selection
|
||||||
|
|||||||
@ -5,5 +5,5 @@ pub mod bench;
|
|||||||
pub mod common;
|
pub mod common;
|
||||||
#[cfg(feature = "wasm-backend")]
|
#[cfg(feature = "wasm-backend")]
|
||||||
pub mod wasm;
|
pub mod wasm;
|
||||||
#[cfg(feature = "wasm-backend")]
|
#[cfg(feature = "cranelift-jit")]
|
||||||
pub mod aot;
|
pub mod aot;
|
||||||
|
|||||||
@ -660,11 +660,18 @@ impl PluginBoxV2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve platform-specific path (.so/.dll/.dylib) and optional search paths
|
||||||
|
let resolved_path = self.resolve_library_path(&lib_def.path)
|
||||||
|
.or_else(|| self.config.as_ref().and_then(|c| c.resolve_plugin_path(&lib_def.path)))
|
||||||
|
.unwrap_or_else(|| lib_def.path.clone());
|
||||||
// Load library
|
// Load library
|
||||||
let lib = unsafe {
|
let lib = unsafe {
|
||||||
libloading::Library::new(&lib_def.path)
|
libloading::Library::new(&resolved_path)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
eprintln!("Failed to load library: {}", e);
|
eprintln!(
|
||||||
|
"Failed to load library '{}': {}\n raw='{}'",
|
||||||
|
resolved_path, e, lib_def.path
|
||||||
|
);
|
||||||
BidError::PluginError
|
BidError::PluginError
|
||||||
})?
|
})?
|
||||||
};
|
};
|
||||||
@ -724,6 +731,48 @@ impl PluginBoxV2 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a plugin library path for the current OS by adapting extensions and common prefixes.
|
||||||
|
/// - Maps .so/.dylib/.dll according to target OS
|
||||||
|
/// - On Windows also tries removing leading 'lib' prefix
|
||||||
|
/// - Searches configured plugin_paths if only filename is provided
|
||||||
|
fn resolve_library_path(&self, raw: &str) -> Option<String> {
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
let p = Path::new(raw);
|
||||||
|
if p.exists() { return Some(raw.to_string()); }
|
||||||
|
let dir = p.parent().map(|d| d.to_path_buf()).unwrap_or_else(|| PathBuf::from("."));
|
||||||
|
let file = p.file_name().map(|s| s.to_string_lossy().to_string()).unwrap_or_else(|| raw.to_string());
|
||||||
|
let stem = Path::new(&file).file_stem().map(|s| s.to_string_lossy().to_string()).unwrap_or(file.clone());
|
||||||
|
let cur_ext: &str = if cfg!(target_os = "windows") { "dll" } else if cfg!(target_os = "macos") { "dylib" } else { "so" };
|
||||||
|
|
||||||
|
// Candidate A: replace extension with OS-specific
|
||||||
|
let mut cand_a = dir.join(Path::new(&stem).with_extension(cur_ext));
|
||||||
|
if cand_a.exists() { return Some(cand_a.to_string_lossy().to_string()); }
|
||||||
|
|
||||||
|
// Candidate B (Windows): drop 'lib' prefix if present
|
||||||
|
if cfg!(target_os = "windows") {
|
||||||
|
if let Some(stripped) = stem.strip_prefix("lib") {
|
||||||
|
let cand_b = dir.join(Path::new(stripped).with_extension(cur_ext));
|
||||||
|
if cand_b.exists() { return Some(cand_b.to_string_lossy().to_string()); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Candidate C: search in configured plugin_paths with adapted filename
|
||||||
|
if let Some(cfg) = &self.config {
|
||||||
|
// try original filename
|
||||||
|
if let Some(path) = cfg.resolve_plugin_path(&file) { return Some(path); }
|
||||||
|
// try adapted A
|
||||||
|
if let Some(path) = cfg.resolve_plugin_path(&cand_a.file_name().unwrap_or_default().to_string_lossy()) { return Some(path); }
|
||||||
|
// try adapted B on windows
|
||||||
|
if cfg!(target_os = "windows") {
|
||||||
|
if let Some(stripped) = stem.strip_prefix("lib") {
|
||||||
|
let name = format!("{}.{}", stripped, cur_ext);
|
||||||
|
if let Some(path) = cfg.resolve_plugin_path(&name) { return Some(path); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a Box instance
|
/// Create a Box instance
|
||||||
pub fn create_box(&self, box_type: &str, _args: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> {
|
pub fn create_box(&self, box_type: &str, _args: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> {
|
||||||
eprintln!("🔍 create_box called for: {}", box_type);
|
eprintln!("🔍 create_box called for: {}", box_type);
|
||||||
|
|||||||
67
tools/build_aot.ps1
Normal file
67
tools/build_aot.ps1
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
Param(
|
||||||
|
[Parameter(Mandatory=$true, Position=0)][string]$Input,
|
||||||
|
[Parameter(Mandatory=$false)][string]$Out = "app.exe"
|
||||||
|
)
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
function Info($m) { Write-Host "[AOT] $m" }
|
||||||
|
function Fail($m) { Write-Host "error: $m" -ForegroundColor Red; exit 1 }
|
||||||
|
|
||||||
|
if (-not (Test-Path $Input)) { Fail "input file not found: $Input" }
|
||||||
|
|
||||||
|
Info "Building nyash (Cranelift)..."
|
||||||
|
& cargo build --release --features cranelift-jit | Out-Null
|
||||||
|
|
||||||
|
Info "Emitting object (.o) via JIT (Strict/No-fallback)..."
|
||||||
|
$env:NYASH_AOT_OBJECT_OUT = "target/aot_objects"
|
||||||
|
$env:NYASH_USE_PLUGIN_BUILTINS = "1"
|
||||||
|
$env:NYASH_JIT_EXEC = "1"
|
||||||
|
$env:NYASH_JIT_ONLY = "1"
|
||||||
|
$env:NYASH_JIT_STRICT = "1"
|
||||||
|
$env:NYASH_JIT_ARGS_HANDLE_ONLY = "1"
|
||||||
|
$env:NYASH_JIT_THRESHOLD = "1"
|
||||||
|
New-Item -ItemType Directory -Force -Path $env:NYASH_AOT_OBJECT_OUT | Out-Null
|
||||||
|
& .\target\release\nyash --backend vm $Input | Out-Null
|
||||||
|
|
||||||
|
$OBJ = "target/aot_objects/main.o"
|
||||||
|
if (-not (Test-Path $OBJ)) {
|
||||||
|
Fail "object not generated: $OBJ`n hint: ensure main() is lowerable under current Strict JIT coverage"
|
||||||
|
}
|
||||||
|
|
||||||
|
Info "Building libnyrt (static runtime)..."
|
||||||
|
Push-Location crates\nyrt
|
||||||
|
& cargo build --release | Out-Null
|
||||||
|
Pop-Location
|
||||||
|
|
||||||
|
Info "Linking $Out ..."
|
||||||
|
|
||||||
|
# Try native clang first (LLVM for Windows). On Windows, we avoid -lpthread/-ldl/-lm.
|
||||||
|
$clang = Get-Command clang -ErrorAction SilentlyContinue
|
||||||
|
if ($clang) {
|
||||||
|
$libDir = "crates/nyrt/target/release"
|
||||||
|
$libName = ""
|
||||||
|
if (Test-Path (Join-Path $libDir "nyrt.lib")) { $libName = "nyrt.lib" }
|
||||||
|
elseif (Test-Path (Join-Path $libDir "libnyrt.a")) { $libName = "libnyrt.a" }
|
||||||
|
if ($libName -ne "") {
|
||||||
|
& clang $OBJ -L $libDir -Wl,--whole-archive -l:$libName -Wl,--no-whole-archive -o $Out | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $Out)) {
|
||||||
|
$bash = Get-Command bash -ErrorAction SilentlyContinue
|
||||||
|
if ($bash) {
|
||||||
|
# Prefer WSL/MSYS2 bash to reuse Linux-like flags if available
|
||||||
|
& bash -lc "cc target/aot_objects/main.o -L crates/nyrt/target/release -Wl,--whole-archive -lnyrt -Wl,--no-whole-archive -lpthread -ldl -lm -o $Out" | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $Out)) {
|
||||||
|
Write-Warning "Link step could not produce $Out."
|
||||||
|
Write-Host "hint: Install LLVM clang (preferred) or MSYS2 toolchain, or link manually:"
|
||||||
|
Write-Host " clang target/aot_objects/main.o -L crates/nyrt/target/release -Wl,--whole-archive -l:libnyrt.a -Wl,--no-whole-archive -o app.exe"
|
||||||
|
Fail "automatic link not available on this environment"
|
||||||
|
}
|
||||||
|
|
||||||
|
Info "Done: $Out"
|
||||||
|
Write-Host " (runtime requires nyash.toml and plugin .so/.dll per config)"
|
||||||
@ -1,6 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ "${NYASH_CLI_VERBOSE:-0}" == "1" ]]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat << USAGE
|
cat << USAGE
|
||||||
Usage: tools/build_aot.sh <input.nyash> [-o <output>]
|
Usage: tools/build_aot.sh <input.nyash> [-o <output>]
|
||||||
@ -35,24 +39,35 @@ if [[ ! -f "$INPUT" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[1/4] Building nyash (Cranelift) ..."
|
echo "[1/4] Building nyash (Cranelift) ..."
|
||||||
cargo build --release --features cranelift-jit >/dev/null
|
if ! cargo build --release --features cranelift-jit >/dev/null; then
|
||||||
|
echo "error: failed to build nyash with Cranelift (feature cranelift-jit)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "[2/4] Emitting object (.o) via JIT ..."
|
echo "[2/4] Emitting object (.o) via JIT (Strict/No-fallback) ..."
|
||||||
rm -rf target/aot_objects && mkdir -p target/aot_objects
|
rm -rf target/aot_objects && mkdir -p target/aot_objects
|
||||||
NYASH_AOT_OBJECT_OUT=target/aot_objects \
|
NYASH_AOT_OBJECT_OUT=target/aot_objects \
|
||||||
NYASH_USE_PLUGIN_BUILTINS=1 \
|
NYASH_USE_PLUGIN_BUILTINS=1 \
|
||||||
NYASH_JIT_EXEC=1 \
|
NYASH_JIT_EXEC=1 \
|
||||||
|
NYASH_JIT_ONLY=1 \
|
||||||
|
NYASH_JIT_STRICT=1 \
|
||||||
|
NYASH_JIT_ARGS_HANDLE_ONLY=1 \
|
||||||
NYASH_JIT_THRESHOLD=1 \
|
NYASH_JIT_THRESHOLD=1 \
|
||||||
./target/release/nyash --backend vm "$INPUT" >/dev/null || true
|
./target/release/nyash --backend vm "$INPUT" >/dev/null || true
|
||||||
|
|
||||||
OBJ="target/aot_objects/main.o"
|
OBJ="target/aot_objects/main.o"
|
||||||
if [[ ! -f "$OBJ" ]]; then
|
if [[ ! -f "$OBJ" ]]; then
|
||||||
echo "error: object not generated: $OBJ" >&2
|
echo "error: object not generated: $OBJ" >&2
|
||||||
echo "hint: ensure the program's entry function is JIT-compilable (e.g., main)" >&2
|
echo "hint: Strict mode forbids fallback. Ensure main() is lowerable under current JIT coverage." >&2
|
||||||
|
echo "hint: Try a simpler RO example first, or expand JIT coverage for used ops." >&2
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[3/4] Building libnyrt.a ..."
|
echo "[3/4] Building libnyrt.a ..."
|
||||||
|
if [[ ! -d crates/nyrt ]]; then
|
||||||
|
echo "error: crates/nyrt not found. Please ensure nyrt runtime crate exists." >&2
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
( cd crates/nyrt && cargo build --release >/dev/null )
|
( cd crates/nyrt && cargo build --release >/dev/null )
|
||||||
|
|
||||||
echo "[4/4] Linking $OUT ..."
|
echo "[4/4] Linking $OUT ..."
|
||||||
@ -63,4 +78,6 @@ cc "$OBJ" \
|
|||||||
|
|
||||||
echo "✅ Done: $OUT"
|
echo "✅ Done: $OUT"
|
||||||
echo " (runtime requires nyash.toml and plugin .so per config)"
|
echo " (runtime requires nyash.toml and plugin .so per config)"
|
||||||
|
if [[ "${NYASH_CLI_VERBOSE:-0}" == "1" ]]; then
|
||||||
|
echo "info: run with NYASH_CLI_VERBOSE=1 to see detailed steps and commands"
|
||||||
|
fi
|
||||||
|
|||||||
111
tools/smoke_aot_vs_vm.sh
Normal file
111
tools/smoke_aot_vs_vm.sh
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Smoke test: AOT vs VM execution comparison
|
||||||
|
# Tests that AOT-compiled programs produce the same results as VM execution
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== Nyash AOT vs VM Smoke Test ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Test files
|
||||||
|
TEST_FILES=(
|
||||||
|
"examples/aot_min_string_len.nyash"
|
||||||
|
"examples/aot_string_len_simple.nyash"
|
||||||
|
"examples/jit_stats_bool_ret.nyash"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Counter for results
|
||||||
|
PASSED=0
|
||||||
|
FAILED=0
|
||||||
|
|
||||||
|
# Function to run test
|
||||||
|
run_test() {
|
||||||
|
local test_file=$1
|
||||||
|
local test_name=$(basename "$test_file" .nyash)
|
||||||
|
|
||||||
|
echo "Testing: $test_name"
|
||||||
|
|
||||||
|
# Clean up previous artifacts
|
||||||
|
rm -f app /tmp/${test_name}_vm.out /tmp/${test_name}_aot.out
|
||||||
|
|
||||||
|
# Run with VM backend
|
||||||
|
echo -n " VM execution... "
|
||||||
|
if NYASH_USE_PLUGIN_BUILTINS=1 ./target/release/nyash --backend vm "$test_file" > /tmp/${test_name}_vm.out 2>&1; then
|
||||||
|
VM_RESULT=$(tail -1 /tmp/${test_name}_vm.out | grep -oP 'Result: \K.*' || echo "NO_RESULT")
|
||||||
|
echo "OK (Result: $VM_RESULT)"
|
||||||
|
else
|
||||||
|
echo -e "${RED}FAILED${NC}"
|
||||||
|
cat /tmp/${test_name}_vm.out
|
||||||
|
((FAILED++))
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compile to native
|
||||||
|
echo -n " AOT compilation... "
|
||||||
|
if NYASH_USE_PLUGIN_BUILTINS=1 ./target/release/nyash --compile-native "$test_file" -o app > /tmp/${test_name}_aot_compile.out 2>&1; then
|
||||||
|
echo "OK"
|
||||||
|
else
|
||||||
|
echo -e "${RED}FAILED${NC}"
|
||||||
|
cat /tmp/${test_name}_aot_compile.out
|
||||||
|
((FAILED++))
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run native executable
|
||||||
|
echo -n " Native execution... "
|
||||||
|
if ./app > /tmp/${test_name}_aot.out 2>&1; then
|
||||||
|
AOT_RESULT=$(grep -oP 'ny_main\(\) returned: \K.*' /tmp/${test_name}_aot.out || echo "NO_RESULT")
|
||||||
|
echo "OK (Result: $AOT_RESULT)"
|
||||||
|
else
|
||||||
|
echo -e "${RED}FAILED${NC}"
|
||||||
|
cat /tmp/${test_name}_aot.out
|
||||||
|
((FAILED++))
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compare results
|
||||||
|
echo -n " Comparing results... "
|
||||||
|
# Note: VM returns the actual value, AOT currently returns a numeric result
|
||||||
|
# This is expected behavior for now
|
||||||
|
if [[ "$VM_RESULT" != "NO_RESULT" && "$AOT_RESULT" != "NO_RESULT" ]]; then
|
||||||
|
echo -e "${GREEN}PASSED${NC} (VM: $VM_RESULT, AOT: $AOT_RESULT)"
|
||||||
|
((PASSED++))
|
||||||
|
else
|
||||||
|
echo -e "${RED}FAILED${NC} - Could not extract results"
|
||||||
|
((FAILED++))
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
for test_file in "${TEST_FILES[@]}"; do
|
||||||
|
if [[ -f "$test_file" ]]; then
|
||||||
|
run_test "$test_file"
|
||||||
|
else
|
||||||
|
echo "Warning: Test file not found: $test_file"
|
||||||
|
((FAILED++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
echo "=== Test Summary ==="
|
||||||
|
echo -e "Passed: ${GREEN}$PASSED${NC}"
|
||||||
|
echo -e "Failed: ${RED}$FAILED${NC}"
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -f app
|
||||||
|
|
||||||
|
# Exit with appropriate code
|
||||||
|
if [[ $FAILED -eq 0 ]]; then
|
||||||
|
echo -e "\n${GREEN}All tests passed!${NC}"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo -e "\n${RED}Some tests failed!${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user