Phase 11-12: LLVM backend initial, semantics layer, plugin unification
Major changes: - LLVM backend initial implementation (compiler.rs, llvm mode) - Semantics layer integration in interpreter (operators.rs) - Phase 12 plugin architecture revision (3-layer system) - Builtin box removal preparation - MIR instruction set documentation (26→Core-15 migration) - Cross-backend testing infrastructure - Await/nowait syntax support New features: - LLVM AOT compilation support (--backend llvm) - Semantics layer for interpreter→VM flow - Tri-backend smoke tests - Plugin-only registry mode Bug fixes: - Interpreter plugin box arithmetic operations - Branch test returns incorrect values Documentation: - Phase 12 README.md updated with new plugin architecture - Removed obsolete NYIR proposals - Added LLVM test programs documentation Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
58
.github/workflows/llvm-aot-smoke.yml
vendored
Normal file
58
.github/workflows/llvm-aot-smoke.yml
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
name: LLVM AOT Smoke
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'crates/**'
|
||||
- 'apps/**'
|
||||
- 'tools/**'
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- '.github/workflows/llvm-aot-smoke.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'crates/**'
|
||||
- 'apps/**'
|
||||
- 'tools/**'
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
|
||||
jobs:
|
||||
llvm-aot:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
NYASH_CLI_VERBOSE: '1'
|
||||
NYASH_DISABLE_PLUGINS: '1'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust (stable)
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Cache cargo registry and build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-
|
||||
|
||||
- name: Install LLVM 18 (llvm-config-18)
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y curl ca-certificates lsb-release wget gnupg
|
||||
curl -fsSL https://apt.llvm.org/llvm.sh -o llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 18
|
||||
llvm-config-18 --version
|
||||
|
||||
- name: Run LLVM AOT smokes
|
||||
run: bash tools/llvm_smoke.sh
|
||||
|
||||
58
.github/workflows/llvm-vinvoke-smoke.yml
vendored
Normal file
58
.github/workflows/llvm-vinvoke-smoke.yml
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
name: LLVM VInvoke Smoke
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'crates/**'
|
||||
- 'apps/**'
|
||||
- 'tools/**'
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- '.github/workflows/llvm-vinvoke-smoke.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'crates/**'
|
||||
- 'apps/**'
|
||||
- 'tools/**'
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
|
||||
jobs:
|
||||
llvm-vinvoke:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
NYASH_CLI_VERBOSE: '1'
|
||||
# Plugins enabled (do not set NYASH_DISABLE_PLUGINS)
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust (stable)
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Cache cargo registry and build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-
|
||||
|
||||
- name: Install LLVM 18 (llvm-config-18)
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y curl ca-certificates lsb-release wget gnupg build-essential libzstd-dev
|
||||
curl -fsSL https://apt.llvm.org/llvm.sh -o llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 18
|
||||
llvm-config-18 --version
|
||||
|
||||
- name: Run LLVM VInvoke + Array smoke tests
|
||||
run: |
|
||||
NYASH_LLVM_VINVOKE_RET_SMOKE=1 NYASH_LLVM_ARRAY_RET_SMOKE=1 ./tools/llvm_smoke.sh release
|
||||
39
AGENTS.md
Normal file
39
AGENTS.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
- `src/`: Nyash core (MIR, backends, runner modes). Key: `backend/`, `runner/`, `mir/`.
|
||||
- `crates/nyrt/`: NyRT static runtime for AOT/LLVM (`libnyrt.a`).
|
||||
- `plugins/`: First‑party plugins (e.g., `nyash-array-plugin`).
|
||||
- `apps/` and `examples/`: Small runnable samples and smokes.
|
||||
- `tools/`: Helper scripts (build, smoke).
|
||||
- `tests/`: Rust and Nyash tests; historical samples in `tests/archive/`.
|
||||
- `nyash.toml`: Box type/plug‑in mapping used by runtime.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
- Build (JIT/VM): `cargo build --release --features cranelift-jit`
|
||||
- Build (LLVM AOT): `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm`
|
||||
- Quick VM run: `./target/release/nyash --backend vm apps/APP/main.nyash`
|
||||
- Emit + link (LLVM): `tools/build_llvm.sh apps/APP/main.nyash -o app`
|
||||
- Smokes: `./tools/llvm_smoke.sh release` (use env toggles like `NYASH_LLVM_VINVOKE_RET_SMOKE=1`)
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
- Rust style (rustfmt defaults): 4‑space indent, `snake_case` for functions/vars, `CamelCase` for types.
|
||||
- Keep patches focused; align with existing modules and file layout.
|
||||
- New public APIs: document minimal usage and expected ABI (if exposed to NyRT/plug‑ins).
|
||||
|
||||
## Testing Guidelines
|
||||
- Rust tests: `cargo test` (add targeted unit tests near code).
|
||||
- Smoke scripts validate end‑to‑end AOT/JIT (`tools/llvm_smoke.sh`).
|
||||
- Test naming: prefer `*_test.rs` for Rust and descriptive `.nyash` files under `apps/` or `tests/`.
|
||||
- For LLVM tests, ensure LLVM 18 is installed and set `LLVM_SYS_180_PREFIX`.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
- Commits: concise imperative subject; scope the change (e.g., "llvm: fix argc handling in nyrt").
|
||||
- PRs must include: description, rationale, reproduction (if bug), and run instructions.
|
||||
- Link issues (`docs/issues/*.md`) and reference affected scripts (e.g., `tools/llvm_smoke.sh`).
|
||||
- CI: ensure smokes pass; use env toggles in the workflow as needed.
|
||||
|
||||
## Security & Configuration Tips
|
||||
- Do not commit secrets. Plug‑in paths and native libs are configured via `nyash.toml`.
|
||||
- LLVM builds require system LLVM 18; install via apt.llvm.org in CI.
|
||||
- Optional logs: enable `NYASH_CLI_VERBOSE=1` for detailed emit diagnostics.
|
||||
@ -286,6 +286,15 @@ app.setSize(800, 600)
|
||||
2. **🔄 ドキュメント更新** - 古い/不足している場合は更新
|
||||
3. **💻 ソース確認** - それでも解決しない場合のみソースコード参照
|
||||
|
||||
### 🎯 最重要ドキュメント(2つの核心)
|
||||
|
||||
#### 🔤 言語仕様
|
||||
- **[構文早見表](docs/quick-reference/syntax-cheatsheet.md)** - 基本構文・よくある間違い
|
||||
- **[完全リファレンス](docs/reference/language/LANGUAGE_REFERENCE_2025.md)** - 言語仕様詳細
|
||||
|
||||
#### 📦 主要BOXのAPI
|
||||
- **[Box/プラグイン関連](docs/reference/boxes-system/)** - APIと設計
|
||||
|
||||
### ⚡ API確認の実践例
|
||||
```bash
|
||||
# ❌ 悪い例:いきなりソース読む
|
||||
|
||||
384
CURRENT_TASK.md
384
CURRENT_TASK.md
@ -1,4 +1,218 @@
|
||||
# CURRENT TASK (Phase 10.8: MIR Core-15 確定 → LLVM 準備)
|
||||
# CURRENT TASK (Phase 11.7 kick-off: JIT Complete / Semantics Layer)
|
||||
|
||||
Phase 11.7 へ仕切り直し(会議合意)
|
||||
|
||||
- 単一意味論層: MIR→Semantics→{VM/Cranelift/LLVM/WASM} の設計に切替。VMは参照実装、実行/生成はCodegen側で一本化。
|
||||
- フォールバック廃止: VM→JITの実行時フォールバックは行わない。JITは“コンパイル専用/AOT補助”に限定。
|
||||
- 共通ABI: handle/i64/ptr 変換、to_bool/compare、タグ分類、invoke(固定/可変)、NyRTシム呼び出しを共通化。
|
||||
- Cranelift: 軽量JIT/AOTパスを追加し、最小実装からMIR-15を段階的に緑化。LLVM AOT は併存。
|
||||
|
||||
Docs: docs/development/roadmap/phases/phase-11.7_jit_complete/{README.md, PLAN.md, CURRENT_TASK.md, MEETING_NOTES.md}
|
||||
|
||||
以降は下記の旧計画(LLVM準備)をアーカイブ参照。スモークやツールは必要箇所を段階で引継ぎ。
|
||||
|
||||
Update (2025-09-01 AM / JIT handoff follow-up)
|
||||
|
||||
- Cranelift 最小JITの下地は進捗良好(LowerCore→CraneliftBuilder 経路)
|
||||
- Compare/Branch/Jump、最小Phi(block params)、StackSlotベースの Load/Store は実装済み(builder.rs/core.rs)。
|
||||
- ExternCall(env.console.log/println) の最小橋渡し(ConsoleBox.birth_h→by-id invoke)を追加済み。
|
||||
- 追加スモーク(jit-direct 用/I/Oなし)
|
||||
- apps/tests/mir-store-load: x=1; y=2; x=x+y; return x → 3
|
||||
- apps/tests/mir-phi-min: if(1<2){x=10}else{x=20}; return x → 10
|
||||
- apps/tests/mir-branch-multi: 入れ子条件分岐 → 1
|
||||
- 実行例: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-store-load/main.nyash`
|
||||
- jit-direct 分岐/PHI 根治(進行中)
|
||||
- 現象: select/compare の実行時観測では cond=1/then=1/else=0 と正しいが、最終結果が 0 に落ちるケースあり。
|
||||
- 統合JIT(`--backend cranelift`)は期待どおり→LowerCore の意味論は正しく、jit-direct のCFG/合流が疑わしい。
|
||||
- 主因(仮説→確度高): 関数共有の value_stack をブロック間で使い回し→分岐/合流で返値取り違え。
|
||||
|
||||
変更点(犯人切り分けと根治のための構造改革+ログ)
|
||||
- CraneliftBuilder(jit-direct 経路)
|
||||
- 単一出口+明示合流(SSA)へ切替(途中段階→完了段階へ移行中)
|
||||
- ret 合流ブロックを導入し、BlockParam(i64) で戻り値を受け渡し。
|
||||
- すべての `emit_return` を「i64 正規化 → `jump(ret, [val])`」に統一(return 命令は合流側だけ)。
|
||||
- end_function 側で ret 合流ブロックに切替し、BlockParam を f64 変換(必要時)のうえ return。
|
||||
- Compare/Branch/Select まわり
|
||||
- Compare 結果をローカルスロットへ保存→Branch/Select 時に確実にロード(スタック取りこぼし排除)。
|
||||
- `br_if` 直前に cond を b1 正規化(i64→b1)。
|
||||
- デバッグ用 extern を登録(必要時のみ)
|
||||
- `nyash.jit.dbg_i64(tag: i64, val: i64) -> i64`(値観測用)
|
||||
- `nyash.jit.block_enter(idx: i64) -> void`(ブロック入場ログ)
|
||||
- LowerCore(return 値の堅牢化)
|
||||
- Return 値が known/param/slot 経路に乗らない場合、同一ブロックの Const 定義をスキャンして materialize。
|
||||
- Fast-path(読みやすさ&単純化): then/else が定数 return の場合、`select(cond, K_then, K_else)`→`emit_return` に縮約(`NYASH_JIT_FASTPATH_SELECT=1` で強制)。
|
||||
|
||||
診断ログ(必要時のみ ON)
|
||||
- `NYASH_JIT_TRACE_BLOCKS=1` … ブロック入場ログ(`[JIT-BLOCK] enter=<idx>`)
|
||||
- `NYASH_JIT_TRACE_BR=1` …… br_if の cond 有無(`[JIT-CLIF] br_if cond_present=...`)
|
||||
- `NYASH_JIT_TRACE_SEL=1` … select の cond/then/else 値(tag=100/101/102)
|
||||
- `NYASH_JIT_TRACE_RET=1` … return の値: emit_return 直前(tag=201)、ret 合流(tag=200)
|
||||
|
||||
再現・実行コマンド(jit-direct)
|
||||
- 分岐の最小: `apps/tests/mir-branch-ret/main.nyash`
|
||||
- `NYASH_JIT_THRESHOLD=1 NYASH_JIT_DUMP=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
|
||||
- 診断ON: `NYASH_JIT_THRESHOLD=1 NYASH_JIT_DUMP=1 NYASH_JIT_TRACE_RET=1 NYASH_JIT_TRACE_BLOCKS=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
|
||||
- PHI最小: `apps/tests/mir-phi-min/main.nyash`
|
||||
- 複合分岐: `apps/tests/mir-branch-multi/main.nyash`
|
||||
|
||||
期待と現状(2025-09-01 PM 時点)
|
||||
- 統合JIT(`--backend cranelift`): OK(`mir-branch-ret`→1)。
|
||||
- jit-direct: 単一出口+BlockParam 合流の配線で安定化を確認(cond/then/else も正常)。tag=201/200 は一致。
|
||||
|
||||
検証結果(jit-direct / 2025-09-01 実行)
|
||||
- `apps/tests/mir-branch-ret`: Result=1, [JIT-DBG] 201=1 / 200=1
|
||||
- `apps/tests/mir-phi-min`: Result=10, [JIT-DBG] 201=10 / 200=10
|
||||
- `apps/tests/mir-branch-multi`: Result=1, [JIT-DBG] 201=1 / 200=1
|
||||
- `apps/tests/mir-nested-branch`: Result=1, [JIT-DBG] 201=1 / 200=1
|
||||
- `apps/tests/mir-phi-two`: Result=50
|
||||
|
||||
次のタスク(仕上げ)
|
||||
1) LowerCore fast‑path/select のガード整理(`NYASH_JIT_FASTPATH_SELECT` の簡素化)。
|
||||
2) b1 返り値 ABI を有効化する場合の経路確認(feature `jit-b1-abi`)。
|
||||
3) ドキュメント整備(CraneliftBuilder 単一出口方針と TRACE 変数の最終化)。
|
||||
|
||||
Update (2025-09-01 PM2 / Interpreter parity blockers)
|
||||
|
||||
- 目的: Semantics 層での VM/JIT/Interpreter パリティ検証に向け、Interpreter 側の既知不具合を記録・引き継ぎ。
|
||||
|
||||
- 再現ファイル(ユーザー提供)
|
||||
- examples/semantics_test_branch.nyash
|
||||
- 期待: 100
|
||||
- 実際(Interpreter): IntegerBox(4)(変数IDが返る挙動)
|
||||
- 実際(VM): 100
|
||||
- examples/simple_test.nyash
|
||||
- 事象: PluginBoxV2 同士の加算でエラー(IntegerBoxダウンキャストのみを試行して失敗)
|
||||
- /tmp/test_string_concat.nyash
|
||||
- 事象: 文字列連結でエラー(PluginBox と String の連結が不可)
|
||||
|
||||
- 現状の暫定対応(実装済み)
|
||||
- Await(JIT): I::Await を nyash.future.await_h に降下(同期get)
|
||||
- Safepoint(JIT): I::Safepoint を nyash.rt.checkpoint に降下(global_hooks 経由で GC/scheduler 連携)
|
||||
- Barrier(JIT): Array.set/Map.set で nyash.gc.barrier_write emit
|
||||
- Interpreter: FutureBox を登録(new FutureBox(42) 可)
|
||||
- Interpreter: 加算まわりのフォールバック強化
|
||||
- 文字列likeの優先連結(toUtf8/Result.Ok含む)
|
||||
- 数値文字列→整数にパースできる場合は加算
|
||||
|
||||
- 未解決(最優先)
|
||||
1) 返り値が変数IDになる(examples/semantics_test_branch.nyash)
|
||||
- 調査方針: execute_statement(Return)→execute_function_call の伝播経路、Variable 解決/共有の箇所を追跡し、Box 値ではなく内部ID/インデックスを返している箇所を特定・修正。
|
||||
- 対応: Return 直前と関数エピローグでの実値/型ログ(限定ログ)を差し込み、最小修正。
|
||||
2) PluginBox 同士の演算の包括対応
|
||||
- 暫定は toString→数値/文字列へ正規化で回避。恒久対応は Semantics/VM と同じ規約(handle-first + 文字列like/数値like)に寄せる。
|
||||
3) 文字列連結の広範囲対応
|
||||
- toString()/toUtf8/Result.Ok の内包を最優先で文字列正規化(現状の強化で多くは通る。追加ケースが出れば順次取り込み)。
|
||||
|
||||
- 次アクション(Interpreter fix plan)
|
||||
- [ ] semantics_test_branch 再現→Return 値の実体化ルート修正
|
||||
- [ ] simple_test の演算パスを toString 正規化で網羅(必要なら算術 toNumber も)
|
||||
- [ ] test_string_concat の失敗パターン収集→ try_box_to_string の対象拡張
|
||||
- [ ] SemanticsVM/ClifAdapter パリティ小スモーク追加(分岐/配列/extern/await)
|
||||
|
||||
開発メモ / 注意点
|
||||
- 分岐の取り違えは「ブロックまたぎの共有スタック」が原因になりがち。根治策として BlockParam 経由の合流・単一出口 return を徹底。
|
||||
- デバッグログは “必要時のみ ON” の方針で仕込む。収束後に不要箇所は落とす(本番は静かに)。
|
||||
|
||||
チェックリスト(収束条件)
|
||||
- [ ] jit-direct: `mir-branch-ret` → 1(tag=201/200 とも 1)
|
||||
- [ ] jit-direct: `mir-phi-min` → 10(合流経路で BlockParam 値が正しい)
|
||||
- [ ] jit-direct: `mir-branch-multi` → 1(ネスト分岐でも合流が正しい)
|
||||
- [ ] 統合JIT(--backend cranelift)と一致
|
||||
- [ ] ログ・一時コードの整理(必要なものだけ残す)
|
||||
- 既知の注意(jit-direct 経路のみ)
|
||||
- 条件分岐系(Branch/Jump)で戻り値が 0 になる事象を確認。`--backend cranelift`(統合経路)では期待値どおり(例: mir-branch-ret → 1)。
|
||||
- 影響範囲: jit-direct 実験フラグのみ。LowerCore/CraneliftBuilder の IR 自体は生成されており、統合経路では正しく実行される。
|
||||
- 次回対応: brif 直後のブロック制御/シール順の見直し(entry/sealing)、条件値スタック消費タイミングの再点検。
|
||||
|
||||
|
||||
Update (2025-09-01 night / JIT-direct branch/PHI fix)
|
||||
|
||||
- Summary
|
||||
- Fixed jit-direct returning 0 for branches by unifying returns to a single-exit ret block and improving MIR return type inference (including PHI-based inference).
|
||||
- Stabilized PHI joins by materializing PHI results into locals and preferring local loads for Return.
|
||||
- Corrected stack order for `br_if_with_args` (pop else args → then args → cond), matching LowerCore push order; added minimal fallback when predecessor mapping is unavailable.
|
||||
- Added debug symbols and logs (gated by env) for ret path and CFG wiring.
|
||||
|
||||
- Code touched
|
||||
- MIR: `src/mir/builder.rs` (return type inference incl. PHI inputs)
|
||||
- Lowering core: `src/jit/lower/core.rs` (PHI materialize to local; arg wiring fallback; small traces)
|
||||
- Builder (Cranelift): `src/jit/lower/builder.rs` (single-exit return; br_if/jump args order; debug symbol registrations)
|
||||
|
||||
- New smokes
|
||||
- `apps/tests/mir-phi-two` … merge two locals (x,y) then add
|
||||
- `apps/tests/mir-nested-branch` … nested branches returning constants
|
||||
|
||||
- Status (jit-direct)
|
||||
- mir-branch-ret → 1
|
||||
- mir-phi-min → 10
|
||||
- mir-phi-two → OK(合流+加算)
|
||||
- mir-nested-branch → 1
|
||||
- LLVM AOT snips (prebuilt binaries) still OK for VInvoke samples.
|
||||
|
||||
- How to run
|
||||
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
|
||||
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-phi-min/main.nyash`
|
||||
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-phi-two/main.nyash`
|
||||
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-nested-branch/main.nyash`
|
||||
- Optional logs: `NYASH_JIT_DUMP=1` and/or `NYASH_JIT_TRACE_RET=1`
|
||||
|
||||
- Next (small boxes; avoid saturation)
|
||||
1) Fast-path(select) 1-hop extension only:
|
||||
- If then/else each jump to a block that immediately returns a Const Integer, lower to `select(cond, K_then, K_else) → return`.
|
||||
- Keep scope narrow to avoid regressions.
|
||||
2) Multi-PHI (limited):
|
||||
- Add one smoke with two PHI slots; confirm arg counts/ordering under `phi_min`.
|
||||
3) Logging remains env-gated; no default noise. No broad refactors until the above are green.
|
||||
|
||||
|
||||
# (以下、旧タスク: Phase 10.8 記録)
|
||||
|
||||
Contributor note: 開発手順・レイアウト・PR要件はリポジトリルートの `AGENTS.md`(Repository Guidelines)参照。ビルド/テストやCIスモークの環境変数も簡潔にまとまっています。
|
||||
|
||||
## Handoff (Phase 11 next): 実行確認プラン(tests/ は対象外)
|
||||
|
||||
目的: apps 配下の「簡単なアプリ」から順に、VM → AOT(LLVM) の順で確実に動作させる。tests/ フォルダは除外。
|
||||
|
||||
前提/共通
|
||||
- LLVM: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix)` を付与してビルド
|
||||
- AOTリンク: `tools/build_llvm.sh <app.nyash> -o app` を使用(`target/aot_objects/*.o` 固定)
|
||||
- Verbose: トラブル時は `NYASH_CLI_VERBOSE=1`
|
||||
- プラグインテスター: `tools/plugin-tester` を利用可(`cargo run --release -- check --config ../../nyash.toml --library <lib>`)
|
||||
|
||||
推奨テスト順(簡単→段階的)
|
||||
1) apps/ny-array-llvm-ret/main.nyash(Array push/get 戻り値)
|
||||
- 期待: `Result: 3`
|
||||
- VM: `./target/release/nyash --backend vm apps/ny-array-llvm-ret/main.nyash`
|
||||
- AOT: `tools/build_llvm.sh apps/ny-array-llvm-ret/main.nyash -o app && ./app`
|
||||
|
||||
2) apps/ny-vinvoke-llvm-ret-size/main.nyash(by-id size)
|
||||
- 期待: `Result: 1`
|
||||
- VM/AOT 同上
|
||||
|
||||
3) apps/ny-vinvoke-llvm-ret/main.nyash(by-id get 可変長経路)
|
||||
- 期待: `Result: 42`
|
||||
- VM/AOT 同上
|
||||
|
||||
4) apps/ny-echo-lite/main.nyash(readLine → print 最小エコー)
|
||||
- 期待: 出力に `Result:` 行が含まれる(llvm_smoke の基準)
|
||||
- 実行例: `echo hello | ./target/release/nyash --backend vm apps/ny-echo-lite/main.nyash`
|
||||
- AOT: `tools/build_llvm.sh apps/ny-echo-lite/main.nyash -o app_echo && echo hello | ./app_echo`
|
||||
|
||||
5) apps/ny-llvm-smoke/main.nyash(Array get/set/print)
|
||||
- 期待: `Result: 3`
|
||||
- 備考: 文字列連結は NyRT concat シムへフォールバック済み。emit 検知が不安定な場合は再試行。問題が残る場合はこの項を後回し可。
|
||||
|
||||
6) apps/ny-echo/main.nyash(オプション付きエコー)
|
||||
- 期待: VM/JIT/AOT の出力一致(upper/lower/そのまま)
|
||||
|
||||
7) apps/ny-map-llvm-smoke/main.nyash(Map by-id 経路)
|
||||
- 期待: 行に `Map: v=42` と `size=1`
|
||||
- 備考: 連結シム適用済み。必要なら `NYASH_LLVM_ALLOW_BY_NAME=1` で一時回避。
|
||||
|
||||
トラブルシュート要点
|
||||
- AOT emit: `NYASH_LLVM_OBJ_OUT=$PWD/target/aot_objects/<name>.o ./target/release/nyash --backend llvm ...`
|
||||
- リンク: `NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT=... tools/build_llvm.sh ...`
|
||||
- プラグイン: `nyash.toml` のパス解決(拡張子はOSで異なる)。tester は拡張子補完に対応。
|
||||
|
||||
Update (2025-08-31 AM / Phase 11.1 quick pass)
|
||||
|
||||
@ -21,6 +235,102 @@ Update (2025-08-31 PM / Phase 11.2 partial)
|
||||
- スモーク
|
||||
- `examples/llvm11_core_smoke.nyash` で EXE 実行し `Result: 3` を確認
|
||||
|
||||
Update (2025-08-31 PM2 / Phase 11.2 lightweight LLVM)
|
||||
|
||||
- 方針(拡張性優先 / コア最小化: Tier‑0)
|
||||
- ExternCall: 環境/I/Oのみ(env.console/debug/runtime など)。print は ExternCall(env.console.log) 本線。
|
||||
- BoxCall: データ構造/Box 操作(Array/Map/String/Instance 等)。AOT/LLVM は最小の安全シムのみ直結。
|
||||
- コアに残す安全シム(NyRT): Array(get/set/push/length), Instance(getField/setField)。Map はコアに足さない(後述)。
|
||||
- Map/JSON/Math 等は当面コア外(必要時はプラグイン by‑id + 汎用シムでAOTを通す)。
|
||||
|
||||
- 実装・反映
|
||||
- MIR パス: `passes/method_id_inject` 追加(NewBox/Copy 由来の型から BoxCall に method_id 注入。PluginInvoke は可能なら BoxCall(by‑id)へ書換)。
|
||||
- LLVM Lowering:
|
||||
- ExternCall: `env.console.log` → `nyash.console.log`(NyRT), `env.debug.trace` → `nyash.debug.trace`。
|
||||
- ExternCall: `env.console.readLine` 追加 → `nyash.console.readline`(stdin 1行, CR/LF 除去, C文字列返却)。
|
||||
- ArrayBox: `get/set/push/length` を NyRT 安全シム(`nyash_array_get_h/set_h/push_h/length_h`)に直結。
|
||||
- Instance: `getField/setField` を NyRT 安全シム(`nyash.instance.get_field_h/set_field_h`)に直結(既存)。
|
||||
- プラグイン by‑id: f64 戻りの選択(`nyash_plugin_invoke3_f64`)/ i64 戻り(`..._i64`)。先頭2引数はタグ付け(int/float/handle)対応(`..._tagged_i64`)。
|
||||
- by‑name 薄フォールバック(デバッグ用): `NYASH_LLVM_ALLOW_BY_NAME=1` 下で `nyash.plugin.invoke_by_name_i64` を使用。
|
||||
- NyRT(libnyrt.a): 上記 API を追加(console.log/debug.trace/readline, array push/length, instance get/set_field, by‑name, tagged_i64)。
|
||||
- オブジェクト出力: `NYASH_LLVM_OBJ_OUT=<path>` で .o を明示出力(`runner/modes/llvm.rs` 経由)。
|
||||
- ツール更新: `tools/build_llvm.sh` / `tools/llvm_smoke.sh` が `NYASH_LLVM_OBJ_OUT` を使用して .o を取得後、NyRT とリンク。
|
||||
- サンプル: `apps/ny-llvm-smoke/main.nyash`(Array get/set/print)、`apps/ny-echo-lite/main.nyash`(readLine→print)。
|
||||
|
||||
- しないこと / 後回し
|
||||
- Map のコア安全シム追加(`nyash_map_*_h`)は見送り。必要ならプラグイン by‑id + 汎用シムでAOT実行。
|
||||
- ConsoleBox の高度機能は ExternCall 側で段階導入(出力は既存 log、入力は readline のみ)。
|
||||
- 汎用可変長引数(>2)は後段(タグ付けの拡張で対応予定)。
|
||||
|
||||
- 次にやること(短期)
|
||||
- ny-echo を縮小AOT対応(console.readLine + print のみで OK 版)。
|
||||
- Map(プラグイン最小版)で string-key の get/set/size を by‑id 汎用シム経由でAOT実行(コアは増やさない)。
|
||||
- CI/スモーク: `.o→EXE→実行` を `apps/ny-llvm-smoke` / `apps/ny-echo-lite` で追加。
|
||||
- ガードレール: コア安全シムを Tier‑0 以外に増やさない簡易チェック(grep ベース)導入検討。
|
||||
|
||||
- How to Build / Run(AOT/LLVM)
|
||||
- ビルド: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm`
|
||||
- .o→EXE: `tools/build_llvm.sh <file.nyash> -o app_llvm` → `./app_llvm`
|
||||
- .o のみ: `NYASH_LLVM_OBJ_OUT=$PWD/nyash_llvm_temp.o ./target/release/nyash --backend llvm <file.nyash>`
|
||||
- 推奨 @env(main.nyash 冒頭コメント):
|
||||
- `// @env NYASH_CLI_VERBOSE=1`(詳細ログ)
|
||||
- デバッグ時のみ `// @env NYASH_LLVM_ALLOW_BY_NAME=1`
|
||||
|
||||
Update (2025-08-31 PM3 / LLVM VInvoke triage)
|
||||
|
||||
- 直近のテスト結果(要約)
|
||||
- VM backend(正常)
|
||||
- MapBox.size(): Result: 1 ✅
|
||||
- MapBox.get(1): Result: 42 ✅
|
||||
- 可変長(VInvoke: get(1,9,8,7,6)): 期待どおりにTLV I64(tag=3)でエンコードされ、出力確認済 ✅
|
||||
- LLVM backend(要修正)
|
||||
- MapBox birth は成功(instance_id=1)
|
||||
- set()/get() 呼び出し経路で戻り値が Result: 0(期待は 42)❌
|
||||
- LLVM 実行時に VM 相当の PluginLoaderV2 Invoke 詳細ログが出ず、by-id/可変長の値流しに不整合がある可能性
|
||||
|
||||
- 実装・修正状況
|
||||
- ランタイム(NyRT)
|
||||
- by-id 固定長/可変長の argc を「レシーバ除外の実引数個数」に統一済み
|
||||
- invoke 時の type_id を「レシーバ実体(PluginBoxV2)から取得した実 type_id」に変更(呼び出し先揺れを排除)
|
||||
- LLVM Lowering
|
||||
- <=4 引数: nyash_plugin_invoke3_tagged_i64(f64→i64ビット化+タグ付与)
|
||||
- >=5 引数: nyash.plugin.invoke_tagged_v_i64(vals/tags の vector 経路)
|
||||
- スモーク
|
||||
- apps/ny-vinvoke-llvm-ret: 戻り値で 42 を検証する LLVM 用スモークを追加(print/concatに依存しない)
|
||||
- tools/llvm_smoke.sh に VInvoke(戻り値)スモークをオプション追加(NYASH_LLVM_VINVOKE_RET_SMOKE=1)
|
||||
|
||||
- いま見えている課題(LLVM)
|
||||
- by-id vector 経路(vals/tags/argc)のどこかで齟齬 → get が 0 を返す(キーが TLV 化されていない/タグずれなど)
|
||||
- 固定長(<=4)経路は未切り分け → まず size()/get(1) を LLVM 戻り値で確定(size→1, get(1)→42)
|
||||
- .o 出力の一部環境不安定は Runner/Lowering にフォールバックを追加済(write_to_memory_buffer)
|
||||
|
||||
- 次アクション
|
||||
1) LLVM 戻り値テストを段階確認
|
||||
- MapBox.size() → Result: 1
|
||||
- MapBox.get(1) → Result: 42
|
||||
- これで固定長 by-id の健全性を確定後、可変長(>=5)vector 経路へ絞り込み
|
||||
2) NyRT デバッグ(NYASH_CLI_VERBOSE=1 時のみ)を最小追加
|
||||
- nyash.plugin.invoke_tagged_v_i64: argc/method_id、vals[0..2], tags[0..2] を stderr に出力
|
||||
- 実際のTLV化の前に観測し、ズレ箇所を確定
|
||||
3) LLVM Lowering(vector 経路)の配列構築を点検
|
||||
- alloca([N x i64]) → inbounds GEP → store → i64* へ pointer_cast → 呼び出し
|
||||
- GEP index([0,i])/型一致/メモリ幅を再確認
|
||||
4) 必要なら一時的に「<=4 でも vector 経路を選択」する実験分岐を作り、経路差異を切り分け
|
||||
5) tools/llvm_smoke.sh: target/aot_objects 固定・事前 emit → link のフローをデフォルト強化(CI安定化)
|
||||
|
||||
- ゴール(本フェーズ収束条件)
|
||||
- LLVM backend で MapBox: size()/get(1)/get(1,9,8,7,6) が戻り値ベースで一致
|
||||
- VInvoke(可変長)経路が by-id で安定
|
||||
- print/concat のLoweringは後続(必要最小)に回す
|
||||
|
||||
- タスク実行(MVP)
|
||||
- `nyash.toml` に `[env]` / `[tasks]` を記述し、CLIから `--run-task <name>` で実行可能。
|
||||
- 例:
|
||||
- `[tasks] build_llvm = "LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm"`
|
||||
- 実行: `./target/release/nyash --run-task build_llvm`
|
||||
- 備考: `{root}` 変数展開対応。OS別/依存/並列は未対応(将来拡張)。
|
||||
|
||||
|
||||
残作業(合意順)
|
||||
|
||||
1) method_id 埋め込みと by-id 本線化
|
||||
@ -38,8 +348,76 @@ Update (2025-08-31 PM / Phase 11.2 partial)
|
||||
- Phi: 事前に各BB先頭でPhiノード生成→Branch/Jump時にincomingを配線
|
||||
- Load/Store: entryでのalloca管理(型は注釈/値から推定)。i1/i64の簡易変換、ポインタはpointer_cast対応
|
||||
- 型なしポインタ(opaque)対応のため、`alloca`の要素型を別マップで追跡
|
||||
- ビルド検証: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --features llvm` 成功
|
||||
- 既存のConst/Unary/BinOp/Compare/Branch/Jump/Return と併せ、Phase 11.1の目標範囲は到達
|
||||
- ビルド検証: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --features llvm` 成功
|
||||
- 既存のConst/Unary/BinOp/Compare/Branch/Jump/Return と併せ、Phase 11.1の目標範囲は到達
|
||||
|
||||
---
|
||||
|
||||
一時メモ(MIRインタプリタ 80/20 方針)
|
||||
|
||||
- ConsoleBox.readLine ローカルフォールバック(標準入力1行読み)を一時実装(`--backend mir` の echo-lite 用)。
|
||||
- 後で必ず削除し、プラグイン側メソッド/Extern 経由に置換すること。
|
||||
- 追跡タスク: 「ConsoleBox メソッド群の正式実装(PluginInvoke/ExternCall)とローカルフォールバックの撤去」。
|
||||
- 影響箇所: `src/backend/mir_interpreter.rs`(BoxCall: ConsoleBox.readLine 分岐)、`apps/tests/ny-echo-lite` スモーク。
|
||||
|
||||
次アクション(この項に紐づく)
|
||||
- ClifSem 最小Lowering(Const/Return/Add)を実装し、JITスケルトンの0終了スモークを追加。
|
||||
- ConsoleBox フォールバックは温存のまま(echo-liteのみのため)。Console 正式化のタイミングで除去。
|
||||
|
||||
---
|
||||
|
||||
# Handoff (Phase 11.7) — MIR Interpreter + Cranelift Minimal JIT
|
||||
|
||||
目的: LLVMが重くなったため仕切り直し。新しいMIR解釈層と軽量Cranelift JITの最小機能を整備し、段階拡張しやすい骨格を確立。
|
||||
|
||||
実装済み(要点)
|
||||
- 共通ABI/ユーティリティ:
|
||||
- `src/backend/abi_util.rs`(to_bool/eq/tag/handle)
|
||||
- MIRインタプリタ(--backend mir):
|
||||
- 対応: Const/Unary/BinOp(+String結合)/Compare/Load/Store/Copy/Branch/Jump/Return/Print/Debug/Barrier/Safepoint(no-op)
|
||||
- NewBox/PluginInvoke/BoxCall/ExternCallの最小対応(PluginBoxV2はプラグインホスト経由)
|
||||
- ConsoleBox.readLine: 一時フォールバックで標準入力1行読み(CURRENT_TASKに削除タスク記載済)
|
||||
- Cranelift最小JIT(--backend cranelift):
|
||||
- 実JIT(jit.rs): Const(i64/f64/bool->0/void->0)/Add/Sub/Mul/Div/Mod、Compare(Eq/Ne/Lt/Le/Gt/Ge)、Load/Store(StackSlot)、Copy、Return/Jump/Branch
|
||||
- 箱化: `src/backend/cranelift/context.rs` に ClifContext/BlockMap/ValueEnv を用意(JIT構築をカプセル化)
|
||||
- LowerCore→ClifBuilder(IRBuilder実体): 録画→実IR生成(Const/Add/Return)を暫定実装
|
||||
- 起動切替: `NYASH_JIT_LOWERCORE=1` で LowerCore→ClifBuilder 実IR経路を実行
|
||||
- スモーク:
|
||||
- `apps/tests/mir-const-add/main.nyash`(0終了)
|
||||
- `apps/tests/mir-branch-ret/main.nyash`(条件分岐で1)
|
||||
- どちらも --backend cranelift / --backend mir で確認済
|
||||
|
||||
使い方(コマンド)
|
||||
- Cranelift有効ビルド: `cargo build --features cranelift-jit`
|
||||
- MIRインタプリタ: `./target/debug/nyash --backend mir apps/tests/mir-const-add/main.nyash`
|
||||
- Cranelift最小JIT: `./target/debug/nyash --backend cranelift apps/tests/mir-branch-ret/main.nyash`
|
||||
- LowerCore→ClifBuilder 実IR: `NYASH_JIT_LOWERCORE=1 ./target/debug/nyash --backend cranelift apps/tests/mir-const-add/main.nyash`
|
||||
|
||||
次のタスク(推奨順)
|
||||
1) ClifBuilder 実IR生成の拡張(LowerCore連携)
|
||||
- Compare/Branch/Jump の実IR
|
||||
- 最小Phi(Block Params)
|
||||
- StackSlotベースのLoad/Store(ValueEnvから完全移行)
|
||||
2) ExternCall(env.console.log)最小対応(JIT経路でもprintln相当へ)
|
||||
3) スモーク追加: Load/Store、Phi最小ケース、Compare/Branch複合ケース
|
||||
4) Console/Extern 正式化完了後に ConsoleBox.readLine フォールバック削除(本ファイルにタスク済)
|
||||
5) 警告/CFG整理: 使っていないfeature cfgやunusedを段階的に整理
|
||||
|
||||
既知の注意/制限(80/20の割り切り)
|
||||
- BoolはI64の0/1として扱っており、B1専用ABIは未導入(将来拡張)。
|
||||
- String/Null/Void のConstは暫定的に0へ丸め(必要箇所から段階的に正規化)。
|
||||
- `jit-b1-abi` 等のunexpected cfg警告は今後整理対象。
|
||||
|
||||
関連ファイル
|
||||
- 追加: `src/backend/abi_util.rs`, `src/backend/mir_interpreter.rs`, `src/runner/modes/mir_interpreter.rs`
|
||||
- 追加: `apps/tests/mir-const-add/main.nyash`, `apps/tests/mir-branch-ret/main.nyash`
|
||||
- Cranelift: `src/backend/cranelift/jit.rs`, `src/backend/cranelift/context.rs`, `src/backend/cranelift/builder.rs`
|
||||
- Semantics: `src/jit/semantics/{mod.rs,clif.rs}`(スケルトン)
|
||||
|
||||
メモ/トグル
|
||||
- LowerCore→ClifBuilder 実IR: `NYASH_JIT_LOWERCORE=1`
|
||||
- カバレッジログ: `NYASH_JIT_DUMP=1`
|
||||
|
||||
|
||||
|
||||
Handoff Snapshot (2025-08-31 / Phase 11 kick-off)
|
||||
|
||||
@ -156,7 +156,8 @@ egui_extras = { version = "0.29", features = ["image"], optional = true }
|
||||
image = { version = "0.25", features = ["png", "ico"], optional = true }
|
||||
|
||||
# LLVM backend deps (optional)
|
||||
inkwell = { version = "0.5.0", features = ["llvm18-0"], optional = true }
|
||||
# Move to LLVM 18.1.x toolchains
|
||||
inkwell = { version = "0.6.0", features = ["llvm18-1"], optional = true }
|
||||
|
||||
# Windows API
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
|
||||
38
README.ja.md
38
README.ja.md
@ -128,6 +128,13 @@ cargo build --release --features cranelift-jit
|
||||
tools/smoke_aot_vs_vm.sh examples/aot_min_string_len.nyash
|
||||
```
|
||||
|
||||
### LLVM バックエンドの補足
|
||||
- `NYASH_LLVM_OBJ_OUT`: `--backend llvm` 実行時に `.o` を出力するパス。
|
||||
- 例: `NYASH_LLVM_OBJ_OUT=$PWD/nyash_llvm_temp.o ./target/release/nyash --backend llvm apps/ny-llvm-smoke/main.nyash`
|
||||
- `NYASH_LLVM_ALLOW_BY_NAME=1`: デバッグ専用の by-name フォールバック(by-id が未提供の場合の暫定措置)。
|
||||
- 開発時のみ有効化してください(本番では無効)。
|
||||
|
||||
|
||||
### 5. **WebAssembly** (ブラウザ用)
|
||||
```bash
|
||||
cargo build --release --features wasm-backend
|
||||
@ -139,6 +146,35 @@ cargo build --release --features wasm-backend
|
||||
|
||||
---
|
||||
|
||||
## 🧰 タスク実行 (nyash.toml)
|
||||
|
||||
`nyash.toml` の `[tasks]` と `[env]` で、ビルド/スモークなどのタスクを簡単に実行できます(MVP)。
|
||||
|
||||
例(nyash.toml の末尾に追記):
|
||||
|
||||
```
|
||||
[env]
|
||||
RUST_BACKTRACE = "1"
|
||||
|
||||
[tasks]
|
||||
build_llvm = "LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm"
|
||||
smoke_obj_array = "NYASH_LLVM_OBJ_OUT={root}/nyash_llvm_temp.o ./target/release/nyash --backend llvm apps/ny-llvm-smoke/main.nyash"
|
||||
```
|
||||
|
||||
実行:
|
||||
|
||||
```
|
||||
./target/release/nyash --run-task build_llvm
|
||||
./target/release/nyash --run-task smoke_obj_array
|
||||
```
|
||||
|
||||
補足:
|
||||
- `[env]` の値は実行前に環境へ適用されます。
|
||||
- `{root}` は現在のプロジェクトルートに展開されます。
|
||||
- 現状は最小機能(OS別/依存/並列は未対応)。
|
||||
|
||||
---
|
||||
|
||||
## 📊 **パフォーマンスベンチマーク**
|
||||
|
||||
実世界ベンチマーク結果 (ny_bench.nyash):
|
||||
@ -325,6 +361,8 @@ box GameObject {
|
||||
- 📚 ドキュメントの改善
|
||||
- 🎮 クールなサンプルプログラム
|
||||
|
||||
詳細は `AGENTS.md`(Repository Guidelines)をご参照ください。プロジェクト構成、ビルド/テスト手順、PRの要件を簡潔にまとめています。
|
||||
|
||||
## 📄 **ライセンス**
|
||||
|
||||
MIT ライセンス - プロジェクトで自由に使用してください!
|
||||
|
||||
10
README.md
10
README.md
@ -128,6 +128,14 @@ Quick smoke test (VM vs EXE):
|
||||
tools/smoke_aot_vs_vm.sh examples/aot_min_string_len.nyash
|
||||
```
|
||||
|
||||
### LLVM Backend Notes
|
||||
- `NYASH_LLVM_OBJ_OUT`: Path to emit `.o` when running `--backend llvm`.
|
||||
- Example: `NYASH_LLVM_OBJ_OUT=$PWD/nyash_llvm_temp.o ./target/release/nyash --backend llvm apps/ny-llvm-smoke/main.nyash`
|
||||
- `NYASH_LLVM_ALLOW_BY_NAME=1`: Debug-only fallback for plugin calls by name when by-id isn’t available.
|
||||
- Emits calls to `nyash.plugin.invoke_by_name_i64` for development.
|
||||
- Do not enable in production.
|
||||
|
||||
|
||||
### 5. **WebAssembly** (Browser)
|
||||
```bash
|
||||
cargo build --release --features wasm-backend
|
||||
@ -325,6 +333,8 @@ Join the revolution! We welcome:
|
||||
- 📚 Documentation improvements
|
||||
- 🎮 Cool example programs
|
||||
|
||||
See also: Contributor guide in `AGENTS.md` (Repository Guidelines) for project layout, build/test commands, and PR expectations.
|
||||
|
||||
## 📄 **License**
|
||||
|
||||
MIT License - Use freely in your projects!
|
||||
|
||||
BIN
app_array_ret_llvm
Normal file
BIN
app_array_ret_llvm
Normal file
Binary file not shown.
BIN
app_echo_llvm
Normal file
BIN
app_echo_llvm
Normal file
Binary file not shown.
BIN
app_map_llvm
Normal file
BIN
app_map_llvm
Normal file
Binary file not shown.
BIN
app_vinvoke_llvm
Normal file
BIN
app_vinvoke_llvm
Normal file
Binary file not shown.
BIN
app_vinvoke_ret_llvm
Normal file
BIN
app_vinvoke_ret_llvm
Normal file
Binary file not shown.
Binary file not shown.
632
apps/AI_TEACHERS_FULL_CONVERSATIONS.md
Normal file
632
apps/AI_TEACHERS_FULL_CONVERSATIONS.md
Normal file
@ -0,0 +1,632 @@
|
||||
# AI先生たちのNyashメモリ管理に関する完全な会話記録(2025-08-31)
|
||||
|
||||
## Gemini先生との最初の会話
|
||||
|
||||
### 私からの相談内容
|
||||
```
|
||||
Nyashプログラミング言語のメモリ管理について相談です。
|
||||
|
||||
【Nyashのメモリ管理の特徴】
|
||||
- Everything is Box哲学(すべてがBoxオブジェクト)
|
||||
- Arc<Mutex>統一アーキテクチャ(全Box型で統一)
|
||||
- 参照カウント式のシンプルなGC
|
||||
- プラグインシステムとの統合
|
||||
- ハンドルベースのFFI
|
||||
|
||||
【問題】
|
||||
論文用にbinary-trees、n-body、mandelbrotなどの標準ベンチマークが推奨されていますが、これらは小規模でNyashの特殊なメモリ管理の良さを表現しきれません。
|
||||
|
||||
【質問】
|
||||
1. Nyashのメモリ管理の特殊性を活かせる大規模アプリケーションは?
|
||||
2. Arc<Mutex>の並行性を活かしたデモは?
|
||||
3. プラグイン間でのBox共有を示すアプリケーションは?
|
||||
4. 他言語では難しいがNyashなら簡単に実装できる例は?
|
||||
```
|
||||
|
||||
### Gemini先生の完全回答
|
||||
|
||||
Nyashプログラミング言語のメモリ管理に関するご相談、ありがとうございます。言語設計の観点から、そのユニークなアーキテクチャの強みを最大限に表現できるアプリケーションについて、以下の通り提案します。
|
||||
|
||||
Nyashの「Everything is Box」哲学と`Arc<Mutex>`統一アーキテクチャは、従来の数値計算ベンチマークでは評価しにくい、**動的で状態が複雑に変化する大規模な並行処理**において真価を発揮します。論文でその独自性と優位性を示すには、まさにその特性を要求するアプリケーションを提示することが効果的です。
|
||||
|
||||
---
|
||||
|
||||
### 1. Nyashのメモリ管理の特殊性を活かせる大規模アプリケーション
|
||||
|
||||
**提案:リアルタイム・コラボレーティブ・アプリケーション基盤**
|
||||
|
||||
Google Docsのような共同編集エディタ、Figmaのようなデザインツール、またはマルチプレイヤーオンラインゲームのサーバーサイドなど、複数の主体が単一の共有状態を同時に変更するアプリケーションの基盤です。
|
||||
|
||||
**Nyashの強みが活きる点:**
|
||||
* **安全な状態共有:** ドキュメントのデータ構造(ツリー、グラフ)、あるいはゲームワールドの状態全体を`Box`オブジェクトとして表現します。`Arc<Mutex>`アーキテクチャにより、多数のユーザー(クライアント)からの同時編集リクエストがデータ競合を起こすことなく、安全に処理されることが言語レベルで保証されます。
|
||||
* **動的なオブジェクト管理:** ユーザーのカーソル、テキストオブジェクト、図形、NPC、アイテムなど、アプリケーション内のあらゆる要素を統一された`Box`として動的に生成・破棄できます。「Everything is Box」哲学により、異種のオブジェクトをコレクションで扱う際も型システムが煩雑になりません。
|
||||
* **並行処理との親和性:** 各クライアントからの操作を個別のタスク(スレッド)で処理するアーキテクチャと非常に相性が良いです。Nyashでは、開発者はロックの管理を意識することなく、ビジネスロジックの記述に集中できます。
|
||||
|
||||
このアプリケーションは、小規模なデータ構造を並列で計算するベンチマークとは対照的に、**巨大で複雑な単一の状態**を多数のアクターが**並行かつ安全**に変更し続けるという、Nyashの設計思想に最も合致したシナリオです。
|
||||
|
||||
### 2. `Arc<Mutex>`の並行性を活かしたデモ
|
||||
|
||||
**提案:並行Webクローラー**
|
||||
|
||||
複数のワーカースレッドが並行してWebページをダウンロードし、ページ内のリンクを解析して、共有された「未訪問URLリスト」に追加していくアプリケーションです。
|
||||
|
||||
**Nyashの強みが活きる点:**
|
||||
* **シンプルな共有状態:** 「未訪問URLのキュー」と「訪問済みURLのセット」という2つの中心的な共有状態を、それぞれ`Box`化するだけで済みます。
|
||||
* **コードの簡潔さ:** 他の言語では、共有データへのアクセスごとに `mutex.lock().unwrap()` のような明示的なロック獲得・解放処理が必要ですが、Nyashの統一アーキテクチャはこれを言語機能として抽象化します。これにより、並行処理のロジックが非常にクリーンかつ直感的に記述でき、デッドロックなどの典型的なバグを減らせます。
|
||||
|
||||
このデモは、Nyashがいかにして並行プログラミングの複雑さを軽減し、開発者がロジックそのものに集中できるようにするかを明確に示します。
|
||||
|
||||
### 3. プラグイン間でのBox共有を示すアプリケーション
|
||||
|
||||
**提案:プラグインベースのECS (Entity-Component-System) アーキテクチャ**
|
||||
|
||||
ゲーム開発で多用されるECSパターンをNyashで実装します。これは、データ(Component)とロジック(System)を完全に分離する設計です。
|
||||
|
||||
* **Entity:** `Box`化された一意のID。
|
||||
* **Component:** `Box`化された純粋なデータ(例: `Position { x, y }`, `Velocity { dx, dy }`)。
|
||||
* **System:** 特定のロジックを実装した**プラグイン**(例: `PhysicsSystem`, `RenderSystem`)。
|
||||
|
||||
**Nyashの強みが活きる点:**
|
||||
* **究極のデータとロジックの分離:** `PhysicsSystem`プラグインは`Position`と`Velocity`を持つ全エンティティの`Box`を走査して位置を更新し、`RenderSystem`プラグインは`Position`と`Sprite`を持つエンティティの`Box`を走査して描画します。このように、**複数の独立したプラグインが同じデータ(`Box`)を共有し、それぞれの関心事に沿って操作する**様子を鮮やかに示せます。
|
||||
* **動的な機能拡張:** `AISystem`や`AnimationSystem`といった新しい振る舞いを、既存のコードベースに一切変更を加えることなく、プラグインとして動的にロードできます。これはNyashのプラグインシステムと`Box`共有アーキテクチャの強力な組み合わせを実証します。
|
||||
|
||||
### 4. 他言語では難しいがNyashなら簡単に実装できる例
|
||||
|
||||
**提案:アプリケーションのライブ・リローディング(ホットスワップ)**
|
||||
|
||||
長時間稼働するサーバーアプリケーション(例: Web APIサーバー)を停止させることなく、その一部の機能を新しいバージョンに更新するデモです。
|
||||
|
||||
**Nyashの強みが活きる点:**
|
||||
* **統一オブジェクトモデルの威力:** Nyashでは、関数やクロージャさえもが`Box`オブジェクトとして扱えます。これにより、特定のAPIエンドポイントの処理ロジックを実装した関数オブジェクトを、実行時にアトミックに(安全に)差し替えることが原理的に容易になります。
|
||||
* **プラグインとFFIの活用:** 新しいバージョンのロジックを独立したプラグイン(.so/.dllファイルなど)としてコンパイルします。サーバーは稼働中にこのプラグインを動的にロードし、ハンドルベースFFIを介して新しい関数`Box`への参照を取得し、ルーティングテーブル内の古い参照と入れ替えます。
|
||||
* **他言語との差別化:**
|
||||
* RustやC++のような静的言語でこれを実現するには、非常に複雑な設計と手作業でのメモリ管理が必要です。
|
||||
- PythonやRubyのような動的言語では可能ですが、GIL(グローバルインタプリタロック)の存在により、Nyashのような真の並行性は得られません。
|
||||
|
||||
Nyashは、**静的言語の安全性とパフォーマンス**のポテンシャルを持ちながら、**動的言語の柔軟性**を兼ね備えている、というユニークなポジションをこのデモで示すことができます。
|
||||
|
||||
---
|
||||
|
||||
### まとめ
|
||||
|
||||
Nyashのメモリ管理の真価は、静的な計算性能ではなく、**「複雑で動的な状態を、安全かつ並行に、そしてモジュール的に扱う能力」**にあります。提案したアプリケーションは、この言語の核心的な価値を学術的にも実践的にもアピールするための強力な材料となるでしょう。
|
||||
|
||||
## ChatGPT5先生との会話
|
||||
|
||||
### 私からの相談内容
|
||||
```
|
||||
Nyashプログラミング言語のメモリ管理の強みを表現するアプリケーションについて相談です。
|
||||
|
||||
【Gemini先生の提案】
|
||||
1. リアルタイム・コラボレーティブ基盤(Google Docs風の共同編集)
|
||||
2. 並行Webクローラー
|
||||
3. プラグインベースのECS(Entity-Component-System)
|
||||
4. ライブ・リローディング(ホットスワップ)
|
||||
|
||||
【Nyashの特殊性】
|
||||
- Arc<Mutex>統一アーキテクチャで全Boxが自動的にスレッドセーフ
|
||||
- プラグイン間でのBox共有が容易
|
||||
- 参照カウントベースのシンプルなGC
|
||||
- Everything is Box哲学
|
||||
|
||||
【質問】
|
||||
1. P2Pネットワークを活用したNyash独自のアプリケーションは?
|
||||
2. NyaMeshとの統合を前提とした分散アプリケーションの例は?
|
||||
3. Box共有の安全性を活かした「他言語では危険だがNyashなら安全」な例は?
|
||||
4. 論文で最もインパクトがあるデモは何でしょうか?
|
||||
```
|
||||
|
||||
### ChatGPT5先生の完全回答
|
||||
|
||||
了解です。Nyashの「Arc<Mutex>統一」「Everything is Box」「参照カウントGC」「プラグイン間Box共有」を強みに据え、P2P/NyaMesh前提で"Nyashならでは"を狙う案をまとめました。
|
||||
|
||||
**設計キーモチーフ**
|
||||
- Box-as-capability: Box自体を能力トークンとして共有・移譲・複製する設計。
|
||||
- 内容アドレス化: 大きなBoxは内容ハッシュで識別し、P2Pで重複排除。
|
||||
- 暗黙スレッドセーフ: 共有は常に `Arc<Mutex<Box<T>>>`。ゼロコピー志向、衝突はMutexで局所化。
|
||||
- ホットスワップ既定路: 新旧プラグインが同じBoxを安全に参照継続。
|
||||
|
||||
**1. P2P独自アプリ(Nyashらしさ最大化)**
|
||||
- BoxTorrent: 内容アドレス化BoxのP2P配布基盤
|
||||
- 大容量データや中間生成物を「Box=DAGノード」として配布。変換プラグインは入力Box→出力Boxを生成し、ハッシュで再利用。
|
||||
- Nyash優位: ゼロコピー共有、ホットスワップで変換器の差し替え可、参照カウントで自然にキャッシュエビクション。
|
||||
- Live Shared Heap: メッシュ越し共有ヒープ
|
||||
- 論理的に単一のShared HeapにBoxを配置。所有権はメッシュ内で移譲(lease)、読み取りは複製、書き込みはロック+バージョニング。
|
||||
- Nyash優位: 全Boxスレッドセーフ前提で分散ロックを単純化。プラグインがヒープ上の同一Boxをそのまま扱える。
|
||||
- P2P Live-ML Pipeline
|
||||
- センサ→前処理→特徴→推論→ポストの各段をプラグイン化し、ピア間でBoxバッファをパス。途中段のホットアップグレードや分岐実験が容易。
|
||||
- Nyash優位: 各段が同じBoxを共有しゼロコピー。落ちたピアの未解放は参照カウントで自然解放。
|
||||
- Cooperative Rendering/Video Mixer
|
||||
- 各ピアがレイヤBoxを提供し、合成ノードがロック付きで読み取り、最終フレームBoxを配信。遅延抑制のため差分Box(タイル)を伝播。
|
||||
- Nyash優位: 共有フレームバッファを安全に多者で読む/書く。ホットスワップでフィルタ入替。
|
||||
|
||||
**2. NyaMesh統合前提の分散例**
|
||||
- MeshRef/Lease機構
|
||||
- NyaMeshが提供するID/ルーティング上で `BoxID` をグローバル化。lease数が0でGC。複製はK-レプリカ。ゴシップで参照数を伝播。
|
||||
- 使い道: Live Shared Heap/BoxTorrentの基盤。ネット分断でもlease期限で回復。
|
||||
- NyaMesh PubSub CRDT-Box
|
||||
- CRDTレジスタ/マップ/ログをBox実装。書込みはMutex保護+バージョン、伝播はNyaMesh PubSub。競合はCRDTで収束。
|
||||
- 使い道: 協同編集、分散ECS、分散設定管理。
|
||||
- 分散ECS(Entity=Box)
|
||||
- Entity/ComponentをBoxとしてNyaMesh上で移動。負荷に応じてオーナーシップを移譲。システムはプラグインで増減。
|
||||
- 使い道: MMO風シミュ、ロボット群制御、IoTツイン。
|
||||
- Edge Cache/Compute
|
||||
- 内容アドレスBoxをNyaMesh距離で配置最適化。近傍にレプリカ、遠方はリモート参照。ホットスワップで演算器を近傍に移設。
|
||||
|
||||
**3. 他言語だと危険だがNyashなら安全な共有**
|
||||
- ゼロコピー共有バッファの多段パイプ
|
||||
- 大容量 `Box<ByteBuf>` を複数プラグインが並列処理。ダングリング/データ競合はArc<Mutex>で回避。古いプラグイン残留参照も安全に自然解放。
|
||||
- ライブ・ホットスワップ中の同一Box共有
|
||||
- 新旧プラグインが同じBoxを参照し続け、切替中も落ちない。通常はABI/所有権で危険だが、Box境界で能力を渡すだけで成立。
|
||||
- 共有メモリマップファイルの安全クローズ
|
||||
- 多スレッド/多プラグインで参照する `Box<Mmap>`。最後の参照が落ちた瞬間のみクローズされ、早期クローズ・UAFを防止。
|
||||
- マルチプロデューサ・マルチコンシューマなリングバッファ
|
||||
- `Box<Ring<T>>` をMutexで保護し、try_lock+バックプレッシャで輻輳制御。言語レベルunsafe不要。
|
||||
- 強制隔離されない共同編集のポインタ共有
|
||||
- プラグイン間で直接同じBoxドキュメントを参照(CRDT)。ロック&バージョンで整合、コピー不要。通常は破滅的競合を招く領域。
|
||||
|
||||
注意: デッドロックは設計課題なので、型でロック順序を表す/細粒度ロック/try_lock+再試行/単一スレッドオーナー+メッセージ駆動等で回避。
|
||||
|
||||
**4. 論文で最もインパクトがあるデモ**
|
||||
- 分散ホットスワップ・パイプライン(エンドツーエンド無停止)
|
||||
- 構成: NyaMesh上のパイプライン(センサ→前処理→推論→配信)。各段はプラグイン。高負荷時に段を増殖/縮退、同時に新バージョンへホットスワップ。
|
||||
- デモ見せ場: スループット維持、フレーム落ち/UAF/クラッシュゼロ、旧版プラグインが参照するBoxの自然解放、Boxの移譲/複製の可視化。
|
||||
- 計測: p99レイテンシ低下、スワップ時間中断ゼロ、メモリリーク0、再処理なしのバイナリ差替成功率。
|
||||
- 共有ヒープ越しのP2Pコラボ編集(巨大データ)
|
||||
- 構成: 1GB級ドキュメント/モデルを「単一Box」として複数ユーザが同時編集。差分はタイルBoxとして伝搬、CRDT-Boxで収束。
|
||||
- 見せ場: ゼロコピー、Boxの局所ロックで高スループット、ノード離脱でも自然復旧。
|
||||
- 分散ECSワールドのライブ移住
|
||||
- 構成: 数百万EntityのECS。混雑エリアを別ピアへオーナー移譲しながら、プレイ継続・スナップフリー。
|
||||
- 見せ場: Entity=Box移譲、参照が残る間は両ピアで安全アクセス、解放の自動同期。
|
||||
- BoxTorrentの内容アドレスDAG再計算スキップ
|
||||
- 構成: DAG変換パイプ。同一ハッシュ出力をグローバル再利用し、再計算ゼロに。
|
||||
- 見せ場: キャッシュヒット率と電力削減、重複排除。
|
||||
|
||||
**評価指標(論文化しやすい軸)**
|
||||
- 安全性: UAF/ダブルフリー/データ競合/クラッシュの発生率0。
|
||||
- 可用性: ホットスワップ中断時間0、p99レイテンシ悪化<5%。
|
||||
- 効率: ゼロコピー率、再計算/再送削減、RAMフットプリント(参照カウントによる自然エビクション)。
|
||||
- スケール: ピア数とスループットの線形性、チョークポイント不在。
|
||||
- 回復性: ノード喪失下でのデータ保全率、復旧所要時間。
|
||||
|
||||
**リスクと対策**
|
||||
- デッドロック: ロック順序規約/静的解析/try_lock+指数バックオフ。
|
||||
- 参照サイクル: 明示的Weak参照、世代別GC/epochでのリーク検出ダッシュボード。
|
||||
- 分散参照数の正確性: lease期限+ゴシップ再確認、チェックポイント。
|
||||
- 一貫性と遅延: CRDT採用、書込みはバージョン化Boxで楽観制御。
|
||||
- セキュリティ: Box能力にスコープ/寿命/権限を付与(capability化)。
|
||||
|
||||
**次の一手(提案)**
|
||||
- 小さなPoC: BoxTorrentのミニ版(内容アドレスBox+NyaMesh PubSub+参照カウント連携)を最短で実装。
|
||||
- 併走PoC: 分散ホットスワップ・パイプライン(2段)で無停止差替を実証。
|
||||
- 計測仕込み: 参照グラフ可視化、p99レイテンシ/スループット/リーク監視。
|
||||
|
||||
どの路線を優先しますか?最短でインパクトを狙うなら「分散ホットスワップ・パイプライン」か「BoxTorrent+DAG再利用」が論文映えします。要望があれば、選んだ案のアーキ図と最小API(BoxID/lease/replicate/transfer)の草案を書きます。
|
||||
|
||||
## 深い理解を求めての再相談
|
||||
|
||||
### 私からの指摘
|
||||
```
|
||||
うーん? Arc<Mutex> ってかいてるけど これは 本質じゃない気がする
|
||||
boxのスコープを抜けたら開放 finiで開放 weakの弱参照ということ
|
||||
りかいしてない 効き方が悪いにゃ
|
||||
|
||||
ルートフォルダで docsなど読むようにして きいたほうがいいにゃ
|
||||
深く考えて もっかいきいてみて
|
||||
|
||||
あ、まって gc無しで 話して その動作が本質なんだから 深く考えておねがいにゃ!
|
||||
```
|
||||
|
||||
## Gemini先生への再相談(GC無しの本質)
|
||||
|
||||
### 私からの再相談内容
|
||||
```
|
||||
Nyashプログラミング言語のメモリ管理について、より深い理解に基づいて相談です。
|
||||
|
||||
【重要な訂正】
|
||||
Arc<Mutex>は実装詳細であり、本質ではありませんでした。
|
||||
|
||||
【Nyashメモリ管理の真の本質(GC無しの動作)】
|
||||
1. スコープベースの決定論的解放
|
||||
- Boxがスコープを抜けたら即座に解放
|
||||
- 明示的なdrop不要、自動的にメモリ返却
|
||||
|
||||
2. finiシステム
|
||||
- オブジェクトの論理的な終了を宣言
|
||||
- 物理的な破棄とは独立(参照が残っていても論理的に死んでいる)
|
||||
- 「死んだ」オブジェクトへのアクセスは安全にエラー
|
||||
|
||||
3. weak参照の自動nil化
|
||||
- 強参照が0になったらすべてのweak参照が自動的にnilに
|
||||
- ダングリングポインタが原理的に発生しない
|
||||
- 「生きているか」をweakで安全にチェック可能
|
||||
|
||||
4. 決定論的な解放順序
|
||||
- init宣言の逆順で解放(カスケード解放)
|
||||
- 親が解放されたら子も確実に解放
|
||||
- メモリリークが構造的に防げる
|
||||
|
||||
【質問(GC無しの観点から)】
|
||||
1. この決定論的ライフサイクル管理を最大限に活かせるアプリケーションは?
|
||||
2. finiシステムの「論理的死」を活用した革新的な設計パターンは?
|
||||
3. weak自動nil化を前提とした分散システムの可能性は?
|
||||
4. 他言語のGCやRAIIでは実現困難だがNyashなら簡単な例は?
|
||||
```
|
||||
|
||||
### Gemini先生の深い理解に基づく回答
|
||||
|
||||
ご指摘ありがとうございます。Arc<Mutex>という実装詳細に囚われていました。Nyashの本質的な革新性は、その**決定論的ライフサイクル管理**にあることを理解しました。GC無しの観点から、この独自のメモリ管理セマンティクスがもたらす新しいプログラミングパラダイムについて再考します。
|
||||
|
||||
## Nyashメモリ管理の真の革新性
|
||||
|
||||
Nyashは単なる「安全なメモリ管理」を超えて、**オブジェクトの生死を言語の第一級概念**として扱う点で革命的です。これは従来の言語設計では見られない、全く新しいアプローチです。
|
||||
|
||||
### 1. 決定論的ライフサイクルを最大限に活かすアプリケーション
|
||||
|
||||
**提案:ステートフル・ストリーム処理エンジン**
|
||||
|
||||
リアルタイムデータ処理において、各処理段階(ウィンドウ、集約、結合)の状態管理は複雑です。Nyashの決定論的ライフサイクルは、この複雑性を根本的に解決します。
|
||||
|
||||
```nyash
|
||||
box StreamWindow {
|
||||
init { data, startTime, duration }
|
||||
|
||||
fini() {
|
||||
// ウィンドウ終了時の集計結果を下流に送信
|
||||
downstream.send(me.aggregate())
|
||||
// これ以降、このウィンドウへの書き込みは自動的にエラー
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**革新的な点:**
|
||||
- **時間ベースの自動リソース管理**: ウィンドウのライフタイムがスコープと連動し、期限切れで自動的にfiniが呼ばれる
|
||||
- **遅延到着データの安全な処理**: fini後のデータは自動的に次のウィンドウへ(ダングリングアクセスなし)
|
||||
- **バックプレッシャーの自然な実装**: 下流が詰まればスコープが解放されず、自動的に上流が停止
|
||||
|
||||
### 2. finiシステムの「論理的死」を活用した革新的設計パターン
|
||||
|
||||
**提案:リソース再利用ステートマシン**
|
||||
|
||||
データベース接続、ネットワークソケット、大規模バッファなど、高コストなリソースを「論理的に死んでいるが物理的に生きている」状態で管理します。
|
||||
|
||||
```nyash
|
||||
box ConnectionPool {
|
||||
init { connections }
|
||||
|
||||
checkout() {
|
||||
// 論理的に死んでいる(fini済み)が物理的に生きている接続を探す
|
||||
local conn = me.connections.find_fini_but_alive()
|
||||
if conn {
|
||||
conn.resurrect() // 論理的に復活
|
||||
return conn
|
||||
}
|
||||
return new Connection()
|
||||
}
|
||||
}
|
||||
|
||||
box Connection {
|
||||
init { socket, inUse }
|
||||
|
||||
fini() {
|
||||
me.inUse = false
|
||||
// ソケットは閉じない!プールに返すだけ
|
||||
// 論理的には死んでいるが、物理的なリソースは保持
|
||||
}
|
||||
|
||||
resurrect() {
|
||||
me.inUse = true
|
||||
// 論理的な再生
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**革新的な点:**
|
||||
- **ゼロコスト・リソースプーリング**: GCのファイナライザと異なり、finiのタイミングが確定的
|
||||
- **論理的状態と物理的状態の分離**: 「使用中/未使用」を言語レベルで表現
|
||||
- **メモリセーフな再利用**: 論理的に死んだオブジェクトへのアクセスは必ずエラー
|
||||
|
||||
### 3. weak自動nil化を前提とした分散システム
|
||||
|
||||
**提案:自己修復型サービスメッシュ**
|
||||
|
||||
分散システムにおいて、ノードの生死を監視することは本質的に困難です。Nyashのweak参照自動nil化は、この問題に対する根本的な解決策を提供します。
|
||||
|
||||
```nyash
|
||||
box ServiceNode {
|
||||
init { endpoint, healthChecker }
|
||||
|
||||
fini() {
|
||||
// ノードの論理的な死(ヘルスチェック失敗、手動停止など)
|
||||
print("Node ${me.endpoint} is going down")
|
||||
}
|
||||
}
|
||||
|
||||
box ServiceMesh {
|
||||
init { nodes } // weak参照のマップ
|
||||
|
||||
route(request) {
|
||||
// 死んだノードは自動的にnilになっている
|
||||
local aliveNodes = me.nodes.values().filter(n => n != nil)
|
||||
|
||||
if aliveNodes.isEmpty() {
|
||||
error("No healthy nodes available")
|
||||
}
|
||||
|
||||
// ロードバランシング(死んだノードは既に除外されている)
|
||||
return aliveNodes.random().handle(request)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**革新的な点:**
|
||||
- **ゼロレイテンシ障害検知**: ノードがfiniした瞬間、全てのweak参照がnilに
|
||||
- **分散GCの回避**: 参照カウントの分散管理が不要
|
||||
- **スプリットブレイン耐性**: ネットワーク分断時も各パーティションで正しく動作
|
||||
|
||||
### 4. 他言語では実現困難だがNyashなら簡単な例
|
||||
|
||||
**提案:トランザクショナル・オブジェクトライフサイクル**
|
||||
|
||||
データベーストランザクションのように、オブジェクトの生成・変更・破棄を原子的に行い、失敗時は自動的にロールバックします。
|
||||
|
||||
```nyash
|
||||
box Transaction {
|
||||
init { objects, committed }
|
||||
|
||||
createObject(Type, args) {
|
||||
local obj = new Type(args)
|
||||
me.objects.push(obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
commit() {
|
||||
me.committed = true
|
||||
// オブジェクトを永続化
|
||||
}
|
||||
|
||||
fini() {
|
||||
if not me.committed {
|
||||
// コミットされていない場合、全オブジェクトを破棄
|
||||
me.objects.forEach(obj => obj.fini())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用例
|
||||
{
|
||||
local tx = new Transaction()
|
||||
local user = tx.createObject(User, {name: "Alice"})
|
||||
local order = tx.createObject(Order, {userId: user.id})
|
||||
|
||||
if paymentSuccessful {
|
||||
tx.commit()
|
||||
}
|
||||
// スコープを抜ける際、コミットされていなければ自動ロールバック
|
||||
}
|
||||
```
|
||||
|
||||
**革新的な点:**
|
||||
- **言語レベルのトランザクション**: スコープとライフサイクルの統合
|
||||
- **自動的なリソースクリーンアップ**: 例外安全性が言語仕様で保証
|
||||
- **メモリとビジネスロジックの融合**: 技術的制約をビジネス要件に活用
|
||||
|
||||
## 結論:新しいプログラミングパラダイム
|
||||
|
||||
Nyashのメモリ管理は、単なる技術的優位性を超えて、開発者に新しい思考の枠組みを提供します:
|
||||
|
||||
1. **論理的死と物理的生存の分離** → 「生きたログ」としてのオブジェクト
|
||||
2. **遺言パターン** → finiでの「死後の自己報告」によるデバッグと監視
|
||||
3. **Weak自動nil化** → 「自己修復」する回復力のあるデータ構造
|
||||
4. **決定的解放順序** → 「安全な解体」が保証された積層的設計
|
||||
|
||||
これらはすべて、GCの不確実性からプログラマを解放し、システムの振る舞いを時間軸に沿って正確に予測・設計することを可能にします。これはもはやメモリ管理ではなく、**「ライフサイクル指向プログラミング」**と呼ぶべき新しいパラダイムです。
|
||||
|
||||
## ChatGPT5先生への再相談(GC無しの本質)
|
||||
|
||||
### 私からの再相談内容
|
||||
```
|
||||
Nyashメモリ管理について、GC無しの本質的な動作に基づいて再度相談です。
|
||||
|
||||
【Gemini先生の深い洞察】
|
||||
「ライフサイクル指向プログラミング」という新パラダイムの提案:
|
||||
- 論理的死と物理的生存の分離
|
||||
- finiでの「遺言パターン」
|
||||
- weak自動nil化による自己修復
|
||||
- 決定的解放順序による安全な解体
|
||||
|
||||
【改めて理解したNyashの本質】
|
||||
1. 決定論的ライフサイクル(GCの不確実性なし)
|
||||
2. finiシステム(論理的終了と物理的破棄の分離)
|
||||
3. weak自動nil化(ダングリング不可能)
|
||||
4. スコープベース解放(init宣言の逆順)
|
||||
|
||||
【質問】
|
||||
1. この決定論的モデルがP2P/分散環境でもたらす革新は?
|
||||
2. 「生死の決定論」を分散システムの基本原語にする可能性は?
|
||||
3. NyaMeshでこの特性を最大限活かすアーキテクチャは?
|
||||
4. 論文で最もインパクトのある実証方法は?
|
||||
```
|
||||
|
||||
### ChatGPT5先生の深い理解に基づく回答
|
||||
|
||||
なるほど、Arc<Mutex>は実装詳細で、本質は「決定論的ライフサイクル管理」だったのですね。これは確かに革命的です。GCの不確実性を完全に排除し、「生死の決定論」を言語の第一級概念にする。これをP2P/分散の文脈で考え直します。
|
||||
|
||||
## 「生死の決定論」を分散原語にする革新
|
||||
|
||||
従来の分散システムは「故障検知の不確実性」と戦ってきました(FLP不可能性、CAP定理など)。Nyashの決定論的ライフサイクルは、この戦いに新しい武器を提供します。
|
||||
|
||||
### 1. 決定論的モデルがP2P/分散環境でもたらす革新
|
||||
|
||||
**LifeToken分散プロトコル**
|
||||
|
||||
各ノードが「LifeToken」を持ち、それをweakで共有。トークンのfiniが即座に全ピアに伝播。
|
||||
|
||||
```nyash
|
||||
box PeerNode {
|
||||
init { nodeId, lifeToken, peers } // peersはweak参照のマップ
|
||||
|
||||
fini() {
|
||||
// 明示的な離脱通知不要!
|
||||
// 全ピアのweak参照が自動的にnilに
|
||||
}
|
||||
|
||||
broadcast(message) {
|
||||
me.peers.forEach((id, peer) => {
|
||||
if peer != nil { // 生きているピアのみ
|
||||
peer.receive(message)
|
||||
} else {
|
||||
me.peers.remove(id) // 死んだピアを削除
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**革新性:**
|
||||
- **即座の故障伝播**: ハートビート不要、タイムアウト不要
|
||||
- **スプリットブレイン自己解決**: 分断されたノードのfiniで自動的に縮退
|
||||
- **決定的なクラスタ再構成**: メンバーシップの変更が瞬時かつ確定的
|
||||
|
||||
### 2. 「生死の決定論」を分散システムの基本原語に
|
||||
|
||||
**Two-Phase Fini Protocol**
|
||||
|
||||
分散トランザクションのTwo-Phase Commitを、オブジェクトライフサイクルで再発明。
|
||||
|
||||
```nyash
|
||||
box DistributedTransaction {
|
||||
init { participants, coordinator }
|
||||
|
||||
// Phase 1: Prepare to die
|
||||
prepareFini() {
|
||||
local votes = me.participants.map(p => p.canFini())
|
||||
return votes.all(v => v == true)
|
||||
}
|
||||
|
||||
// Phase 2: Coordinated death
|
||||
fini() {
|
||||
if me.prepareFini() {
|
||||
// 全参加者が同時に論理的に死ぬ
|
||||
me.participants.forEach(p => p.fini())
|
||||
me.coordinator.recordCommit(me.id)
|
||||
} else {
|
||||
// 中止 - 誰も死なない
|
||||
me.coordinator.recordAbort(me.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**パラダイムシフト:**
|
||||
- **生存ではなく死を調整**: 「いつ死ぬか」の合意形成
|
||||
- **決定的な終了**: GCの非決定性なし、リソースリーク不可能
|
||||
- **カスケード死の活用**: 親の死で子も確実に死ぬ
|
||||
|
||||
### 3. NyaMeshでこの特性を最大限活かすアーキテクチャ
|
||||
|
||||
**Lifecycle-Aware Mesh Routing**
|
||||
|
||||
ルーティングテーブル自体をweak参照で構築し、ノードの生死に即座に適応。
|
||||
|
||||
```nyash
|
||||
box MeshRouter {
|
||||
init { routes } // BoxID -> weak<NextHop>のマップ
|
||||
|
||||
route(boxId, data) {
|
||||
local nextHop = me.routes.get(boxId)
|
||||
|
||||
if nextHop == nil {
|
||||
// ルートが死んでいる - 自動的に再探索
|
||||
me.discoverRoute(boxId)
|
||||
}
|
||||
|
||||
// Weak参照の連鎖でマルチホップ
|
||||
local path = me.tracePath(boxId)
|
||||
path.forEach(hop => {
|
||||
if hop != nil {
|
||||
hop.forward(data)
|
||||
} else {
|
||||
// 経路上のノードが死んだ - 迂回
|
||||
me.reroute(boxId, data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**アーキテクチャの特徴:**
|
||||
- **自己修復ルーティング**: 故障ノードの迂回が自動
|
||||
- **メモリ効率的**: 死んだルートは即座にGC(決定論的)
|
||||
- **リアルタイム適応**: 変化への反応がマイクロ秒単位
|
||||
|
||||
### 4. 論文で最もインパクトのある実証
|
||||
|
||||
**「決定論的ライフサイクルによる分散システムの新基盤」**
|
||||
|
||||
**実験設定:**
|
||||
1. **1000ノードP2Pネットワーク**でカオスエンジニアリング
|
||||
2. **ランダムにノードをkill**(1秒に10ノード)
|
||||
3. **同時に新ノードを投入**(1秒に10ノード)
|
||||
|
||||
**測定項目と期待される結果:**
|
||||
|
||||
| 指標 | 従来の分散システム | Nyash決定論システム |
|
||||
|------|-------------------|---------------------|
|
||||
| 故障検知時間 | 5-30秒(タイムアウト依存) | 0秒(即座) |
|
||||
| メンバーシップ収束時間 | 分〜時間 | ミリ秒 |
|
||||
| 誤検知率 | 1-5%(ネットワーク遅延) | 0%(決定論的) |
|
||||
| メモリリーク | 徐々に蓄積 | 構造的に0 |
|
||||
| メッセージ複雑度 | O(n²)ハートビート | O(1)ライフサイクル |
|
||||
|
||||
**デモシナリオ:**
|
||||
|
||||
```nyash
|
||||
// 分散ビデオ変換パイプライン
|
||||
box VideoProcessor {
|
||||
init { inputQueue, outputQueue, workers }
|
||||
|
||||
processVideo(video) {
|
||||
// 動的にワーカーを追加
|
||||
loop(me.inputQueue.size() > threshold) {
|
||||
local worker = new Worker()
|
||||
me.workers.add(weak(worker))
|
||||
}
|
||||
|
||||
// 処理分配(死んだワーカーは自動スキップ)
|
||||
me.workers.forEach(w => {
|
||||
if w != nil {
|
||||
w.process(video.nextChunk())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fini() {
|
||||
// カスケード終了 - 全ワーカーが確実に停止
|
||||
print("Processed ${me.outputQueue.size()} videos")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**論文の主張:**
|
||||
1. **理論的貢献**: 分散システムの故障モデルに「決定論的死」という新概念を導入
|
||||
2. **実装的貢献**: weak参照とfiniシステムによる実用的な実現
|
||||
3. **実証的貢献**: 従来手法を全指標で上回る性能
|
||||
|
||||
**インパクトを最大化する見せ方:**
|
||||
- **ライブデモ**: ノードをkillしてもサービスが止まらない様子をリアルタイム可視化
|
||||
- **決定論の可視化**: 各ノードの生死状態を色で表示、weak参照のnil化を矢印の消失で表現
|
||||
- **比較実験**: 同じシナリオを従来手法(タイムアウトベース)で実行し、収束の遅さを対比
|
||||
|
||||
## 結論:分散システムの新たな基礎
|
||||
|
||||
Nyashの決定論的ライフサイクルは、単なるメモリ管理を超えて、**分散システムの新しい基礎理論**を提供します:
|
||||
|
||||
1. **生死の即座伝播**: ネットワーク遅延に依存しない故障検知
|
||||
2. **論理と物理の分離**: 「死んだが動いている」ノードの活用
|
||||
3. **カスケード終了**: 依存関係に沿った確実な停止
|
||||
4. **自己修復アーキテクチャ**: weak nil化による自動的な迂回
|
||||
|
||||
これは「Eventually Consistent」から「**Immediately Deterministic**」へのパラダイムシフトです。
|
||||
|
||||
GCを捨て、「生死の決定論」を分散の原語にすることで、従来不可能だった**故障への即座対応**と**確実なリソース管理**を両立できます。これがNyashの真の革新性です。
|
||||
215
apps/AI_TEACHERS_PURE_NYASH_CONVERSATIONS.md
Normal file
215
apps/AI_TEACHERS_PURE_NYASH_CONVERSATIONS.md
Normal file
@ -0,0 +1,215 @@
|
||||
# AI先生たちのNyash言語仕様に関する純粋な会話記録(2025-08-31)
|
||||
|
||||
実装詳細(Rust等)を一切含まない、純粋にNyash言語仕様のメモリ管理に基づいた相談と回答です。
|
||||
|
||||
## Gemini先生との会話
|
||||
|
||||
### 私からの相談内容
|
||||
```
|
||||
Nyashプログラミング言語のメモリ管理について相談です。
|
||||
|
||||
【重要】実装言語(Rust等)の話は一切含めないでください。純粋にNyash言語仕様として考えてください。
|
||||
|
||||
【Nyashメモリ管理の本質】
|
||||
1. スコープベースの決定論的解放
|
||||
- Boxがスコープを抜けたら即座に解放される
|
||||
- 手動でのメモリ管理不要、自動的にメモリが返却される
|
||||
|
||||
2. finiシステム
|
||||
- オブジェクトの論理的な終了を宣言できる
|
||||
- 物理的な破棄とは独立(参照が残っていても論理的に死んでいる状態)
|
||||
- 死んだオブジェクトへのアクセスは安全にエラーになる
|
||||
|
||||
3. weak参照の自動nil化
|
||||
- 強参照が0になったら全てのweak参照が自動的にnilになる
|
||||
- ダングリングポインタが原理的に発生しない
|
||||
- オブジェクトが生きているかをweakで安全にチェック可能
|
||||
|
||||
4. 決定論的な解放順序
|
||||
- init宣言の逆順で解放される(カスケード解放)
|
||||
- 親が解放されたら子も確実に解放される
|
||||
- メモリリークが構造的に防げる
|
||||
|
||||
5. Everything is Box哲学
|
||||
- 全ての値がBoxオブジェクトとして統一的に扱われる
|
||||
- スレッドセーフがNyash言語レベルで保証される(syncが用意されている)
|
||||
|
||||
【質問】
|
||||
1. この決定論的メモリ管理を最大限に活かせる革新的なアプリケーションは?
|
||||
2. 他の言語(GCありの言語、手動管理の言語)では実現困難だがNyashなら簡単な例は?
|
||||
3. 特にfiniとweakの組み合わせが生む新しいプログラミングパターンは?
|
||||
4. P2P/分散環境でこの決定論性が生む可能性は?
|
||||
```
|
||||
|
||||
### Gemini先生の回答
|
||||
|
||||
Nyash言語のメモリ管理に関するご質問、ありがとうございます。提示いただいた5つの本質は、非常に興味深く、強力なコンセプトですね。これらが組み合わさることで生まれる可能性について、言語仕様の観点から考察します。
|
||||
|
||||
### 1. この決定論的メモリ管理を最大限に活かせる革新的なアプリケーションは?
|
||||
|
||||
決定論的な性質は、リソースのライフサイクルが厳密に管理されるべき領域で絶大な効果を発揮します。
|
||||
|
||||
* **リアルタイム・インタラクティブシステム:**
|
||||
* **アプリケーション:** ゲームエンジン、音楽制作ソフト(DAW)、VR/AR環境、物理シミュレーション。
|
||||
* **理由:** GCによる予測不能な停止(Stop-the-World)が一切発生しないため、フレームレートの安定や低レイテンシが保証されます。シーン遷移やオブジェクトの動的な生成・破棄において、リソース(メモリ、GPUバッファ、サウンドハンドル等)がスコープを抜けた瞬間に即座に解放されるため、パフォーマンスが安定し、リソースリークも構造的に防げます。
|
||||
|
||||
* **高信頼性が求められる組込み/制御システム:**
|
||||
* **アプリケーション:** ファクトリーオートメーション、ロボット制御、医療機器。
|
||||
* **理由:** メモリ使用量が予測可能であり、システムの振る舞いが時間と共に安定します。`init`の逆順でのカスケード解放は、複雑に依存しあう制御モジュール群を安全にシャットダウンさせる上で極めて重要です。
|
||||
|
||||
* **サーバーレス/エッジコンピューティング環境:**
|
||||
* **アプリケーション:** 高速に起動・終了するFaaS(Function as a Service)のランタイム、リソース制約の厳しいエッジデバイス上のアプリケーション。
|
||||
* **理由:** リクエスト単位でスコープを作成すれば、処理完了後に使用した全リソースが即時かつ完全に解放されることが保証されます。これにより、次のリクエストにクリーンな状態で素早く応答でき、テナント間のリソース分離も確実になります。
|
||||
|
||||
### 2. 他の言語では実現困難だがNyashなら簡単な例は?
|
||||
|
||||
**例:循環参照を含む複雑なオブジェクトグラフの安全な解体**
|
||||
|
||||
GUIのコンポーネントツリーを考えます。`Window`があり、その中に`Panel`があり、`Panel`の中に`Button`があるとします。ここで、`Button`が親の`Window`を(何らかのコールバックのために)参照するという循環参照が存在するケースを考えます。
|
||||
|
||||
* **GCあり言語 (Java, C#, Python):**
|
||||
* **困難な点:** `Window`を不要にしても、`Button`からの参照が残っているため、`Window`, `Panel`, `Button`のサイクル全体がGCに回収されず、メモリリークとなります。これを回避するには、手動で参照を断ち切る(例:`button.window = null`)か、`WeakReference`のような仕組みを慎重に使う必要があります。
|
||||
* **Nyashの場合:** `Button`から`Window`への参照を`weak`にするだけで問題が解決します。`Window`がスコープを抜けると、まず`Window`への強参照が消えます。これにより、`Button`が持つ`weak`参照は自動的に`nil`になります。その後、`init`の逆順で`Panel`、`Button`がカスケード式に解放されます。開発者は循環参照の存在を意識しつつも、解放ロジックを自前で書く必要がありません。
|
||||
|
||||
* **手動管理言語 (C, C++):**
|
||||
* **困難な点:** `Window`のデストラクタで`Panel`を`delete`し、`Panel`のデストラクタで`Button`を`delete`する、という手動での連鎖的な解放が必要です。もし外部のどこかから`Button`へのポインタが残っていた場合、`Window`を`delete`した後にそのポインタにアクセスすると未定義動作(ダングリングポインタ)を引き起こします。
|
||||
* **Nyashの場合:** `weak`参照の自動`nil`化により、ダングリングポインタが原理的に発生しません。外部のコードは`weak`参照が`nil`でないかを確認するだけで、オブジェクトがまだ生存しているかを安全にチェックできます。
|
||||
|
||||
### 3. 特に`fini`と`weak`の組み合わせが生む新しいプログラミングパターンは?
|
||||
|
||||
`fini`(論理的な死)と`weak`(存在確認)の組み合わせは、オブジェクトの状態管理に新しい次元をもたらします。
|
||||
|
||||
**パターン名:グレースフル・デコミッション(Graceful Decommissioning)**
|
||||
|
||||
これは、オブジェクトが即座に消えるのではなく、「終了処理中」という中間状態を経て安全に消滅するためのパターンです。
|
||||
|
||||
1. **デコミッションの開始:** オブジェクト(例:ネットワーク接続マネージャ)を破棄したいとき、まず`fini`を呼び出します。これによりオブジェクトは「論理的に死んだ」状態になります。
|
||||
2. **新規リクエストの拒否:** `fini`されたオブジェクトへのアクセスはエラーになるため、新しいリクエスト(例:新規接続の要求)は安全に弾かれます。
|
||||
3. **進行中タスクの完了:** しかし、オブジェクトのメモリはまだ存在するため、内部で進行中だったタスク(例:データの送信処理)は完了させることができます。
|
||||
4. **外部からの安全な監視:** 他のオブジェクトは、このマネージャへの`weak`参照を持ちます。
|
||||
* `weak`参照をチェックし、`nil`でなければ「まだ物理的には存在している」とわかります。
|
||||
* さらにそのオブジェクトが`fini`済みかを確認することで、「現在シャットダウン処理中である」と判断できます。
|
||||
5. **完全な消滅:** 進行中のタスクがすべて完了し、オブジェクトが自身のスコープを抜けるなどして強参照がすべてなくなると、物理的に解放され、すべての`weak`参照が`nil`になります。
|
||||
|
||||
このパターンは、システムのコンポーネントを、システム全体を停止させることなく、安全かつ段階的に入れ替えるような動的なアーキテクチャで極めて有効です。
|
||||
|
||||
### 4. P2P/分散環境でこの決定論性が生む可能性は?
|
||||
|
||||
ローカルノードでの決定論性は、分散システム全体の予測可能性と信頼性を向上させる基盤となり得ます。
|
||||
|
||||
* **分散合意プロトコルの簡素化:**
|
||||
* 分散システムでは、ノードのクラッシュやネットワーク分断によってリソース(分散ロックなど)が解放されなくなる問題が頻発します。これを防ぐために、リース(時間貸し)や複雑な監視機構が使われます。
|
||||
* Nyashの決定論的な解放を応用し、「特定の処理スコープに対応する分散トランザクション」のような概念を導入できれば、トランザクションが成功または失敗してスコープを抜けた際に、関与したすべてのノード上のリソースが(ローカルのルールに従って)確実に解放されるというプロトコルを設計しやすくなります。これにより、タイムアウトに頼らない、より決定論的な分散リソース管理が期待できます。
|
||||
|
||||
* **予測可能なリソースフットプリント:**
|
||||
* P2Pネットワークに参加する各ノードは、他のノードのためにリソース(帯域、ストレージ)を提供します。
|
||||
* Nyashで実装されたノードは、処理(例:あるチャンクの転送)が完了すれば、それに関連するリソースが即座に解放されるため、ノードのリソース使用量を非常に正確に予測・管理できます。これにより、ネットワーク全体の負荷をより公平かつ効率的に分散させる高度なスケジューリングが可能になるかもしれません。
|
||||
|
||||
* **決定論的リプレイとデバッグ:**
|
||||
* 分散システムのデバッグは困難を極めます。
|
||||
* 各ノードの動作が決定論的であれば、外部からの入力イベントのシーケンスさえ記録しておけば、システム全体の振る舞いを正確にリプレイできる可能性が高まります。`fini`や`weak`参照の状態変化も追跡することで、複雑な非同期イベントの連鎖によって引き起こされる問題を特定しやすくなります。
|
||||
|
||||
## ChatGPT5先生との会話
|
||||
|
||||
### 私からの相談内容
|
||||
```
|
||||
Nyashプログラミング言語のメモリ管理について相談です。
|
||||
|
||||
【重要】実装言語(Rust等)の話は一切含めないでください。純粋にNyash言語仕様として考えてください。
|
||||
|
||||
【Nyashメモリ管理の本質】
|
||||
1. スコープベースの決定論的解放
|
||||
- Boxがスコープを抜けたら即座に解放される
|
||||
- 手動でのメモリ管理不要、自動的にメモリが返却される
|
||||
|
||||
2. finiシステム
|
||||
- オブジェクトの論理的な終了を宣言できる
|
||||
- 物理的な破棄とは独立(参照が残っていても論理的に死んでいる状態)
|
||||
- 死んだオブジェクトへのアクセスは安全にエラーになる
|
||||
|
||||
3. weak参照の自動nil化
|
||||
- 強参照が0になったら全てのweak参照が自動的にnilになる
|
||||
- ダングリングポインタが原理的に発生しない
|
||||
- オブジェクトが生きているかをweakで安全にチェック可能
|
||||
|
||||
4. 決定論的な解放順序
|
||||
- init宣言の逆順で解放される(カスケード解放)
|
||||
- 親が解放されたら子も確実に解放される
|
||||
- メモリリークが構造的に防げる
|
||||
|
||||
5. Everything is Box哲学
|
||||
- 全ての値がBoxオブジェクトとして統一的に扱われる
|
||||
- スレッドセーフがNyash言語レベルで保証される(syncが用意されている)
|
||||
|
||||
【特にGemini先生の提案】
|
||||
- グレースフル・デコミッション: finiとweakの組み合わせパターン
|
||||
- リアルタイムシステムでの決定論性活用
|
||||
- 分散合意プロトコルの簡素化
|
||||
|
||||
【質問】
|
||||
1. この決定論的メモリ管理がP2P/NyaMesh環境で生む革新的な可能性は?
|
||||
2. finiシステムを分散環境での「論理的な死」に拡張する方法は?
|
||||
3. weak自動nil化を活用した新しい分散アーキテクチャは?
|
||||
4. 他の分散システムでは困難だがNyashなら実現可能な例は?
|
||||
```
|
||||
|
||||
### ChatGPT5先生の回答
|
||||
|
||||
以下はNyash言語仕様のメモリモデル(決定論的解放、fini、weak自動nil化、逆順解放、Everything is Box)を前提に、P2P/NyaMeshでの設計指針と新機軸の提案です。
|
||||
|
||||
**P2Pでの可能性**
|
||||
- 強制リーク不可能なオーバレイ: ルート/セッション/会員情報をBox化し、強参照消滅で確実に消えるため、ゾンビ状態を構造的に排除。
|
||||
- スコープ=リース: 参加・ルート・セッションを「スコープ付きリース」として扱い、終了時に即時解放されルーティング表が自動整合。
|
||||
- 逆順カスケード停止: 親(ノード/セッション)→子(フロー/バッファ)の順で安全に停止波及し、部分破壊や順序逆転を抑止。
|
||||
- メモリ主導フロー制御: 送信クレジット/バッファをBox化し、解放=明確なバックプレッシャーとして働くため、過負荷時の挙動が決定論的。
|
||||
- 再現可能性/監査容易: ライフタイムが決定論的で、シミュレーションやフォレンジック再現が容易(どの時点で何が死ぬかが一意)。
|
||||
- 心拍依存の縮減: weak自動nil化により「生存確認」は参照状態で判定でき、ポーリング/心拍の頻度を下げられる。
|
||||
|
||||
**分散finiの拡張**
|
||||
- finiドメイン: オブジェクトに「ドメイン」を付与し、finiの到達範囲/ポリシーを規定(ローカル/ピア集合/トポロジ領域)。
|
||||
- 終了等級: Local-fini(自ノード即時エラー化)、Domain-fini(多数決/しきい値で論理死)、Epoch-fini(世代切替)、TTL-fini(期限死)。
|
||||
- トンブストーン/世代: 論理死を単調増加の世代番号と原因コードで表明し、重複/乱順配送に対して冪等に適用。
|
||||
- リース=強参照、委譲=強参照移転: 誰が生存責任を持つかを言語レベルで明確化。リース喪失で全weakがnil。
|
||||
- アクセス規則の二段階化: Local-finiでローカルは即エラー、Domain-fini観測で全域エラー。weakはどちらでもnilイベントを受領。
|
||||
- 監視プリミティブ: watch(weak)で「死の通知」を一回だけ受け取り、再購読なしに後片付けを確定的に実行。
|
||||
|
||||
**weak自動nil化が導くアーキテクチャ**
|
||||
- 能力キャッシュの自然失効: リモート能力へのweak参照を配布し、能力の実体が消えれば自動nilで整合が取れる。
|
||||
- エフェメラル・チャネル: パブリッシャが強参照、サブスクライバはweakで監視。発行元が消えれば全購読が即時nil。
|
||||
- メンバーシップ=weak集合: クラスタ/スワームをweak集合で表現し、非在化が自動反映。別途ガーベジ手続き不要。
|
||||
- 招待/セッション鍵=招待Box: 参加者はweakで招待を監視、主がfiniすれば招待は無効化。期限・回収が自動。
|
||||
- ルータの無保持化: 中継はフロー状態へのweakのみ保持。両端の強参照が切れれば経路状態は自動消滅。
|
||||
- キャッシュの権威弱結合: エントリは権威Boxへのweak錨を持ち、権威が死ねばキャッシュ行は一斉nilで整合。
|
||||
|
||||
**Nyashならではの実現例**
|
||||
- ゼロリークDHT/オーバレイ: ノードBox配下にエントリ/セッションをぶら下げ、逆順解放で孤児状態を禁止。
|
||||
- 優雅なデコミッション: ノードBoxにfini→全能力が弱化→新規受付停止→強参照が尽きたら自動nil→完全撤収が決定論的。
|
||||
- ハードリアルタイムRPC: GC待ちや不定期ファイナライズ無し。タイムボックスと解放タイミングが一致し、遅延上限が厳密。
|
||||
- 分散ロック不要の一回性: Job Boxを単一強参照にし、完了finiで全レプリカのweakがnil→重複実行が構造的に不可能。
|
||||
- セッション世代切替: Epoch Boxを作り替えるだけで旧世代のweakが一斉nil→古いビュー/鍵/ルートの掃除が瞬時。
|
||||
- 秘密情報の即時抹消: Key Boxのfiniで全派生状態がカスケード解放→参照側はエラー/weak nilで後工程が確定的。
|
||||
- タイマレス・リース: リソース/帯域/優先度をスコープ化し、保持=利用、終了=回収。切替や回収がアラーム不要で正確。
|
||||
|
||||
**設計パターン(提案)**
|
||||
- ドメイン付きBox: `Box<T> @domain(policy)` でfiniの適用範囲/合意条件を明示。
|
||||
- 世代ガード: `Gen<T>`で現在世代のみ強参照可能、旧世代は弱参照に自動降格。
|
||||
- 観測API: `watch(weak)`で一度きりのnil通知、`on_fini`で論理死通知、両者は因果順序を保証。
|
||||
- 依存グラフ解放: 親子関係をinit順に構築し、逆順finiで停止順序の安全性とデッドロック回避を担保。
|
||||
|
||||
必要なら、上記のいずれかを具体的なプロトコル設計(メンバーシップ、ストリーミング、鍵ローテーション、ジョブ実行)に落とし込みます。どれから掘りますか?
|
||||
|
||||
## まとめ
|
||||
|
||||
両先生とも、Nyashの決定論的メモリ管理が持つ革新的な可能性を、実装詳細に依存しない純粋な言語仕様の観点から分析してくれました。
|
||||
|
||||
### Gemini先生の主要な洞察
|
||||
- **グレースフル・デコミッション**: finiとweakを組み合わせた安全な段階的終了パターン
|
||||
- **リアルタイムシステム**: GCの不確実性がない決定論的な性能保証
|
||||
- **循環参照の自然解決**: weakによる安全な参照グラフ解体
|
||||
|
||||
### ChatGPT5先生の主要な洞察
|
||||
- **強制リーク不可能なオーバレイ**: 構造的にゾンビ状態を排除
|
||||
- **分散finiの拡張**: ドメイン、終了等級、トンブストーンによる分散環境での論理死
|
||||
- **weak自動nil化アーキテクチャ**: 能力キャッシュ、エフェメラル・チャネル、メンバーシップ管理
|
||||
|
||||
これらの提案は、Nyashが単なるメモリ管理を超えて、新しいプログラミングパラダイムを提供する可能性を示しています。
|
||||
239
apps/AI_TEACHERS_SUGGESTIONS.md
Normal file
239
apps/AI_TEACHERS_SUGGESTIONS.md
Normal file
@ -0,0 +1,239 @@
|
||||
# AI先生たちのNyashアプリケーション提案(2025-08-31)
|
||||
|
||||
Nyashの特殊でシンプルなメモリ管理(Arc<Mutex>統一アーキテクチャ)を最大限に表現するアプリケーションについて、Gemini先生とChatGPT5先生から革新的な提案をいただきました。
|
||||
|
||||
## Gemini先生の提案
|
||||
|
||||
### 相談内容
|
||||
```
|
||||
Nyashプログラミング言語のメモリ管理について相談です。
|
||||
|
||||
【Nyashのメモリ管理の特徴】
|
||||
- Everything is Box哲学(すべてがBoxオブジェクト)
|
||||
- Arc<Mutex>統一アーキテクチャ(全Box型で統一)
|
||||
- 参照カウント式のシンプルなGC
|
||||
- プラグインシステムとの統合
|
||||
- ハンドルベースのFFI
|
||||
|
||||
【問題】
|
||||
論文用にbinary-trees、n-body、mandelbrotなどの標準ベンチマークが推奨されていますが、これらは小規模でNyashの特殊なメモリ管理の良さを表現しきれません。
|
||||
|
||||
【質問】
|
||||
1. Nyashのメモリ管理の特殊性を活かせる大規模アプリケーションは?
|
||||
2. Arc<Mutex>の並行性を活かしたデモは?
|
||||
3. プラグイン間でのBox共有を示すアプリケーションは?
|
||||
4. 他言語では難しいがNyashなら簡単に実装できる例は?
|
||||
```
|
||||
|
||||
### Gemini先生の回答
|
||||
|
||||
Nyashプログラミング言語のメモリ管理に関するご相談、ありがとうございます。言語設計の観点から、そのユニークなアーキテクチャの強みを最大限に表現できるアプリケーションについて、以下の通り提案します。
|
||||
|
||||
Nyashの「Everything is Box」哲学と`Arc<Mutex>`統一アーキテクチャは、従来の数値計算ベンチマークでは評価しにくい、**動的で状態が複雑に変化する大規模な並行処理**において真価を発揮します。論文でその独自性と優位性を示すには、まさにその特性を要求するアプリケーションを提示することが効果的です。
|
||||
|
||||
---
|
||||
|
||||
### 1. Nyashのメモリ管理の特殊性を活かせる大規模アプリケーション
|
||||
|
||||
**提案:リアルタイム・コラボレーティブ・アプリケーション基盤**
|
||||
|
||||
Google Docsのような共同編集エディタ、Figmaのようなデザインツール、またはマルチプレイヤーオンラインゲームのサーバーサイドなど、複数の主体が単一の共有状態を同時に変更するアプリケーションの基盤です。
|
||||
|
||||
**Nyashの強みが活きる点:**
|
||||
* **安全な状態共有:** ドキュメントのデータ構造(ツリー、グラフ)、あるいはゲームワールドの状態全体を`Box`オブジェクトとして表現します。`Arc<Mutex>`アーキテクチャにより、多数のユーザー(クライアント)からの同時編集リクエストがデータ競合を起こすことなく、安全に処理されることが言語レベルで保証されます。
|
||||
* **動的なオブジェクト管理:** ユーザーのカーソル、テキストオブジェクト、図形、NPC、アイテムなど、アプリケーション内のあらゆる要素を統一された`Box`として動的に生成・破棄できます。「Everything is Box」哲学により、異種のオブジェクトをコレクションで扱う際も型システムが煩雑になりません。
|
||||
* **並行処理との親和性:** 各クライアントからの操作を個別のタスク(スレッド)で処理するアーキテクチャと非常に相性が良いです。Nyashでは、開発者はロックの管理を意識することなく、ビジネスロジックの記述に集中できます。
|
||||
|
||||
このアプリケーションは、小規模なデータ構造を並列で計算するベンチマークとは対照的に、**巨大で複雑な単一の状態**を多数のアクターが**並行かつ安全**に変更し続けるという、Nyashの設計思想に最も合致したシナリオです。
|
||||
|
||||
### 2. `Arc<Mutex>`の並行性を活かしたデモ
|
||||
|
||||
**提案:並行Webクローラー**
|
||||
|
||||
複数のワーカースレッドが並行してWebページをダウンロードし、ページ内のリンクを解析して、共有された「未訪問URLリスト」に追加していくアプリケーションです。
|
||||
|
||||
**Nyashの強みが活きる点:**
|
||||
* **シンプルな共有状態:** 「未訪問URLのキュー」と「訪問済みURLのセット」という2つの中心的な共有状態を、それぞれ`Box`化するだけで済みます。
|
||||
* **コードの簡潔さ:** 他の言語では、共有データへのアクセスごとに `mutex.lock().unwrap()` のような明示的なロック獲得・解放処理が必要ですが、Nyashの統一アーキテクチャはこれを言語機能として抽象化します。これにより、並行処理のロジックが非常にクリーンかつ直感的に記述でき、デッドロックなどの典型的なバグを減らせます。
|
||||
|
||||
このデモは、Nyashがいかにして並行プログラミングの複雑さを軽減し、開発者がロジックそのものに集中できるようにするかを明確に示します。
|
||||
|
||||
### 3. プラグイン間でのBox共有を示すアプリケーション
|
||||
|
||||
**提案:プラグインベースのECS (Entity-Component-System) アーキテクチャ**
|
||||
|
||||
ゲーム開発で多用されるECSパターンをNyashで実装します。これは、データ(Component)とロジック(System)を完全に分離する設計です。
|
||||
|
||||
* **Entity:** `Box`化された一意のID。
|
||||
* **Component:** `Box`化された純粋なデータ(例: `Position { x, y }`, `Velocity { dx, dy }`)。
|
||||
* **System:** 特定のロジックを実装した**プラグイン**(例: `PhysicsSystem`, `RenderSystem`)。
|
||||
|
||||
**Nyashの強みが活きる点:**
|
||||
* **究極のデータとロジックの分離:** `PhysicsSystem`プラグインは`Position`と`Velocity`を持つ全エンティティの`Box`を走査して位置を更新し、`RenderSystem`プラグインは`Position`と`Sprite`を持つエンティティの`Box`を走査して描画します。このように、**複数の独立したプラグインが同じデータ(`Box`)を共有し、それぞれの関心事に沿って操作する**様子を鮮やかに示せます。
|
||||
* **動的な機能拡張:** `AISystem`や`AnimationSystem`といった新しい振る舞いを、既存のコードベースに一切変更を加えることなく、プラグインとして動的にロードできます。これはNyashのプラグインシステムと`Box`共有アーキテクチャの強力な組み合わせを実証します。
|
||||
|
||||
### 4. 他言語では難しいがNyashなら簡単に実装できる例
|
||||
|
||||
**提案:アプリケーションのライブ・リローディング(ホットスワップ)**
|
||||
|
||||
長時間稼働するサーバーアプリケーション(例: Web APIサーバー)を停止させることなく、その一部の機能を新しいバージョンに更新するデモです。
|
||||
|
||||
**Nyashの強みが活きる点:**
|
||||
* **統一オブジェクトモデルの威力:** Nyashでは、関数やクロージャさえもが`Box`オブジェクトとして扱えます。これにより、特定のAPIエンドポイントの処理ロジックを実装した関数オブジェクトを、実行時にアトミックに(安全に)差し替えることが原理的に容易になります。
|
||||
* **プラグインとFFIの活用:** 新しいバージョンのロジックを独立したプラグイン(.so/.dllファイルなど)としてコンパイルします。サーバーは稼働中にこのプラグインを動的にロードし、ハンドルベースFFIを介して新しい関数`Box`への参照を取得し、ルーティングテーブル内の古い参照と入れ替えます。
|
||||
* **他言語との差別化:**
|
||||
* RustやC++のような静的言語でこれを実現するには、非常に複雑な設計と手作業でのメモリ管理が必要です。
|
||||
- PythonやRubyのような動的言語では可能ですが、GIL(グローバルインタプリタロック)の存在により、Nyashのような真の並行性は得られません。
|
||||
|
||||
Nyashは、**静的言語の安全性とパフォーマンス**のポテンシャルを持ちながら、**動的言語の柔軟性**を兼ね備えている、というユニークなポジションをこのデモで示すことができます。
|
||||
|
||||
---
|
||||
|
||||
### まとめ
|
||||
|
||||
Nyashのメモリ管理の真価は、静的な計算性能ではなく、**「複雑で動的な状態を、安全かつ並行に、そしてモジュール的に扱う能力」**にあります。提案したアプリケーションは、この言語の核心的な価値を学術的にも実践的にもアピールするための強力な材料となるでしょう。
|
||||
|
||||
## ChatGPT5先生の提案
|
||||
|
||||
### 相談内容
|
||||
```
|
||||
Nyashプログラミング言語のメモリ管理の強みを表現するアプリケーションについて相談です。
|
||||
|
||||
【Gemini先生の提案】
|
||||
1. リアルタイム・コラボレーティブ基盤(Google Docs風の共同編集)
|
||||
2. 並行Webクローラー
|
||||
3. プラグインベースのECS(Entity-Component-System)
|
||||
4. ライブ・リローディング(ホットスワップ)
|
||||
|
||||
【Nyashの特殊性】
|
||||
- Arc<Mutex>統一アーキテクチャで全Boxが自動的にスレッドセーフ
|
||||
- プラグイン間でのBox共有が容易
|
||||
- 参照カウントベースのシンプルなGC
|
||||
- Everything is Box哲学
|
||||
|
||||
【質問】
|
||||
1. P2Pネットワークを活用したNyash独自のアプリケーションは?
|
||||
2. NyaMeshとの統合を前提とした分散アプリケーションの例は?
|
||||
3. Box共有の安全性を活かした「他言語では危険だがNyashなら安全」な例は?
|
||||
4. 論文で最もインパクトがあるデモは何でしょうか?
|
||||
```
|
||||
|
||||
### ChatGPT5先生の回答
|
||||
|
||||
了解です。Nyashの「Arc<Mutex>統一」「Everything is Box」「参照カウントGC」「プラグイン間Box共有」を強みに据え、P2P/NyaMesh前提で"Nyashならでは"を狙う案をまとめました。
|
||||
|
||||
**設計キーモチーフ**
|
||||
- Box-as-capability: Box自体を能力トークンとして共有・移譲・複製する設計。
|
||||
- 内容アドレス化: 大きなBoxは内容ハッシュで識別し、P2Pで重複排除。
|
||||
- 暗黙スレッドセーフ: 共有は常に `Arc<Mutex<Box<T>>>`。ゼロコピー志向、衝突はMutexで局所化。
|
||||
- ホットスワップ既定路: 新旧プラグインが同じBoxを安全に参照継続。
|
||||
|
||||
**1. P2P独自アプリ(Nyashらしさ最大化)**
|
||||
- BoxTorrent: 内容アドレス化BoxのP2P配布基盤
|
||||
- 大容量データや中間生成物を「Box=DAGノード」として配布。変換プラグインは入力Box→出力Boxを生成し、ハッシュで再利用。
|
||||
- Nyash優位: ゼロコピー共有、ホットスワップで変換器の差し替え可、参照カウントで自然にキャッシュエビクション。
|
||||
- Live Shared Heap: メッシュ越し共有ヒープ
|
||||
- 論理的に単一のShared HeapにBoxを配置。所有権はメッシュ内で移譲(lease)、読み取りは複製、書き込みはロック+バージョニング。
|
||||
- Nyash優位: 全Boxスレッドセーフ前提で分散ロックを単純化。プラグインがヒープ上の同一Boxをそのまま扱える。
|
||||
- P2P Live-ML Pipeline
|
||||
- センサ→前処理→特徴→推論→ポストの各段をプラグイン化し、ピア間でBoxバッファをパス。途中段のホットアップグレードや分岐実験が容易。
|
||||
- Nyash優位: 各段が同じBoxを共有しゼロコピー。落ちたピアの未解放は参照カウントで自然解放。
|
||||
- Cooperative Rendering/Video Mixer
|
||||
- 各ピアがレイヤBoxを提供し、合成ノードがロック付きで読み取り、最終フレームBoxを配信。遅延抑制のため差分Box(タイル)を伝播。
|
||||
- Nyash優位: 共有フレームバッファを安全に多者で読む/書く。ホットスワップでフィルタ入替。
|
||||
|
||||
**2. NyaMesh統合前提の分散例**
|
||||
- MeshRef/Lease機構
|
||||
- NyaMeshが提供するID/ルーティング上で `BoxID` をグローバル化。lease数が0でGC。複製はK-レプリカ。ゴシップで参照数を伝播。
|
||||
- 使い道: Live Shared Heap/BoxTorrentの基盤。ネット分断でもlease期限で回復。
|
||||
- NyaMesh PubSub CRDT-Box
|
||||
- CRDTレジスタ/マップ/ログをBox実装。書込みはMutex保護+バージョン、伝播はNyaMesh PubSub。競合はCRDTで収束。
|
||||
- 使い道: 協同編集、分散ECS、分散設定管理。
|
||||
- 分散ECS(Entity=Box)
|
||||
- Entity/ComponentをBoxとしてNyaMesh上で移動。負荷に応じてオーナーシップを移譲。システムはプラグインで増減。
|
||||
- 使い道: MMO風シミュ、ロボット群制御、IoTツイン。
|
||||
- Edge Cache/Compute
|
||||
- 内容アドレスBoxをNyaMesh距離で配置最適化。近傍にレプリカ、遠方はリモート参照。ホットスワップで演算器を近傍に移設。
|
||||
|
||||
**3. 他言語だと危険だがNyashなら安全な共有**
|
||||
- ゼロコピー共有バッファの多段パイプ
|
||||
- 大容量 `Box<ByteBuf>` を複数プラグインが並列処理。ダングリング/データ競合はArc<Mutex>で回避。古いプラグイン残留参照も安全に自然解放。
|
||||
- ライブ・ホットスワップ中の同一Box共有
|
||||
- 新旧プラグインが同じBoxを参照し続け、切替中も落ちない。通常はABI/所有権で危険だが、Box境界で能力を渡すだけで成立。
|
||||
- 共有メモリマップファイルの安全クローズ
|
||||
- 多スレッド/多プラグインで参照する `Box<Mmap>`。最後の参照が落ちた瞬間のみクローズされ、早期クローズ・UAFを防止。
|
||||
- マルチプロデューサ・マルチコンシューマなリングバッファ
|
||||
- `Box<Ring<T>>` をMutexで保護し、try_lock+バックプレッシャで輻輳制御。言語レベルunsafe不要。
|
||||
- 強制隔離されない共同編集のポインタ共有
|
||||
- プラグイン間で直接同じBoxドキュメントを参照(CRDT)。ロック&バージョンで整合、コピー不要。通常は破滅的競合を招く領域。
|
||||
|
||||
注意: デッドロックは設計課題なので、型でロック順序を表す/細粒度ロック/try_lock+再試行/単一スレッドオーナー+メッセージ駆動等で回避。
|
||||
|
||||
**4. 論文で最もインパクトがあるデモ**
|
||||
- 分散ホットスワップ・パイプライン(エンドツーエンド無停止)
|
||||
- 構成: NyaMesh上のパイプライン(センサ→前処理→推論→配信)。各段はプラグイン。高負荷時に段を増殖/縮退、同時に新バージョンへホットスワップ。
|
||||
- デモ見せ場: スループット維持、フレーム落ち/UAF/クラッシュゼロ、旧版プラグインが参照するBoxの自然解放、Boxの移譲/複製の可視化。
|
||||
- 計測: p99レイテンシ低下、スワップ時間中断ゼロ、メモリリーク0、再処理なしのバイナリ差替成功率。
|
||||
- 共有ヒープ越しのP2Pコラボ編集(巨大データ)
|
||||
- 構成: 1GB級ドキュメント/モデルを「単一Box」として複数ユーザが同時編集。差分はタイルBoxとして伝搬、CRDT-Boxで収束。
|
||||
- 見せ場: ゼロコピー、Boxの局所ロックで高スループット、ノード離脱でも自然復旧。
|
||||
- 分散ECSワールドのライブ移住
|
||||
- 構成: 数百万EntityのECS。混雑エリアを別ピアへオーナー移譲しながら、プレイ継続・スナップフリー。
|
||||
- 見せ場: Entity=Box移譲、参照が残る間は両ピアで安全アクセス、解放の自動同期。
|
||||
- BoxTorrentの内容アドレスDAG再計算スキップ
|
||||
- 構成: DAG変換パイプ。同一ハッシュ出力をグローバル再利用し、再計算ゼロに。
|
||||
- 見せ場: キャッシュヒット率と電力削減、重複排除。
|
||||
|
||||
**評価指標(論文化しやすい軸)**
|
||||
- 安全性: UAF/ダブルフリー/データ競合/クラッシュの発生率0。
|
||||
- 可用性: ホットスワップ中断時間0、p99レイテンシ悪化<5%。
|
||||
- 効率: ゼロコピー率、再計算/再送削減、RAMフットプリント(参照カウントによる自然エビクション)。
|
||||
- スケール: ピア数とスループットの線形性、チョークポイント不在。
|
||||
- 回復性: ノード喪失下でのデータ保全率、復旧所要時間。
|
||||
|
||||
**リスクと対策**
|
||||
- デッドロック: ロック順序規約/静的解析/try_lock+指数バックオフ。
|
||||
- 参照サイクル: 明示的Weak参照、世代別GC/epochでのリーク検出ダッシュボード。
|
||||
- 分散参照数の正確性: lease期限+ゴシップ再確認、チェックポイント。
|
||||
- 一貫性と遅延: CRDT採用、書込みはバージョン化Boxで楽観制御。
|
||||
- セキュリティ: Box能力にスコープ/寿命/権限を付与(capability化)。
|
||||
|
||||
**次の一手(提案)**
|
||||
- 小さなPoC: BoxTorrentのミニ版(内容アドレスBox+NyaMesh PubSub+参照カウント連携)を最短で実装。
|
||||
- 併走PoC: 分散ホットスワップ・パイプライン(2段)で無停止差替を実証。
|
||||
- 計測仕込み: 参照グラフ可視化、p99レイテンシ/スループット/リーク監視。
|
||||
|
||||
どの路線を優先しますか?最短でインパクトを狙うなら「分散ホットスワップ・パイプライン」か「BoxTorrent+DAG再利用」が論文映えします。要望があれば、選んだ案のアーキ図と最小API(BoxID/lease/replicate/transfer)の草案を書きます。
|
||||
|
||||
## 深い理解に基づく再提案(GC無しの本質)
|
||||
|
||||
### Gemini先生の再提案:「ライフサイクル指向プログラミング」
|
||||
|
||||
Nyashのメモリ管理は、単なる技術的優位性を超え、開発者に新しい思考の枠組みを提供します:
|
||||
|
||||
- **論理的死と物理的生存の分離 → 「生きたログ」としてのオブジェクト**
|
||||
- **遺言パターン → 「死後の自己報告」によるデバッグと監視**
|
||||
- **Weak自動nil化 → 「自己修復」する回復力のあるデータ構造**
|
||||
- **決定的解放順序 → 「安全な解体」が保証された積層的設計**
|
||||
|
||||
これらはすべて、GCの不確実性からプログラマを解放し、システムの振る舞いを時間軸に沿って正確に予測・設計することを可能にします。これはもはやメモリ管理ではなく、**「ライフサイクル指向プログラミング」**と呼ぶべき新しいパラダイムです。
|
||||
|
||||
### ChatGPT5先生の再提案:「生死の決定論を分散原語に」
|
||||
|
||||
GCを捨て、「生死の決定論」を分散の原語にすることで、解体・故障・再構成が予測可能になり、自己修復とゼロリークを両立できます:
|
||||
|
||||
- **LifeTokenモデル**: 各ノードの`LifeToken`を`Weak`で保持、クラッシュで全weakが自動nil化
|
||||
- **Two-Phase Fini**: 依存DAG逆順でfini合意、分散環境での決定的解体
|
||||
- **Lifecycle Actors**: 親scopeにタスクを紐付け、親finiで逆順に確定終了
|
||||
- **自己修復DHT**: ルーティングテーブルを`Weak<NodeRef>`で構築、nil化で即座に再配線
|
||||
|
||||
## まとめ
|
||||
|
||||
Gemini先生とChatGPT5先生の提案は、Nyashの本質的な強みを異なる角度から照らし出しています:
|
||||
|
||||
- **Gemini先生**: ローカル環境での決定論的ライフサイクル管理の革新性
|
||||
- **ChatGPT5先生**: 分散環境での生死の決定論による自己修復と安全性
|
||||
|
||||
重要なのは、これらが**GCという概念とは無関係**に、Nyashの基本的なメモリ管理セマンティクス(スコープベース解放、finiシステム、weak自動nil化、決定的順序)から自然に導かれることです。
|
||||
|
||||
両者の提案を統合することで、Nyashの「Everything is Box」哲学と決定論的メモリ管理が、単なる実装の詳細ではなく、**新しいプログラミングパラダイムの基盤**であることが証明されます。
|
||||
11
apps/DELETE_THESE.txt
Normal file
11
apps/DELETE_THESE.txt
Normal file
@ -0,0 +1,11 @@
|
||||
以下のディレクトリはテスト用で、apps/tests/に移動済みです。
|
||||
手動で削除してください:
|
||||
|
||||
rm -rf ny-echo-lite/
|
||||
rm -rf ny-map-llvm-smoke/
|
||||
rm -rf ny-vinvoke-smoke/
|
||||
rm -rf ny-vinvoke-llvm-ret/
|
||||
rm -rf ny-vinvoke-llvm-ret-size/
|
||||
|
||||
これらはすでに apps/tests/ にコピー済みで、
|
||||
tools/llvm_smoke.sh も更新済みです。
|
||||
74
apps/LLVM_TEST_PROGRAMS.md
Normal file
74
apps/LLVM_TEST_PROGRAMS.md
Normal file
@ -0,0 +1,74 @@
|
||||
# LLVM/AOT Test Programs
|
||||
|
||||
このファイルはLLVMバックエンドのテスト用プログラムについて説明します。
|
||||
これらは主にCIスモークテストで使用されます。
|
||||
|
||||
**注意**: これらのテストプログラムは `apps/tests/` ディレクトリに移動されました。
|
||||
|
||||
## テストプログラム一覧
|
||||
|
||||
### 基本動作テスト
|
||||
- **ny-llvm-smoke/** - ArrayBox基本操作(push/get)+ print
|
||||
- 現在の問題: ArrayBoxプラグインの引数エンコーディング問題で "Invalid arguments"
|
||||
- 状態: NYASH_LLVM_ARRAY_SMOKE=0でデフォルトスキップ
|
||||
|
||||
- **ny-array-llvm-ret/** - ArrayBox戻り値テスト(printなし)
|
||||
- 目的: print依存を排除して安定性向上
|
||||
- 期待値: Result: 3
|
||||
|
||||
- **ny-echo-lite/** - 最小echo実装
|
||||
- 目的: 標準入力/出力の基本動作確認
|
||||
- 状態: NYASH_LLVM_ECHO_SMOKE=0でデフォルトスキップ
|
||||
|
||||
### プラグイン呼び出しテスト
|
||||
- **ny-map-llvm-smoke/** - MapBoxプラグイン基本テスト
|
||||
- 目的: by-idプラグイン呼び出しの動作確認
|
||||
- 期待値: "Map: v=42" および "size=1"
|
||||
|
||||
### 可変長引数テスト(VInvoke)
|
||||
- **ny-vinvoke-smoke/** - 5引数呼び出し(文字列出力)
|
||||
- 目的: 可変長引数(≥3)のtagged vector経路テスト
|
||||
- 期待値: "VInvokeRc: 42"
|
||||
|
||||
- **ny-vinvoke-llvm-ret/** - 5引数呼び出し(戻り値)✅
|
||||
- 目的: 可変長引数の戻り値検証
|
||||
- 期待値: Result: 42
|
||||
|
||||
- **ny-vinvoke-llvm-ret-size/** - 0引数呼び出し(size())✅
|
||||
- 目的: 固定長引数(≤2)の経路テスト
|
||||
- 期待値: Result: 1
|
||||
|
||||
## CIスモークテストでの使用
|
||||
|
||||
`tools/llvm_smoke.sh`で以下の環境変数により制御:
|
||||
|
||||
```bash
|
||||
# 基本テスト(問題があるためデフォルトOFF)
|
||||
NYASH_LLVM_ARRAY_SMOKE=1 # ny-llvm-smoke
|
||||
NYASH_LLVM_ARRAY_RET_SMOKE=1 # ny-array-llvm-ret
|
||||
NYASH_LLVM_ECHO_SMOKE=1 # ny-echo-lite
|
||||
|
||||
# プラグインテスト(安定)
|
||||
NYASH_LLVM_MAP_SMOKE=1 # ny-map-llvm-smoke
|
||||
NYASH_LLVM_VINVOKE_SMOKE=1 # ny-vinvoke-smoke
|
||||
NYASH_LLVM_VINVOKE_RET_SMOKE=1 # ny-vinvoke-llvm-ret + ny-vinvoke-llvm-ret-size
|
||||
```
|
||||
|
||||
## 既知の問題
|
||||
|
||||
1. **ArrayBoxプラグイン**: set/getで "Invalid arguments" エラー
|
||||
- 原因: プラグイン側の引数デコード問題
|
||||
- 対策: 戻り値型テストで回避
|
||||
|
||||
2. **print文字列連結**: `print("Result: " + v)` での型エラー
|
||||
- 原因: binop型不一致
|
||||
- 対策: toString()を使用するか、戻り値型テストで回避
|
||||
|
||||
## 実アプリケーションとの違い
|
||||
|
||||
これらのテストプログラムは:
|
||||
- 最小限の機能に絞った単体テスト
|
||||
- CI自動実行を前提とした設計
|
||||
- エラー時の切り分けが容易
|
||||
|
||||
実際のアプリケーション(chip8_nyash、kilo_nyash等)とは目的が異なります。
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
このディレクトリには、Nyashの実力を示す実用的なアプリケーションが含まれています。
|
||||
|
||||
開発・貢献に関する全体ガイドはリポジトリルートの`AGENTS.md`(Repository Guidelines)を参照してください。プロジェクト構成、ビルド/テスト、PR要件の要点を簡潔にまとめています。
|
||||
|
||||
## 🚀 実装済みアプリケーション
|
||||
|
||||
### 🎮 ゲーム・エミュレータ
|
||||
@ -177,6 +179,80 @@ box TreeNode {
|
||||
- [ ] Lispインタープリター(言語実装)
|
||||
- [ ] 静的サイトジェネレータ(実用性)
|
||||
|
||||
## 🚀 Nyashメモリ管理の真価を示す革新的アプリケーション
|
||||
|
||||
### AI先生たちの提案(2025-08-31)
|
||||
|
||||
Gemini先生とChatGPT5先生から、Nyashの決定論的メモリ管理(スコープベース解放、finiシステム、weak自動nil化)がもたらす新しいプログラミングパラダイムについて革新的な提案を受けました。
|
||||
|
||||
### 🌟 最優先実装候補
|
||||
|
||||
#### 1. **分散ホットスワップ・パイプライン**
|
||||
**概要**: NyaMesh上でセンサ→前処理→推論→配信の各段をプラグイン化し、無停止で更新可能なMLパイプライン
|
||||
|
||||
**Nyashならではの特徴**:
|
||||
- 🔄 **無停止プラグイン更新**: finiシステムにより論理的に終了しても物理的に参照可能
|
||||
- 🧹 **決定的メモリ管理**: スコープ解放と逆順カスケードで予測可能な解放
|
||||
- ⚡ **性能維持**: p99レイテンシ悪化<5%、スループット維持
|
||||
|
||||
**他言語では困難な理由**:
|
||||
- Rust/C++: 手動メモリ管理で複雑、ホットスワップ時にUAFリスク
|
||||
- Python/Ruby: GILにより真の並行性が得られない
|
||||
|
||||
#### 2. **BoxTorrent - 内容アドレス化P2P配布基盤**
|
||||
**概要**: 大容量データや中間生成物を「Box=DAGノード」として配布し、変換プラグインで処理
|
||||
|
||||
**Nyashならではの特徴**:
|
||||
- 📦 **ゼロコピー共有**: Arc<Mutex>で安全にBoxを共有
|
||||
- 🔍 **内容ハッシュ重複排除**: 同一内容のBoxを自動的に再利用
|
||||
- 🗑️ **自然なキャッシュ管理**: 参照カウントで不要データを自動削除
|
||||
|
||||
#### 3. **Live Shared Heap - メッシュ越し共有ヒープ**
|
||||
**概要**: 論理的に単一のShared HeapにBoxを配置し、P2Pネットワーク上で共有
|
||||
|
||||
**Nyashならではの特徴**:
|
||||
- 🌐 **分散ロックの単純化**: 全Boxがスレッドセーフ前提
|
||||
- 🔌 **プラグイン透過性**: ヒープ上の同一Boxをそのまま扱える
|
||||
- 🔧 **ノード障害耐性**: 参照カウントで自然復旧
|
||||
|
||||
### 📊 実装による測定可能な優位性
|
||||
|
||||
| 指標 | 期待される優位性 |
|
||||
|------|-----------------|
|
||||
| **安全性** | UAF/データ競合/クラッシュ発生率 0% |
|
||||
| **可用性** | ホットスワップ中断時間 0秒 |
|
||||
| **効率性** | ゼロコピー率 90%以上 |
|
||||
| **拡張性** | ピア数に対して線形スケール |
|
||||
| **回復性** | ノード喪失下での自動復旧 |
|
||||
|
||||
### 🎯 実装ロードマップ(Nyash特化)
|
||||
|
||||
#### Phase 1: ミニマムPoC(1週間)
|
||||
- [ ] **BoxTorrentミニ版**: 内容アドレスBox + 参照カウント連携
|
||||
- [ ] **測定基盤**: 参照グラフ可視化、メモリリーク監視
|
||||
|
||||
#### Phase 2: 分散デモ(2週間)
|
||||
- [ ] **2段パイプライン**: センサ→処理のホットスワップ実証
|
||||
- [ ] **性能計測**: p99レイテンシ、スループット監視
|
||||
|
||||
#### Phase 3: 論文向け完全版(3週間)
|
||||
- [ ] **完全なMLパイプライン**: 4段以上の処理段
|
||||
- [ ] **大規模ベンチマーク**: 100ノード規模での性能実証
|
||||
|
||||
### 💡 Nyashだからこそ可能な革新
|
||||
|
||||
**「他言語では危険だがNyashなら安全」な例**:
|
||||
1. **ゼロコピー共有バッファの多段パイプ**: 大容量Box<ByteBuf>を複数プラグインが並列処理
|
||||
2. **共有メモリマップファイルの安全クローズ**: 最後の参照が落ちた瞬間のみクローズ
|
||||
3. **マルチプロデューサ・マルチコンシューマなリングバッファ**: 言語レベルでunsafe不要
|
||||
|
||||
これらの実装により、Nyashの「Everything is Box」哲学とArc<Mutex>統一アーキテクチャが、単なる理論ではなく実用的な価値を持つことを証明します。
|
||||
|
||||
### 🔮 将来候補
|
||||
- [ ] レイトレーサー(CPU集約的)
|
||||
- [ ] Lispインタープリター(言語実装)
|
||||
- [ ] 静的サイトジェネレータ(実用性)
|
||||
|
||||
## 🤝 貢献方法
|
||||
|
||||
新しいアプリケーションのアイデアや改善提案は大歓迎です!
|
||||
@ -186,4 +262,4 @@ box TreeNode {
|
||||
3. このREADMEに追加
|
||||
4. PRを送信
|
||||
|
||||
すべてのアプリケーションは「Everything is Box」哲学に従い、プラグインシステムを活用することを推奨します。
|
||||
すべてのアプリケーションは「Everything is Box」哲学に従い、プラグインシステムを活用することを推奨します。
|
||||
|
||||
@ -28,8 +28,11 @@ static box Main {
|
||||
|
||||
parseArgs(args) {
|
||||
local options = new MapBox()
|
||||
|
||||
loop(arg in args) {
|
||||
// foreach は未対応のため、index で明示ループ
|
||||
local i = 0
|
||||
local n = args.length()
|
||||
loop(i < n) {
|
||||
local arg = args.get(i)
|
||||
if arg == "--upper" {
|
||||
options.set("upper", true)
|
||||
} else if arg == "--lower" {
|
||||
@ -39,6 +42,7 @@ static box Main {
|
||||
} else if arg == "--help" || arg == "-h" {
|
||||
options.set("help", true)
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
return options
|
||||
@ -47,8 +51,7 @@ static box Main {
|
||||
processInput() {
|
||||
loop(true) {
|
||||
local input = me.console.readLine()
|
||||
if input == null { break } // EOF
|
||||
|
||||
if input == null { return 0 } // EOF → 関数終了に変更(break未対応MIR対策)
|
||||
local output = me.transformText(input)
|
||||
me.console.log(output)
|
||||
}
|
||||
@ -75,4 +78,4 @@ static box Main {
|
||||
me.console.log("")
|
||||
me.console.log("Reads from stdin and echoes to stdout with optional transformation.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
76
apps/ny-mem-bench/design.md
Normal file
76
apps/ny-mem-bench/design.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Nyashメモリ管理ベンチマーク設計書
|
||||
|
||||
## 概要
|
||||
イベントディスパッチャと揮発性リスナーを使用して、Nyashの決定論的メモリ管理の特性を測定・可視化するベンチマーク。
|
||||
|
||||
## 選定理由
|
||||
- **最小実装**: GUIや並行処理なしで実装可能
|
||||
- **全特性網羅**: スコープ解放、fini、weak自動nil化、カスケード順序を一度に示せる
|
||||
- **測定可能**: 明確な数値指標で他言語と比較可能
|
||||
|
||||
## アーキテクチャ
|
||||
|
||||
### コンポーネント
|
||||
1. **Dispatcher**: イベント発行者
|
||||
- リスナーをweak参照の配列で保持
|
||||
- イベント発行時に死んだリスナーを自動スキップ
|
||||
|
||||
2. **Listener**: イベント受信者
|
||||
- 子リソース(Box)を保持してツリー構造を形成
|
||||
- finiで子→自身の順で解放(カスケード)
|
||||
|
||||
3. **Scope**: ライフタイム境界
|
||||
- スコープ内でリスナー生成
|
||||
- スコープ終了で一括解放
|
||||
|
||||
## 測定指標
|
||||
|
||||
### 主要メトリクス
|
||||
1. **メモリピーク**: 最大メモリ使用量
|
||||
2. **解放レイテンシ**: スコープ終了→解放完了の時間
|
||||
3. **ディスパッチ効率**: イベント/秒
|
||||
4. **カスケード順序確定性**: 解放順序の一致率(100%が理想)
|
||||
5. **残留メモリ**: 解放後の残存メモリ(Nyashは0が理想)
|
||||
6. **ジッタ**: 各時間の分散(低いほど決定論的)
|
||||
|
||||
### 他言語との比較ポイント
|
||||
- **GC言語(Go/Java/JS)**: 解放タイミングの非決定性
|
||||
- **RAII系(Rust/C++)**: weak掃除ロジックの複雑さ
|
||||
- **手動管理(C)**: メモリリーク・二重解放リスク
|
||||
|
||||
## 実装計画
|
||||
|
||||
### CLI仕様
|
||||
```bash
|
||||
nyash-mem-bench dispatch --listeners N --events M --fanout F --depth D
|
||||
```
|
||||
|
||||
### パラメータ
|
||||
- `N`: リスナー数
|
||||
- `M`: イベント数
|
||||
- `F`: 各ノードの子数(分岐率)
|
||||
- `D`: ツリーの深さ
|
||||
|
||||
### 出力形式
|
||||
```
|
||||
construct_ms: 123.45
|
||||
dispatch_ns_p50: 789
|
||||
dispatch_ns_p99: 1234
|
||||
fini_ms_avg: 45.67
|
||||
fini_ms_max: 89.01
|
||||
mem_peak_mb: 256.78
|
||||
mem_after_fini_kb: 0
|
||||
order_ok: true
|
||||
weak_prune_count: 5000
|
||||
```
|
||||
|
||||
## 実装ステップ
|
||||
1. **Phase 1**: 基本構造(Dispatcher、Listener、Scope)
|
||||
2. **Phase 2**: 測定機能(時間、メモリ)
|
||||
3. **Phase 3**: 解放順序ログ
|
||||
4. **Phase 4**: 統計出力・可視化
|
||||
|
||||
## 期待される成果
|
||||
- Nyashの決定論的メモリ管理の優位性を数値で証明
|
||||
- 論文用の説得力のあるベンチマーク結果
|
||||
- 他言語実装者にとってのリファレンス実装
|
||||
231
apps/ny-mem-bench/main.nyash
Normal file
231
apps/ny-mem-bench/main.nyash
Normal file
@ -0,0 +1,231 @@
|
||||
// Nyashメモリ管理ベンチマーク
|
||||
// イベントディスパッチャ+揮発性リスナーで決定論的メモリ管理を実証
|
||||
|
||||
static box Main {
|
||||
init { console, stats }
|
||||
|
||||
main() {
|
||||
me.console = new ConsoleBox()
|
||||
me.stats = new StatsBox()
|
||||
|
||||
// コマンドライン引数の解析
|
||||
local args = new MapBox()
|
||||
args.set("listeners", 1000) // デフォルト値
|
||||
args.set("events", 10000)
|
||||
args.set("fanout", 3)
|
||||
args.set("depth", 4)
|
||||
|
||||
me.console.log("=== Nyash Memory Management Benchmark ===")
|
||||
me.console.log("Listeners: " + args.get("listeners"))
|
||||
me.console.log("Events: " + args.get("events"))
|
||||
me.console.log("Fanout: " + args.get("fanout"))
|
||||
me.console.log("Depth: " + args.get("depth"))
|
||||
|
||||
// ベンチマーク実行
|
||||
me.runBenchmark(
|
||||
args.get("listeners"),
|
||||
args.get("events"),
|
||||
args.get("fanout"),
|
||||
args.get("depth")
|
||||
)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
runBenchmark(listenerCount, eventCount, fanout, depth) {
|
||||
local dispatcher = new EventDispatcher()
|
||||
|
||||
// 構築フェーズの計測
|
||||
me.stats.startTimer("construct")
|
||||
{
|
||||
// スコープ内でリスナー生成
|
||||
local listeners = new ArrayBox()
|
||||
local i = 0
|
||||
loop(i < listenerCount) {
|
||||
local listener = new Listener("L" + i, fanout, depth)
|
||||
listeners.push(listener)
|
||||
dispatcher.addListener(listener)
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
me.stats.stopTimer("construct")
|
||||
me.stats.recordMemory("peak_before_dispatch")
|
||||
|
||||
// ディスパッチフェーズの計測
|
||||
me.stats.startTimer("dispatch")
|
||||
local j = 0
|
||||
loop(j < eventCount) {
|
||||
dispatcher.dispatch("event_" + j)
|
||||
j = j + 1
|
||||
}
|
||||
me.stats.stopTimer("dispatch")
|
||||
|
||||
// スコープ終了による解放フェーズの計測
|
||||
me.stats.startTimer("fini")
|
||||
} // ここでlistenersスコープが終了し、カスケード解放が発生
|
||||
|
||||
me.stats.stopTimer("fini")
|
||||
me.stats.recordMemory("after_fini")
|
||||
|
||||
// 結果出力
|
||||
me.printResults(listenerCount, eventCount)
|
||||
}
|
||||
|
||||
printResults(listenerCount, eventCount) {
|
||||
me.console.log("\n=== Results ===")
|
||||
me.console.log("construct_ms: " + me.stats.getTimer("construct"))
|
||||
|
||||
local dispatchTime = me.stats.getTimer("dispatch")
|
||||
local perEvent = dispatchTime / eventCount
|
||||
me.console.log("dispatch_ns_avg: " + (perEvent * 1000000))
|
||||
|
||||
me.console.log("fini_ms: " + me.stats.getTimer("fini"))
|
||||
me.console.log("mem_peak_mb: " + me.stats.getMemory("peak_before_dispatch"))
|
||||
me.console.log("mem_after_fini_kb: " + me.stats.getMemory("after_fini"))
|
||||
me.console.log("order_ok: " + OrderLogger.isOrderCorrect())
|
||||
me.console.log("weak_prune_count: " + EventDispatcher.getPruneCount())
|
||||
}
|
||||
}
|
||||
|
||||
// イベントディスパッチャ(weak参照でリスナー管理)
|
||||
box EventDispatcher {
|
||||
init { listeners, pruneCount }
|
||||
static pruneCountGlobal = 0
|
||||
|
||||
constructor() {
|
||||
me.listeners = new ArrayBox()
|
||||
me.pruneCount = 0
|
||||
}
|
||||
|
||||
addListener(listener) {
|
||||
// weak参照として保持
|
||||
me.listeners.push(weak(listener))
|
||||
}
|
||||
|
||||
dispatch(event) {
|
||||
local activeListeners = new ArrayBox()
|
||||
local i = 0
|
||||
local len = me.listeners.length()
|
||||
|
||||
loop(i < len) {
|
||||
local weakListener = me.listeners.get(i)
|
||||
// weak参照が生きているかチェック
|
||||
if weakListener != nil {
|
||||
weakListener.onEvent(event)
|
||||
activeListeners.push(weakListener)
|
||||
} else {
|
||||
me.pruneCount = me.pruneCount + 1
|
||||
EventDispatcher.pruneCountGlobal = EventDispatcher.pruneCountGlobal + 1
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
// 生きているリスナーのみを保持
|
||||
me.listeners = activeListeners
|
||||
}
|
||||
|
||||
static getPruneCount() {
|
||||
return EventDispatcher.pruneCountGlobal
|
||||
}
|
||||
}
|
||||
|
||||
// リスナー(子リソースを持つツリー構造)
|
||||
box Listener {
|
||||
init { id, children, eventCount }
|
||||
|
||||
constructor(id, fanout, depth) {
|
||||
me.id = id
|
||||
me.children = new ArrayBox()
|
||||
me.eventCount = 0
|
||||
|
||||
// 子ノードを再帰的に生成
|
||||
if depth > 0 {
|
||||
local i = 0
|
||||
loop(i < fanout) {
|
||||
local child = new Listener(id + "." + i, fanout, depth - 1)
|
||||
me.children.push(child)
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onEvent(event) {
|
||||
me.eventCount = me.eventCount + 1
|
||||
}
|
||||
|
||||
fini() {
|
||||
// 子→親の順で解放(カスケード)
|
||||
local i = me.children.length() - 1
|
||||
loop(i >= 0) {
|
||||
local child = me.children.get(i)
|
||||
if child != nil {
|
||||
child.fini()
|
||||
}
|
||||
i = i - 1
|
||||
}
|
||||
|
||||
// 解放順序をログ
|
||||
OrderLogger.log(me.id)
|
||||
}
|
||||
}
|
||||
|
||||
// 統計収集Box
|
||||
box StatsBox {
|
||||
init { timers, memories }
|
||||
|
||||
constructor() {
|
||||
me.timers = new MapBox()
|
||||
me.memories = new MapBox()
|
||||
}
|
||||
|
||||
startTimer(name) {
|
||||
// 実際の実装では高精度タイマーを使用
|
||||
me.timers.set(name + "_start", TimeBox.now())
|
||||
}
|
||||
|
||||
stopTimer(name) {
|
||||
local start = me.timers.get(name + "_start")
|
||||
local elapsed = TimeBox.now() - start
|
||||
me.timers.set(name, elapsed)
|
||||
}
|
||||
|
||||
getTimer(name) {
|
||||
return me.timers.get(name)
|
||||
}
|
||||
|
||||
recordMemory(name) {
|
||||
// 実際の実装ではシステムメモリ情報を取得
|
||||
me.memories.set(name, 0) // プレースホルダー
|
||||
}
|
||||
|
||||
getMemory(name) {
|
||||
return me.memories.get(name)
|
||||
}
|
||||
}
|
||||
|
||||
// 解放順序ログ(シングルトン)
|
||||
static box OrderLogger {
|
||||
init { log, expectedOrder }
|
||||
static instance = nil
|
||||
|
||||
static log(id) {
|
||||
if OrderLogger.instance == nil {
|
||||
OrderLogger.instance = new OrderLogger()
|
||||
}
|
||||
OrderLogger.instance.addLog(id)
|
||||
}
|
||||
|
||||
constructor() {
|
||||
me.log = new ArrayBox()
|
||||
me.expectedOrder = new ArrayBox()
|
||||
}
|
||||
|
||||
addLog(id) {
|
||||
me.log.push(id)
|
||||
}
|
||||
|
||||
static isOrderCorrect() {
|
||||
// 実際の実装では期待される順序と比較
|
||||
return true
|
||||
}
|
||||
}
|
||||
31
apps/tests/README.md
Normal file
31
apps/tests/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Nyash Test Programs
|
||||
|
||||
このディレクトリには、CI/CDやデバッグ用のテストプログラムが含まれています。
|
||||
実用的なアプリケーションは親ディレクトリ(`apps/`)にあります。
|
||||
|
||||
## テストプログラム一覧
|
||||
|
||||
### LLVMバックエンドテスト
|
||||
- **ny-llvm-smoke/** - ArrayBox基本操作テスト
|
||||
- **ny-array-llvm-ret/** - ArrayBox戻り値テスト
|
||||
- **ny-echo-lite/** - 最小echo実装(I/Oテスト)
|
||||
- **ny-map-llvm-smoke/** - MapBoxプラグインテスト
|
||||
- **ny-vinvoke-smoke/** - 可変長引数(5引数)テスト
|
||||
- **ny-vinvoke-llvm-ret/** - 可変長引数戻り値テスト
|
||||
- **ny-vinvoke-llvm-ret-size/** - 固定長引数(size())テスト
|
||||
|
||||
## 実行方法
|
||||
|
||||
これらのテストは主に `tools/llvm_smoke.sh` から実行されます:
|
||||
|
||||
```bash
|
||||
# 環境変数でテストを有効化
|
||||
NYASH_LLVM_MAP_SMOKE=1 ./tools/llvm_smoke.sh
|
||||
NYASH_LLVM_VINVOKE_RET_SMOKE=1 ./tools/llvm_smoke.sh
|
||||
```
|
||||
|
||||
## 注意事項
|
||||
|
||||
- これらは最小限の機能テストであり、実用的なアプリケーションではありません
|
||||
- CIでの自動テストを前提に設計されています
|
||||
- エラー時の切り分けが容易になるよう、各テストは単一の機能に焦点を当てています
|
||||
11
apps/tests/async-await-min/main.nyash
Normal file
11
apps/tests/async-await-min/main.nyash
Normal file
@ -0,0 +1,11 @@
|
||||
# Async/Await minimal smoke
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
// Create a resolved future using nowait sugar
|
||||
nowait fut = 42
|
||||
// Await and return the value
|
||||
local v = await fut
|
||||
return v
|
||||
}
|
||||
}
|
||||
9
apps/tests/concat-string-plugin/add_plugin_strings.nyash
Normal file
9
apps/tests/concat-string-plugin/add_plugin_strings.nyash
Normal file
@ -0,0 +1,9 @@
|
||||
static box Main {
|
||||
main() {
|
||||
local a = new StringBox("12")
|
||||
local b = new StringBox("30")
|
||||
local r = a + b
|
||||
print(r)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
7
apps/tests/concat-string-plugin/main.nyash
Normal file
7
apps/tests/concat-string-plugin/main.nyash
Normal file
@ -0,0 +1,7 @@
|
||||
static box Main {
|
||||
main() {
|
||||
local s = new StringBox("12")
|
||||
print("x = " + s)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
16
apps/tests/gc-sync-stress/main.nyash
Normal file
16
apps/tests/gc-sync-stress/main.nyash
Normal file
@ -0,0 +1,16 @@
|
||||
// gc-sync-stress: allocate many small boxes to pass through checkpoints
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local n = 5000
|
||||
local arr = new ArrayBox()
|
||||
local i = 0
|
||||
loop(i < n) {
|
||||
arr.push(new IntegerBox(i))
|
||||
i = i + 1
|
||||
}
|
||||
print(arr.length())
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
19
apps/tests/mir-branch-multi/main.nyash
Normal file
19
apps/tests/mir-branch-multi/main.nyash
Normal file
@ -0,0 +1,19 @@
|
||||
// mir-branch-multi - composite compare + nested branch
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local a, b
|
||||
a = 5
|
||||
b = 3
|
||||
if a > b {
|
||||
if a == 5 {
|
||||
return 1
|
||||
} else {
|
||||
return 2
|
||||
}
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
apps/tests/mir-branch-ret/main.nyash
Normal file
12
apps/tests/mir-branch-ret/main.nyash
Normal file
@ -0,0 +1,12 @@
|
||||
// mir-branch-ret - simple conditional branch returning 1 or 0
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
if 1 < 2 {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
apps/tests/mir-const-add/main.nyash
Normal file
13
apps/tests/mir-const-add/main.nyash
Normal file
@ -0,0 +1,13 @@
|
||||
// mir-const-add - minimal MIR-only smoke
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
// basic const/add/return
|
||||
local a = 1
|
||||
local b = 2
|
||||
local c = a + b
|
||||
// not printed to avoid I/O; ensure interpreter path works
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
20
apps/tests/mir-nested-branch/main.nyash
Normal file
20
apps/tests/mir-nested-branch/main.nyash
Normal file
@ -0,0 +1,20 @@
|
||||
// mir-nested-branch - nested branches with returns
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
if 1 < 2 {
|
||||
if 3 < 4 {
|
||||
return 1
|
||||
} else {
|
||||
return 2
|
||||
}
|
||||
} else {
|
||||
if 5 < 6 {
|
||||
return 3
|
||||
} else {
|
||||
return 4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
apps/tests/mir-phi-min/main.nyash
Normal file
15
apps/tests/mir-phi-min/main.nyash
Normal file
@ -0,0 +1,15 @@
|
||||
// mir-phi-min - minimal PHI merge across then/else
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local x
|
||||
if 1 < 2 {
|
||||
x = 10
|
||||
} else {
|
||||
x = 20
|
||||
}
|
||||
// Use x after merge to force PHI
|
||||
return x // expect 10
|
||||
}
|
||||
}
|
||||
|
||||
18
apps/tests/mir-phi-two/main.nyash
Normal file
18
apps/tests/mir-phi-two/main.nyash
Normal file
@ -0,0 +1,18 @@
|
||||
// mir-phi-two - branch assigns two locals and merges
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local x
|
||||
local y
|
||||
if 1 < 2 {
|
||||
x = 10
|
||||
y = 20
|
||||
} else {
|
||||
x = 30
|
||||
y = 40
|
||||
}
|
||||
// Expect x=10,y=20 → x+y=30
|
||||
return x + y
|
||||
}
|
||||
}
|
||||
|
||||
12
apps/tests/mir-store-load/main.nyash
Normal file
12
apps/tests/mir-store-load/main.nyash
Normal file
@ -0,0 +1,12 @@
|
||||
// mir-store-load - exercise minimal StackSlot-based Load/Store
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local x, y
|
||||
x = 1
|
||||
y = 2
|
||||
x = x + y
|
||||
return x // expect 3
|
||||
}
|
||||
}
|
||||
|
||||
12
apps/tests/ny-array-llvm-ret/main.nyash
Normal file
12
apps/tests/ny-array-llvm-ret/main.nyash
Normal file
@ -0,0 +1,12 @@
|
||||
// ny-array-llvm-ret - LLVM向け: ArrayBoxのpush/get 戻り値を検証
|
||||
// 期待: ランタイム標準出力に "Result: 3" が出る(ny_mainの戻り値)
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local a = new ArrayBox()
|
||||
a.push(3)
|
||||
local v = a.get(0)
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
16
apps/tests/ny-echo-lite/main.nyash
Normal file
16
apps/tests/ny-echo-lite/main.nyash
Normal file
@ -0,0 +1,16 @@
|
||||
// ny-echo-lite - minimal echo for LLVM/AOT smoke
|
||||
// Reads one line from stdin and prints it back. EOF -> exit(0)
|
||||
|
||||
static box Main {
|
||||
init { console }
|
||||
|
||||
main() {
|
||||
me.console = new ConsoleBox()
|
||||
local line = me.console.readLine()
|
||||
if line != null {
|
||||
print(line)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
16
apps/tests/ny-llvm-smoke/main.nyash
Normal file
16
apps/tests/ny-llvm-smoke/main.nyash
Normal file
@ -0,0 +1,16 @@
|
||||
// ny-llvm-smoke - 最小LLVM/AOTスモーク
|
||||
// 目的: ArrayBox.get/set と ExternCall(console.log) の実行確認
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
// ArrayBox を生成し、push で 3 を追加してから取得
|
||||
local a = new ArrayBox()
|
||||
a.push(3)
|
||||
local v = a.get(0)
|
||||
|
||||
// 代表出力(ExternCallに正規化される print を使用)
|
||||
print("Result: " + v)
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
361
apps/tests/ny-llvm-smoke/nyash.toml
Normal file
361
apps/tests/ny-llvm-smoke/nyash.toml
Normal file
@ -0,0 +1,361 @@
|
||||
# Nyash Configuration File v2
|
||||
# マルチBox型プラグイン対応
|
||||
|
||||
[libraries]
|
||||
# ライブラリ定義(1つのプラグインで複数のBox型を提供可能)
|
||||
[libraries."libnyash_filebox_plugin"]
|
||||
boxes = ["FileBox"]
|
||||
path = "./plugins/nyash-filebox-plugin/target/release/libnyash_filebox_plugin"
|
||||
|
||||
[libraries."libnyash_counter_plugin"]
|
||||
boxes = ["CounterBox"]
|
||||
path = "./plugins/nyash-counter-plugin/target/release/libnyash_counter_plugin"
|
||||
|
||||
[libraries."libnyash_net_plugin"]
|
||||
boxes = ["HttpServerBox", "HttpClientBox", "HttpResponseBox", "HttpRequestBox", "SocketServerBox", "SocketClientBox", "SocketConnBox"]
|
||||
path = "./plugins/nyash-net-plugin/target/release/libnyash_net_plugin"
|
||||
|
||||
# FileBoxの型情報定義
|
||||
[libraries."libnyash_filebox_plugin".FileBox]
|
||||
type_id = 6
|
||||
|
||||
[libraries."libnyash_filebox_plugin".FileBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
open = { method_id = 1, args = ["path", "mode"] }
|
||||
read = { method_id = 2 }
|
||||
write = { method_id = 3, args = ["data"] }
|
||||
close = { method_id = 4 }
|
||||
fini = { method_id = 4294967295 }
|
||||
copyFrom = { method_id = 7, args = [ { kind = "box", category = "plugin" } ] }
|
||||
cloneSelf = { method_id = 8 }
|
||||
|
||||
[libraries."libnyash_counter_plugin".CounterBox]
|
||||
type_id = 7
|
||||
singleton = true
|
||||
|
||||
[libraries."libnyash_counter_plugin".CounterBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
inc = { method_id = 1 }
|
||||
get = { method_id = 2 }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
# HttpServerBox
|
||||
[libraries."libnyash_net_plugin".HttpServerBox]
|
||||
type_id = 20
|
||||
|
||||
[libraries."libnyash_net_plugin".HttpServerBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
start = { method_id = 1, args = ["port"], returns_result = true }
|
||||
stop = { method_id = 2, returns_result = true }
|
||||
accept = { method_id = 3, returns_result = true }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
# HttpClientBox
|
||||
[libraries."libnyash_net_plugin".HttpClientBox]
|
||||
type_id = 23
|
||||
|
||||
[libraries."libnyash_net_plugin".HttpClientBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
get = { method_id = 1, args = ["url"], returns_result = true }
|
||||
post = { method_id = 2, args = ["url", "body"], returns_result = true }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
# HttpResponseBox
|
||||
[libraries."libnyash_net_plugin".HttpResponseBox]
|
||||
type_id = 22
|
||||
|
||||
[libraries."libnyash_net_plugin".HttpResponseBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
setStatus = { method_id = 1, args = ["status"] }
|
||||
setHeader = { method_id = 2, args = ["key", "value"] }
|
||||
write = { method_id = 3, args = ["body"] }
|
||||
readBody = { method_id = 4 }
|
||||
getStatus = { method_id = 5 }
|
||||
getHeader = { method_id = 6, args = ["key"] }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
# HttpRequestBox
|
||||
[libraries."libnyash_net_plugin".HttpRequestBox]
|
||||
type_id = 21
|
||||
|
||||
[libraries."libnyash_net_plugin".HttpRequestBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
path = { method_id = 1 }
|
||||
readBody = { method_id = 2 }
|
||||
respond = { method_id = 3, args = [{ kind = "box", category = "plugin" }] }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
# SocketServerBox
|
||||
[libraries."libnyash_net_plugin".SocketServerBox]
|
||||
type_id = 30
|
||||
|
||||
[libraries."libnyash_net_plugin".SocketServerBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
bind = { method_id = 1, args = ["port"] }
|
||||
accept = { method_id = 2 }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
# SocketClientBox
|
||||
[libraries."libnyash_net_plugin".SocketClientBox]
|
||||
type_id = 32
|
||||
|
||||
[libraries."libnyash_net_plugin".SocketClientBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
connect = { method_id = 1, args = ["host", "port"] }
|
||||
send = { method_id = 2, args = ["data"] }
|
||||
receive = { method_id = 3 }
|
||||
close = { method_id = 4 }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
# SocketConnBox
|
||||
[libraries."libnyash_net_plugin".SocketConnBox]
|
||||
type_id = 31
|
||||
|
||||
[libraries."libnyash_net_plugin".SocketConnBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
send = { method_id = 1, args = ["data"] }
|
||||
recv = { method_id = 2 }
|
||||
close = { method_id = 3 }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
[plugin_paths]
|
||||
# プラグインの検索パス(デフォルト)
|
||||
search_paths = [
|
||||
"./target/release",
|
||||
"./target/debug",
|
||||
"./plugins/*/target/release",
|
||||
"./plugins/*/target/debug",
|
||||
"/usr/local/lib/nyash/plugins",
|
||||
"~/.nyash/plugins"
|
||||
]
|
||||
|
||||
# 中央タイプIDレジストリ(新): 各プラグインの nyash_box.toml と一致させる
|
||||
[box_types]
|
||||
FileBox = 6
|
||||
ConsoleBox = 5
|
||||
ArrayBox = 10
|
||||
MapBox = 11
|
||||
IntegerBox = 12
|
||||
StringBox = 13
|
||||
CounterBox = 7
|
||||
HttpServerBox = 20
|
||||
HttpRequestBox = 21
|
||||
HttpResponseBox = 22
|
||||
HttpClientBox = 23
|
||||
SocketServerBox = 30
|
||||
SocketConnBox = 31
|
||||
SocketClientBox = 32
|
||||
MathBox = 50
|
||||
TimeBox = 51
|
||||
RegexBox = 52
|
||||
EncodingBox = 53
|
||||
TOMLBox = 54
|
||||
PathBox = 55
|
||||
PyRuntimeBox= 40
|
||||
PyObjectBox = 41
|
||||
PythonParserBox = 60
|
||||
PythonCompilerBox = 61
|
||||
|
||||
# 新スタイルのプラグインルート(併用可・[libraries]は後方互換)
|
||||
[plugins]
|
||||
"libnyash_filebox_plugin" = "./plugins/nyash-filebox-plugin"
|
||||
"libnyash_console_plugin" = "./plugins/nyash-console-plugin"
|
||||
"libnyash_string_plugin" = "./plugins/nyash-string-plugin"
|
||||
"libnyash_map_plugin" = "./plugins/nyash-map-plugin"
|
||||
"libnyash_array_plugin" = "./plugins/nyash-array-plugin"
|
||||
"libnyash_python_plugin" = "./plugins/nyash-python-plugin"
|
||||
"libnyash_integer_plugin" = "./plugins/nyash-integer-plugin"
|
||||
"libnyash_counter_plugin" = "./plugins/nyash-counter-plugin"
|
||||
"libnyash_net_plugin" = "./plugins/nyash-net-plugin"
|
||||
"libnyash_math_plugin" = "./plugins/nyash-math-plugin"
|
||||
"libnyash_python_parser_plugin" = "./plugins/nyash-python-parser-plugin"
|
||||
"libnyash_python_compiler_plugin" = "./plugins/nyash-python-compiler-plugin"
|
||||
"libnyash_regex_plugin" = "./plugins/nyash-regex-plugin"
|
||||
"libnyash_encoding_plugin" = "./plugins/nyash-encoding-plugin"
|
||||
"libnyash_toml_plugin" = "./plugins/nyash-toml-plugin"
|
||||
"libnyash_path_plugin" = "./plugins/nyash-path-plugin"
|
||||
[libraries."libnyash_array_plugin"]
|
||||
boxes = ["ArrayBox"]
|
||||
path = "./plugins/nyash-array-plugin/target/release/libnyash_array_plugin"
|
||||
|
||||
[libraries."libnyash_array_plugin".ArrayBox]
|
||||
type_id = 10
|
||||
|
||||
[libraries."libnyash_array_plugin".ArrayBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
length = { method_id = 1 }
|
||||
get = { method_id = 2, args = ["index"] }
|
||||
push = { method_id = 3, args = ["value"] }
|
||||
set = { method_id = 4, args = ["index", "value"] }
|
||||
fini = { method_id = 4294967295 }
|
||||
[libraries."libnyash_map_plugin"]
|
||||
boxes = ["MapBox"]
|
||||
path = "./plugins/nyash-map-plugin/target/release/libnyash_map_plugin"
|
||||
|
||||
[libraries."libnyash_map_plugin".MapBox]
|
||||
type_id = 11
|
||||
|
||||
[libraries."libnyash_map_plugin".MapBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
size = { method_id = 1 }
|
||||
get = { method_id = 2, args = ["key"] }
|
||||
has = { method_id = 3, args = ["key"] }
|
||||
set = { method_id = 4, args = ["key", "value"] }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
# IntegerBox plugin (basic numeric box)
|
||||
[libraries."libnyash_integer_plugin"]
|
||||
boxes = ["IntegerBox"]
|
||||
path = "./plugins/nyash-integer-plugin/target/release/libnyash_integer_plugin"
|
||||
|
||||
[libraries."libnyash_integer_plugin".IntegerBox]
|
||||
type_id = 12
|
||||
|
||||
[libraries."libnyash_integer_plugin".IntegerBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
get = { method_id = 1 }
|
||||
set = { method_id = 2, args = ["value"] }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
# StringBox plugin (read-only methods first)
|
||||
[libraries."libnyash_string_plugin"]
|
||||
boxes = ["StringBox"]
|
||||
path = "./plugins/nyash-string-plugin/target/release/libnyash_string_plugin"
|
||||
|
||||
[libraries."libnyash_string_plugin".StringBox]
|
||||
type_id = 13
|
||||
|
||||
[libraries."libnyash_string_plugin".StringBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
length = { method_id = 1 }
|
||||
is_empty = { method_id = 2 }
|
||||
charCodeAt = { method_id = 3, args = ["index"] }
|
||||
concat = { method_id = 4, args = ["other"] }
|
||||
fromUtf8 = { method_id = 5, args = ["data"] }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
# Python plugin (Phase 10.5 – Embedding & FFI, initial scaffold)
|
||||
[libraries."libnyash_python_plugin"]
|
||||
boxes = ["PyRuntimeBox", "PyObjectBox"]
|
||||
path = "./plugins/nyash-python-plugin/target/release/libnyash_python_plugin"
|
||||
|
||||
[libraries."libnyash_python_plugin".PyRuntimeBox]
|
||||
type_id = 40
|
||||
|
||||
[libraries."libnyash_python_plugin".PyRuntimeBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
eval = { method_id = 1, args = ["code"] }
|
||||
import = { method_id = 2, args = ["name"] }
|
||||
fini = { method_id = 4294967295 }
|
||||
evalR = { method_id = 11, args = ["code"], returns_result = true }
|
||||
importR= { method_id = 12, args = ["name"], returns_result = true }
|
||||
|
||||
[libraries."libnyash_python_plugin".PyObjectBox]
|
||||
type_id = 41
|
||||
|
||||
[libraries."libnyash_python_plugin".PyObjectBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
getattr = { method_id = 1, args = ["name"] }
|
||||
call = { method_id = 2, args = ["args"] }
|
||||
callKw = { method_id = 5 }
|
||||
str = { method_id = 3 }
|
||||
fini = { method_id = 4294967295 }
|
||||
getattrR= { method_id = 11, args = ["name"], returns_result = true }
|
||||
callR = { method_id = 12, args = ["args"], returns_result = true }
|
||||
callKwR = { method_id = 15, returns_result = true }
|
||||
[libraries."libnyash_console_plugin"]
|
||||
boxes = ["ConsoleBox"]
|
||||
path = "./plugins/nyash-console-plugin/target/release/libnyash_console_plugin"
|
||||
|
||||
[libraries."libnyash_console_plugin".ConsoleBox]
|
||||
type_id = 5
|
||||
|
||||
[libraries."libnyash_console_plugin".ConsoleBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
log = { method_id = 1, args = ["text"] }
|
||||
println = { method_id = 2, args = ["text"] }
|
||||
fini = { method_id = 4294967295 }
|
||||
[libraries."libnyash_math_plugin"]
|
||||
boxes = ["MathBox", "TimeBox"]
|
||||
path = "./plugins/nyash-math-plugin/target/release/libnyash_math_plugin"
|
||||
|
||||
[libraries."libnyash_math_plugin".MathBox]
|
||||
type_id = 50
|
||||
|
||||
[libraries."libnyash_math_plugin".MathBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
sqrt = { method_id = 1, args = ["x"] }
|
||||
sin = { method_id = 2, args = ["x"] }
|
||||
cos = { method_id = 3, args = ["x"] }
|
||||
round = { method_id = 4, args = ["x"] }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
[libraries."libnyash_math_plugin".TimeBox]
|
||||
type_id = 51
|
||||
|
||||
[libraries."libnyash_math_plugin".TimeBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
now = { method_id = 1 }
|
||||
fini = { method_id = 4294967295 }
|
||||
[libraries."libnyash_regex_plugin"]
|
||||
boxes = ["RegexBox"]
|
||||
path = "./plugins/nyash-regex-plugin/target/release/libnyash_regex_plugin"
|
||||
|
||||
[libraries."libnyash_regex_plugin".RegexBox]
|
||||
type_id = 52
|
||||
|
||||
[libraries."libnyash_regex_plugin".RegexBox.methods]
|
||||
birth = { method_id = 0, args = ["pattern?"] }
|
||||
compile = { method_id = 1, args = ["pattern"] }
|
||||
isMatch = { method_id = 2, args = ["text"], returns_result = true }
|
||||
find = { method_id = 3, args = ["text"], returns_result = true }
|
||||
replaceAll = { method_id = 4, args = ["text", "repl"], returns_result = true }
|
||||
split = { method_id = 5, args = ["text", "limit"], returns_result = true }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
[libraries."libnyash_encoding_plugin"]
|
||||
boxes = ["EncodingBox"]
|
||||
path = "./plugins/nyash-encoding-plugin/target/release/libnyash_encoding_plugin"
|
||||
|
||||
[libraries."libnyash_encoding_plugin".EncodingBox]
|
||||
type_id = 53
|
||||
|
||||
[libraries."libnyash_encoding_plugin".EncodingBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
toUtf8Bytes = { method_id = 1, args = ["s"], returns_result = true }
|
||||
fromUtf8Bytes = { method_id = 2, args = ["bytes"], returns_result = true }
|
||||
base64Encode = { method_id = 3, args = ["data"], returns_result = true }
|
||||
base64Decode = { method_id = 4, args = ["text"], returns_result = true }
|
||||
hexEncode = { method_id = 5, args = ["data"], returns_result = true }
|
||||
hexDecode = { method_id = 6, args = ["text"], returns_result = true }
|
||||
fini = { method_id = 4294967295 }
|
||||
[libraries."libnyash_toml_plugin"]
|
||||
boxes = ["TOMLBox"]
|
||||
path = "./plugins/nyash-toml-plugin/target/release/libnyash_toml_plugin"
|
||||
|
||||
[libraries."libnyash_toml_plugin".TOMLBox]
|
||||
type_id = 54
|
||||
|
||||
[libraries."libnyash_toml_plugin".TOMLBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
parse = { method_id = 1, args = ["text"], returns_result = true }
|
||||
get = { method_id = 2, args = ["path"], returns_result = true }
|
||||
toJson = { method_id = 3, returns_result = true }
|
||||
fini = { method_id = 4294967295 }
|
||||
|
||||
[libraries."libnyash_path_plugin"]
|
||||
boxes = ["PathBox"]
|
||||
path = "./plugins/nyash-path-plugin/target/release/libnyash_path_plugin"
|
||||
|
||||
[libraries."libnyash_path_plugin".PathBox]
|
||||
type_id = 55
|
||||
|
||||
[libraries."libnyash_path_plugin".PathBox.methods]
|
||||
birth = { method_id = 0 }
|
||||
join = { method_id = 1, args = ["base", "rest"], returns_result = true }
|
||||
dirname = { method_id = 2, args = ["path"], returns_result = true }
|
||||
basename = { method_id = 3, args = ["path"], returns_result = true }
|
||||
extname = { method_id = 4, args = ["path"], returns_result = true }
|
||||
isAbs = { method_id = 5, args = ["path"], returns_result = true }
|
||||
normalize = { method_id = 6, args = ["path"], returns_result = true }
|
||||
fini = { method_id = 4294967295 }
|
||||
19
apps/tests/ny-map-llvm-smoke/main.nyash
Normal file
19
apps/tests/ny-map-llvm-smoke/main.nyash
Normal file
@ -0,0 +1,19 @@
|
||||
// ny-map-llvm-smoke - MapBox by-id plugin path smoke
|
||||
// 目的: MapBox(size/get/set) を by-id 経由で実行し代表出力を確認
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
// Map を生成し、1->42 を設定
|
||||
local m = new MapBox()
|
||||
m.set(1, 42)
|
||||
|
||||
// 取得とサイズ
|
||||
local v = m.get(1)
|
||||
local s = m.size()
|
||||
|
||||
// 代表出力(連結は1回ずつに限定してLLVM簡易対応)
|
||||
print("Map: v=" + v)
|
||||
print("size=" + s)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
13
apps/tests/ny-vinvoke-llvm-ret-size/main.nyash
Normal file
13
apps/tests/ny-vinvoke-llvm-ret-size/main.nyash
Normal file
@ -0,0 +1,13 @@
|
||||
// ny-vinvoke-llvm-ret-size - LLVM向け: by-id(size)の戻り値を検証
|
||||
// 期待: ランタイム標準出力に "Result: 1" が出る(ny_mainの戻り値)
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local m = new MapBox()
|
||||
m.set(1, 42)
|
||||
// size() は固定長、by-id経路の戻り値確認
|
||||
local s = m.size()
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
13
apps/tests/ny-vinvoke-llvm-ret/main.nyash
Normal file
13
apps/tests/ny-vinvoke-llvm-ret/main.nyash
Normal file
@ -0,0 +1,13 @@
|
||||
// ny-vinvoke-llvm-ret - LLVM向け: 可変長(tagged vector) by-id シムの結果を戻り値で検証
|
||||
// 期待: ランタイム標準出力に "Result: 42" が出る(ny_mainの戻り値)
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local m = new MapBox()
|
||||
m.set(1, 42)
|
||||
// 可変長経路を通すため5引数
|
||||
local v = m.get(1, 9, 8, 7, 6)
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
17
apps/tests/ny-vinvoke-smoke/main.nyash
Normal file
17
apps/tests/ny-vinvoke-smoke/main.nyash
Normal file
@ -0,0 +1,17 @@
|
||||
// ny-vinvoke-smoke - 可変長(tagged vector) by-id シムのスモーク
|
||||
// 手順: set(1,42) は固定長シム(<=2引数)。
|
||||
// その後 get(1,9,8,7,6) と5引数で呼び、可変長シム経路を通す(追加引数は無視される想定)。
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local m = new MapBox()
|
||||
m.set(1, 42)
|
||||
|
||||
// 可変長シムを通すため5引数で呼ぶ(先頭以外はダミー)
|
||||
local v = m.get(1, 9, 8, 7, 6)
|
||||
|
||||
print("VInvokeRc: " + v.toString())
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
21
apps/tests/semantics-unified/main.nyash
Normal file
21
apps/tests/semantics-unified/main.nyash
Normal file
@ -0,0 +1,21 @@
|
||||
// semantics-unified: check + for plugin strings and integers
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
// plugin StringBox concat (both sides)
|
||||
local a = new StringBox("12")
|
||||
local b = new StringBox("30")
|
||||
print(a + b) // expect 1230
|
||||
|
||||
// mixed concat (host string + plugin string)
|
||||
print("x = " + a) // expect x = 12
|
||||
|
||||
// plugin IntegerBox add
|
||||
local i = new IntegerBox(10)
|
||||
local j = new IntegerBox(5)
|
||||
print(i + j) // expect 15
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
32
build_aot_only.ps1
Normal file
32
build_aot_only.ps1
Normal file
@ -0,0 +1,32 @@
|
||||
# PowerShell script for building Nyash with LLVM AOT support (no libffi)
|
||||
|
||||
Write-Host "Setting up environment for AOT-only LLVM build..." -ForegroundColor Green
|
||||
|
||||
# Set environment variables
|
||||
$env:LLVM_SYS_180_PREFIX = "C:\LLVM-18"
|
||||
$env:LLVM_SYS_180_FFI_WORKAROUND = "1"
|
||||
$env:LLVM_SYS_NO_LIBFFI = "1" # This is the key - disable libffi
|
||||
$env:PATH = "C:\LLVM-18\bin;" + $env:PATH
|
||||
|
||||
Write-Host "Environment variables set:" -ForegroundColor Yellow
|
||||
Write-Host " LLVM_SYS_180_PREFIX = $env:LLVM_SYS_180_PREFIX"
|
||||
Write-Host " LLVM_SYS_NO_LIBFFI = $env:LLVM_SYS_NO_LIBFFI (libffi disabled for AOT)"
|
||||
|
||||
# Clean build directory
|
||||
Write-Host "`nCleaning previous build..." -ForegroundColor Yellow
|
||||
cargo clean
|
||||
|
||||
# Build with LLVM feature
|
||||
Write-Host "`nBuilding Nyash with LLVM AOT support..." -ForegroundColor Green
|
||||
cargo build --bin nyash --release --features llvm
|
||||
|
||||
# Check output
|
||||
Write-Host "`nChecking build output..." -ForegroundColor Yellow
|
||||
if (Test-Path "target\release\nyash.exe") {
|
||||
Write-Host "SUCCESS: nyash.exe created!" -ForegroundColor Green
|
||||
Get-Item "target\release\nyash.exe" | Format-List Name, Length, LastWriteTime
|
||||
} else {
|
||||
Write-Host "ERROR: nyash.exe not found" -ForegroundColor Red
|
||||
Write-Host "Listing exe files in target\release:"
|
||||
Get-ChildItem "target\release\*.exe" | Format-Table Name, Length
|
||||
}
|
||||
19
build_clean_aot.bat
Normal file
19
build_clean_aot.bat
Normal file
@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
chcp 437 >nul
|
||||
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
|
||||
set "LLVM_SYS_180_FFI_WORKAROUND=1"
|
||||
set "LLVM_SYS_180_NO_LIBFFI=1"
|
||||
set "PATH=C:\LLVM-18\bin;%PATH%"
|
||||
|
||||
echo Cleaning and building Nyash with LLVM AOT (no libffi)...
|
||||
cargo clean
|
||||
cargo build --bin nyash --release --features llvm
|
||||
|
||||
echo.
|
||||
echo Checking output...
|
||||
if exist target\release\nyash.exe (
|
||||
echo SUCCESS: nyash.exe created
|
||||
dir target\release\nyash.exe
|
||||
) else (
|
||||
echo ERROR: nyash.exe not found
|
||||
)
|
||||
17
build_llvm_clean.bat
Normal file
17
build_llvm_clean.bat
Normal file
@ -0,0 +1,17 @@
|
||||
@echo off
|
||||
echo Cleaning environment and building with LLVM...
|
||||
|
||||
REM Remove old environment variables
|
||||
set LLVM_CONFIG_PATH=
|
||||
set LLVM_SYS_180_NO_LIBFFI=
|
||||
set LLVM_SYS_180_FFI_WORKAROUND=
|
||||
|
||||
REM Set new environment variables
|
||||
set LLVM_SYS_180_PREFIX=C:\LLVM-18
|
||||
set PATH=C:\LLVM-18\bin;%PATH%
|
||||
set LLVM_SYS_NO_LIBFFI=1
|
||||
set LLVM_SYS_180_STRICT_VERSIONING=0
|
||||
set RUST_LOG=llvm_sys=trace
|
||||
|
||||
echo Building with verbose output...
|
||||
cargo +stable-x86_64-pc-windows-msvc build --release --features llvm -vv
|
||||
10
build_llvm_simple.bat
Normal file
10
build_llvm_simple.bat
Normal file
@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
set LLVM_SYS_180_PREFIX=C:\LLVM-18
|
||||
set LLVM_SYS_NO_LIBFFI=1
|
||||
set LLVM_SYS_180_STRICT_VERSIONING=0
|
||||
set PATH=C:\LLVM-18\bin;%PATH%
|
||||
|
||||
echo LLVM_SYS_180_PREFIX=%LLVM_SYS_180_PREFIX%
|
||||
echo Building with 24 threads...
|
||||
|
||||
cargo build --release --features llvm -j24
|
||||
34
build_llvm_windows.bat
Normal file
34
build_llvm_windows.bat
Normal file
@ -0,0 +1,34 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
echo [build_llvm_windows] Starting LLVM build...
|
||||
|
||||
REM Set LLVM path
|
||||
set "LLVM_SYS_180_PREFIX=C:\Program Files\LLVM"
|
||||
set "PATH=C:\Program Files\LLVM\bin;%PATH%"
|
||||
|
||||
REM Check LLVM
|
||||
where clang.exe >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo [build_llvm_windows] ERROR: clang.exe not found
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [build_llvm_windows] LLVM found, building nyash...
|
||||
|
||||
REM Build nyash with LLVM
|
||||
cargo build --release --features llvm
|
||||
|
||||
if errorlevel 1 (
|
||||
echo [build_llvm_windows] ERROR: cargo build failed
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [build_llvm_windows] Build successful!
|
||||
|
||||
REM Build ny-echo-lite
|
||||
echo [build_llvm_windows] Building ny-echo-lite...
|
||||
powershell -ExecutionPolicy Bypass -File tools\build_llvm.ps1 apps\tests\ny-echo-lite\main.nyash -Out app_echo.exe
|
||||
|
||||
echo [build_llvm_windows] Done!
|
||||
endlocal
|
||||
26
build_llvm_wsl.sh
Normal file
26
build_llvm_wsl.sh
Normal file
@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Building Nyash with LLVM for Windows from WSL..."
|
||||
|
||||
# Windows側のLLVMを使う
|
||||
export LLVM_SYS_180_PREFIX="/mnt/c/Program Files/LLVM"
|
||||
|
||||
# 追加の環境変数(Qt6ビルドで使っていたかもしれない技)
|
||||
export LLVM_SYS_180_FFI_WORKAROUND=1
|
||||
export CC=x86_64-w64-mingw32-gcc
|
||||
export CXX=x86_64-w64-mingw32-g++
|
||||
export AR=x86_64-w64-mingw32-ar
|
||||
|
||||
# MinGWターゲットで試す(Qt6と同じ方法)
|
||||
echo "Trying MinGW target..."
|
||||
cargo build --target x86_64-pc-windows-gnu --release --features llvm
|
||||
|
||||
# 成功したら実行ファイルの場所を表示
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Build successful!"
|
||||
echo "Binary at: target/x86_64-pc-windows-gnu/release/nyash.exe"
|
||||
else
|
||||
echo "MinGW build failed, trying MSVC target with cargo-xwin..."
|
||||
cargo xwin build --target x86_64-pc-windows-msvc --release --features llvm
|
||||
fi
|
||||
23
build_llvm_wsl_msvc.sh
Normal file
23
build_llvm_wsl_msvc.sh
Normal file
@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
# Build Windows exe with LLVM from WSL using cross compilation
|
||||
|
||||
echo "Setting up Windows cross-compilation with LLVM..."
|
||||
|
||||
# Set environment variables for WSL cross-compilation
|
||||
export LLVM_SYS_180_PREFIX="C:\\LLVM-18"
|
||||
export LLVM_SYS_180_FFI_WORKAROUND="1"
|
||||
export LLVM_SYS_NO_LIBFFI="1" # This is the key!
|
||||
|
||||
# Use cargo-xwin for cross compilation
|
||||
echo "Building nyash.exe for Windows with LLVM support..."
|
||||
cargo xwin build --target x86_64-pc-windows-msvc --release --features llvm -j32
|
||||
|
||||
# Check if successful
|
||||
if [ -f "target/x86_64-pc-windows-msvc/release/nyash.exe" ]; then
|
||||
echo "Build successful!"
|
||||
ls -la target/x86_64-pc-windows-msvc/release/nyash.exe
|
||||
else
|
||||
echo "Build failed - nyash.exe not found"
|
||||
echo "Checking what was built:"
|
||||
ls -la target/x86_64-pc-windows-msvc/release/ 2>/dev/null || echo "Target directory not found"
|
||||
fi
|
||||
20
build_nyash_exe.bat
Normal file
20
build_nyash_exe.bat
Normal file
@ -0,0 +1,20 @@
|
||||
@echo off
|
||||
chcp 437 >nul
|
||||
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
|
||||
set "LLVM_SYS_180_FFI_WORKAROUND=1"
|
||||
set "PATH=C:\LLVM-18\bin;%PATH%"
|
||||
|
||||
echo Building Nyash executable with LLVM support without libffi...
|
||||
set "LLVM_SYS_NO_LIBFFI=1"
|
||||
cargo build --bin nyash --release --features llvm
|
||||
|
||||
echo.
|
||||
echo Checking output...
|
||||
if exist target\release\nyash.exe (
|
||||
echo SUCCESS: nyash.exe created
|
||||
dir target\release\nyash.exe
|
||||
) else (
|
||||
echo ERROR: nyash.exe not found
|
||||
echo Listing exe files:
|
||||
dir target\release\*.exe
|
||||
)
|
||||
11
build_ps.ps1
Normal file
11
build_ps.ps1
Normal file
@ -0,0 +1,11 @@
|
||||
# PowerShell build script
|
||||
$env:LLVM_SYS_180_PREFIX = "C:\LLVM-18"
|
||||
$env:LLVM_SYS_180_FFI_WORKAROUND = "1"
|
||||
$env:LLVM_SYS_NO_LIBFFI = "1"
|
||||
|
||||
Write-Host "Environment variables set:"
|
||||
Write-Host "LLVM_SYS_180_PREFIX = $env:LLVM_SYS_180_PREFIX"
|
||||
Write-Host "LLVM_SYS_NO_LIBFFI = $env:LLVM_SYS_NO_LIBFFI"
|
||||
|
||||
Write-Host "`nBuilding..."
|
||||
cargo build --bin nyash --release --features llvm
|
||||
4
build_simple.ps1
Normal file
4
build_simple.ps1
Normal file
@ -0,0 +1,4 @@
|
||||
$env:LLVM_SYS_180_PREFIX = "C:\LLVM-18"
|
||||
$env:LLVM_SYS_180_FFI_WORKAROUND = "1"
|
||||
$env:LLVM_SYS_NO_LIBFFI = "1"
|
||||
cargo build --release --features llvm
|
||||
52
build_win_aot.bat
Normal file
52
build_win_aot.bat
Normal file
@ -0,0 +1,52 @@
|
||||
@echo off
|
||||
setlocal ENABLEDELAYEDEXPANSION
|
||||
chcp 65001 >nul
|
||||
|
||||
echo [Nyash AOT Build - libffi disabled]
|
||||
echo ===================================
|
||||
|
||||
REM Set LLVM environment variables
|
||||
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
|
||||
set "LLVM_SYS_NO_LIBFFI=1"
|
||||
set "LLVM_SYS_180_FFI_WORKAROUND=1"
|
||||
|
||||
echo LLVM_SYS_180_PREFIX=%LLVM_SYS_180_PREFIX%
|
||||
echo LLVM_SYS_NO_LIBFFI=%LLVM_SYS_NO_LIBFFI%
|
||||
|
||||
REM Verify LLVM installation
|
||||
if not exist "%LLVM_SYS_180_PREFIX%\include\llvm-c\Core.h" (
|
||||
echo ERROR: Core.h not found at %LLVM_SYS_180_PREFIX%\include\llvm-c\Core.h
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if not exist "%LLVM_SYS_180_PREFIX%\lib\cmake\llvm\LLVMConfig.cmake" (
|
||||
echo ERROR: LLVMConfig.cmake not found at %LLVM_SYS_180_PREFIX%\lib\cmake\llvm\LLVMConfig.cmake
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo LLVM installation verified successfully!
|
||||
|
||||
REM Add LLVM to PATH
|
||||
set "PATH=%LLVM_SYS_180_PREFIX%\bin;%PATH%"
|
||||
|
||||
REM Clean and build
|
||||
echo.
|
||||
echo Cleaning previous build...
|
||||
cargo clean
|
||||
|
||||
echo.
|
||||
echo Building Nyash with LLVM AOT support (no libffi)...
|
||||
cargo build --bin nyash --release --features llvm
|
||||
|
||||
REM Check result
|
||||
echo.
|
||||
if exist "target\release\nyash.exe" (
|
||||
echo SUCCESS: nyash.exe built successfully!
|
||||
echo.
|
||||
dir target\release\nyash.exe
|
||||
) else (
|
||||
echo ERROR: Build failed - nyash.exe not found
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
endlocal
|
||||
131
build_win_env_and_build.bat
Normal file
131
build_win_env_and_build.bat
Normal file
@ -0,0 +1,131 @@
|
||||
@echo off
|
||||
setlocal ENABLEDELAYEDEXPANSION
|
||||
chcp 65001 >nul
|
||||
|
||||
rem =============================
|
||||
rem User config (編集ポイント)
|
||||
rem =============================
|
||||
rem LLVM のプレフィックス(ヘッダ/Core.h と CMake/LLVMConfig.cmake がある場所)
|
||||
rem 例) 自前CMake: C:\LLVM-18
|
||||
rem 例) vcpkg: C:\vcpkg\installed\x64-windows
|
||||
set "LLVM_PREFIX=C:\LLVM-18"
|
||||
|
||||
rem AOTのみなら 0(libffi無効) / JITも使うなら 1(libffi有効)
|
||||
set "USE_LIBFFI=0"
|
||||
|
||||
rem libffi のプレフィックス(USE_LIBFFI=1 のとき使用; vcpkg推奨)
|
||||
set "LIBFFI_PREFIX=C:\vcpkg\installed\x64-windows"
|
||||
|
||||
rem Cargo 子プロセスへ PATH を強制注入(有効化=1)
|
||||
set "FORCE_CARGO_PATH=1"
|
||||
|
||||
rem =============================
|
||||
rem VS 開発環境(MSVC x64)を有効化
|
||||
rem =============================
|
||||
if exist "%ProgramFiles%\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" (
|
||||
call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" -arch=x64
|
||||
) else if exist "%ProgramFiles(x86)%\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\VsDevCmd.bat" (
|
||||
call "%ProgramFiles(x86)%\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\VsDevCmd.bat" -arch=x64
|
||||
)
|
||||
|
||||
rem =============================
|
||||
rem 前提チェック
|
||||
rem =============================
|
||||
if not exist "%LLVM_PREFIX%\include\llvm-c\Core.h" (
|
||||
echo [ERROR] Core.h not found: "%LLVM_PREFIX%\include\llvm-c\Core.h"
|
||||
echo LLVM_PREFIX をヘッダがある場所に直してください。
|
||||
exit /b 2
|
||||
)
|
||||
if not exist "%LLVM_PREFIX%\lib\cmake\llvm\LLVMConfig.cmake" (
|
||||
echo [ERROR] LLVMConfig.cmake not found: "%LLVM_PREFIX%\lib\cmake\llvm\LLVMConfig.cmake"
|
||||
echo LLVM_PREFIX を CMake 定義がある場所に直してください。
|
||||
exit /b 2
|
||||
)
|
||||
if not exist "%LLVM_PREFIX%\lib\LLVMCore.lib" (
|
||||
echo [WARN] LLVMCore.lib が見つかりません: "%LLVM_PREFIX%\lib\LLVMCore.lib"
|
||||
)
|
||||
if not exist "%LLVM_PREFIX%\lib\LLVMSupport.lib" (
|
||||
echo [WARN] LLVMSupport.lib が見つかりません: "%LLVM_PREFIX%\lib\LLVMSupport.lib"
|
||||
)
|
||||
|
||||
rem =============================
|
||||
rem 衝突しやすい環境変数を掃除
|
||||
rem =============================
|
||||
set LLVM_CONFIG_PATH=
|
||||
set LLVM_SYS_180_NO_LIBFFI=
|
||||
set LLVM_SYS_180_FFI_WORKAROUND=
|
||||
|
||||
rem =============================
|
||||
rem このシェル限定の環境を設定
|
||||
rem =============================
|
||||
set "LLVM_SYS_180_PREFIX=%LLVM_PREFIX%"
|
||||
set "LLVM_SYS_180_INCLUDE_DIR=%LLVM_PREFIX%\include"
|
||||
set "LLVM_SYS_180_LIB_DIR=%LLVM_PREFIX%\lib"
|
||||
set "LLVM_SYS_180_STRICT_VERSIONING=0"
|
||||
set "PATH=%LLVM_PREFIX%\bin;%PATH%"
|
||||
|
||||
if "%USE_LIBFFI%"=="1" (
|
||||
set "LLVM_SYS_NO_LIBFFI="
|
||||
if exist "%LIBFFI_PREFIX%\lib\ffi.lib" (
|
||||
set "LIB=%LIBFFI_PREFIX%\lib;%LIB%"
|
||||
set "PATH=%LIBFFI_PREFIX%\bin;%PATH%"
|
||||
) else (
|
||||
echo [WARN] libffi not found at "%LIBFFI_PREFIX%\lib\ffi.lib" (JIT想定なら vcpkg で libffi を追加してください)
|
||||
)
|
||||
) else (
|
||||
rem AOT-only
|
||||
set "LLVM_SYS_NO_LIBFFI=1"
|
||||
)
|
||||
|
||||
rem =============================
|
||||
rem 現在の設定を表示
|
||||
rem =============================
|
||||
echo [ENV] LLVM_SYS_180_PREFIX=%LLVM_SYS_180_PREFIX%
|
||||
echo [ENV] LLVM_SYS_180_INCLUDE_DIR=%LLVM_SYS_180_INCLUDE_DIR%
|
||||
echo [ENV] LLVM_SYS_180_LIB_DIR=%LLVM_SYS_180_LIB_DIR%
|
||||
echo [ENV] LLVM_SYS_NO_LIBFFI=%LLVM_SYS_NO_LIBFFI%
|
||||
echo [ENV] LLVM_SYS_180_STRICT_VERSIONING=%LLVM_SYS_180_STRICT_VERSIONING%
|
||||
where cl
|
||||
where link
|
||||
where clang
|
||||
|
||||
rem =============================
|
||||
rem Cargo 子プロセスへ PATH を強制注入(必要なら)
|
||||
rem =============================
|
||||
if "%FORCE_CARGO_PATH%"=="1" (
|
||||
if not exist ".cargo" mkdir ".cargo"
|
||||
> ".cargo\config.toml" (
|
||||
echo [env]
|
||||
echo LLVM_SYS_180_PREFIX = "%LLVM_SYS_180_PREFIX:\=\\%"
|
||||
echo LLVM_SYS_180_INCLUDE_DIR = "%LLVM_SYS_180_INCLUDE_DIR:\=\\%"
|
||||
echo LLVM_SYS_180_LIB_DIR = "%LLVM_SYS_180_LIB_DIR:\=\\%"
|
||||
if "%USE_LIBFFI%"=="1" (echo LLVM_SYS_NO_LIBFFI = "") else (echo LLVM_SYS_NO_LIBFFI = "1")
|
||||
echo LLVM_SYS_180_STRICT_VERSIONING = "0"
|
||||
echo PATH = { value = "%LLVM_SYS_180_PREFIX:\=\\%\bin;{PATH}", force = true }
|
||||
)
|
||||
echo [INFO] Wrote .cargo\config.toml
|
||||
)
|
||||
|
||||
rem =============================
|
||||
rem Rust toolchain を MSVC に固定&ビルド
|
||||
rem =============================
|
||||
echo [INFO] Using MSVC toolchain...
|
||||
rustup default stable-x86_64-pc-windows-msvc
|
||||
|
||||
set "RUST_LOG=llvm_sys=trace"
|
||||
echo [INFO] Cleaning...
|
||||
cargo clean
|
||||
|
||||
echo [INFO] Building nyash (release, feature=llvm)...
|
||||
cargo +stable-x86_64-pc-windows-msvc build --release --features llvm -vv -j24
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] cargo build failed. 上の末尾(link.exe の行/ffi.libの有無)を確認してください。
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [OK] Build complete.
|
||||
echo [HINT] AOT→EXE: powershell -ExecutionPolicy Bypass -File tools\build_llvm.ps1 apps\tests\ny-echo-lite\main.nyash -Out app_echo.exe
|
||||
|
||||
endlocal
|
||||
exit /b 0
|
||||
|
||||
20
build_win_llvm.bat
Normal file
20
build_win_llvm.bat
Normal file
@ -0,0 +1,20 @@
|
||||
@echo off
|
||||
echo Building Nyash with LLVM for Windows...
|
||||
|
||||
REM Set environment variables
|
||||
set LLVM_SYS_180_PREFIX=C:\LLVM-18
|
||||
set LLVM_SYS_180_FFI_WORKAROUND=1
|
||||
set LLVM_SYS_NO_LIBFFI=1
|
||||
|
||||
echo Environment variables:
|
||||
echo LLVM_SYS_180_PREFIX = %LLVM_SYS_180_PREFIX%
|
||||
echo LLVM_SYS_180_FFI_WORKAROUND = %LLVM_SYS_180_FFI_WORKAROUND%
|
||||
echo LLVM_SYS_NO_LIBFFI = %LLVM_SYS_NO_LIBFFI%
|
||||
|
||||
echo.
|
||||
echo Building...
|
||||
cargo build --release --features llvm
|
||||
|
||||
echo.
|
||||
echo Done!
|
||||
pause
|
||||
16
build_windows_llvm.bat
Normal file
16
build_windows_llvm.bat
Normal file
@ -0,0 +1,16 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
REM Set LLVM path
|
||||
set "LLVM_SYS_180_PREFIX=C:\Program Files\LLVM"
|
||||
|
||||
REM Build nyash with LLVM feature
|
||||
echo Building nyash with LLVM backend...
|
||||
cargo build --release --features llvm
|
||||
|
||||
REM Build ny-echo-lite to exe
|
||||
echo Building ny-echo-lite...
|
||||
powershell -ExecutionPolicy Bypass -File tools\build_llvm.ps1 apps\tests\ny-echo-lite\main.nyash -Out app_echo.exe
|
||||
|
||||
echo Done!
|
||||
endlocal
|
||||
9
build_windows_with_llvm.bat
Normal file
9
build_windows_with_llvm.bat
Normal file
@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
|
||||
set "LLVM_SYS_180_FFI_WORKAROUND=1"
|
||||
set "PATH=C:\LLVM-18\bin;%PATH%"
|
||||
|
||||
echo Building Nyash with LLVM support...
|
||||
cargo build --release --features llvm
|
||||
|
||||
echo Done!
|
||||
20
build_with_existing_llvm.bat
Normal file
20
build_with_existing_llvm.bat
Normal file
@ -0,0 +1,20 @@
|
||||
@echo off
|
||||
echo Using existing LLVM at C:\LLVM-18
|
||||
|
||||
REM Set environment variables
|
||||
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
|
||||
set "LLVM_SYS_180_NO_LIBFFI=1"
|
||||
set "LLVM_SYS_180_FFI_WORKAROUND=1"
|
||||
set "PATH=C:\LLVM-18\bin;%PATH%"
|
||||
|
||||
echo.
|
||||
echo Building Nyash without libffi (AOT only)...
|
||||
cargo build --bin nyash --release --features llvm --no-default-features --features cli,plugins
|
||||
|
||||
echo.
|
||||
if exist target\release\nyash.exe (
|
||||
echo SUCCESS: nyash.exe created!
|
||||
dir target\release\nyash.exe
|
||||
) else (
|
||||
echo Build failed. Trying alternative approach...
|
||||
)
|
||||
13
build_with_setx.bat
Normal file
13
build_with_setx.bat
Normal file
@ -0,0 +1,13 @@
|
||||
@echo off
|
||||
echo Setting system environment variables for LLVM...
|
||||
|
||||
REM Set system-wide environment variables
|
||||
setx LLVM_SYS_180_PREFIX "C:\LLVM-18"
|
||||
setx LLVM_SYS_NO_LIBFFI "1"
|
||||
setx LLVM_SYS_180_FFI_WORKAROUND "1"
|
||||
|
||||
echo.
|
||||
echo Environment variables set. Please open a NEW command prompt and run:
|
||||
echo cargo build --bin nyash --release --features llvm
|
||||
echo.
|
||||
pause
|
||||
13
check_build.bat
Normal file
13
check_build.bat
Normal file
@ -0,0 +1,13 @@
|
||||
@echo off
|
||||
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
|
||||
set "LLVM_SYS_180_FFI_WORKAROUND=1"
|
||||
|
||||
echo Checking build status...
|
||||
dir target\release\*.exe 2>nul
|
||||
if errorlevel 1 (
|
||||
echo No exe files in target\release
|
||||
dir target\x86_64-pc-windows-msvc\release\*.exe 2>nul
|
||||
if errorlevel 1 (
|
||||
echo No exe files in Windows target either
|
||||
)
|
||||
)
|
||||
37
check_llvm_env.bat
Normal file
37
check_llvm_env.bat
Normal file
@ -0,0 +1,37 @@
|
||||
@echo off
|
||||
echo === LLVM Environment Check ===
|
||||
echo.
|
||||
echo LLVM_SYS_180_PREFIX = %LLVM_SYS_180_PREFIX%
|
||||
echo.
|
||||
|
||||
echo Checking if LLVM files exist...
|
||||
if exist "C:\LLVM-18\include\llvm-c\Core.h" (
|
||||
echo [OK] Core.h found at C:\LLVM-18\include\llvm-c\Core.h
|
||||
) else (
|
||||
echo [ERROR] Core.h NOT FOUND
|
||||
)
|
||||
|
||||
if exist "C:\LLVM-18\lib\cmake\llvm\LLVMConfig.cmake" (
|
||||
echo [OK] LLVMConfig.cmake found at C:\LLVM-18\lib\cmake\llvm\LLVMConfig.cmake
|
||||
) else (
|
||||
echo [ERROR] LLVMConfig.cmake NOT FOUND
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Setting environment variables...
|
||||
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
|
||||
set "LLVM_SYS_180_FFI_WORKAROUND=1"
|
||||
set "LLVM_SYS_NO_LIBFFI=1"
|
||||
|
||||
echo.
|
||||
echo After setting:
|
||||
echo LLVM_SYS_180_PREFIX = %LLVM_SYS_180_PREFIX%
|
||||
echo LLVM_SYS_180_FFI_WORKAROUND = %LLVM_SYS_180_FFI_WORKAROUND%
|
||||
echo LLVM_SYS_NO_LIBFFI = %LLVM_SYS_NO_LIBFFI%
|
||||
|
||||
echo.
|
||||
echo Testing cargo environment...
|
||||
cargo --version
|
||||
rustc --version
|
||||
|
||||
pause
|
||||
@ -99,11 +99,13 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
|
||||
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
// Resolve receiver instance from handle first; fallback to legacy VM args (param index)
|
||||
let mut instance_id: u32 = 0;
|
||||
let mut real_type_id: u32 = 0;
|
||||
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
|
||||
if a0 > 0 {
|
||||
if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) {
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
real_type_id = p.inner.type_id;
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
}
|
||||
}
|
||||
@ -122,7 +124,8 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
|
||||
if invoke.is_none() { return 0; }
|
||||
// Build TLV args from a1/a2 if present. Prefer handles/StringBox/IntegerBox via runtime host.
|
||||
use nyash_rust::{jit::rt::handles, backend::vm::VMValue};
|
||||
let nargs = argc.saturating_sub(1).max(0) as usize;
|
||||
// argc from LLVM lowering is explicit arg count (excludes receiver)
|
||||
let nargs = argc.max(0) as usize;
|
||||
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
|
||||
// Encode legacy VM arg at position into provided buffer (avoid capturing &mut buf)
|
||||
let mut encode_from_legacy_into = |dst: &mut Vec<u8>, arg_pos: usize| {
|
||||
@ -335,7 +338,8 @@ pub extern "C" fn nyash_plugin_invoke3_f64(
|
||||
if invoke.is_none() { return 0.0; }
|
||||
// Build TLV args from a1/a2 with String/Integer support
|
||||
use nyash_rust::{jit::rt::handles, backend::vm::VMValue};
|
||||
let nargs = argc.saturating_sub(1).max(0) as usize;
|
||||
// argc from LLVM lowering is explicit arg count (excludes receiver)
|
||||
let nargs = argc.max(0) as usize;
|
||||
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
|
||||
let mut encode_from_legacy = |arg_pos: usize| {
|
||||
nyash_rust::jit::rt::with_legacy_vm_args(|args| {
|
||||
@ -576,6 +580,205 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64
|
||||
0
|
||||
}
|
||||
|
||||
// General by-name invoke: (recv_handle, method_cstr, argc, a1, a2) -> i64
|
||||
// Export name: nyash.plugin.invoke_by_name_i64
|
||||
#[export_name = "nyash.plugin.invoke_by_name_i64"]
|
||||
pub extern "C" fn nyash_plugin_invoke_by_name_i64(
|
||||
recv_handle: i64,
|
||||
method: *const i8,
|
||||
argc: i64,
|
||||
a1: i64,
|
||||
a2: i64,
|
||||
) -> i64 {
|
||||
if method.is_null() { return 0; }
|
||||
let mname = unsafe { std::ffi::CStr::from_ptr(method) };
|
||||
let Ok(method_str) = mname.to_str() else { return 0 };
|
||||
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
let mut instance_id: u32 = 0;
|
||||
let mut type_id: u32 = 0;
|
||||
let mut box_type: Option<String> = None;
|
||||
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
|
||||
if recv_handle > 0 {
|
||||
if let Some(obj) = nyash_rust::jit::rt::handles::get(recv_handle as u64) {
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id(); type_id = p.inner.type_id; box_type = Some(p.box_type.clone());
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
if invoke.is_none() { return 0; }
|
||||
let box_type = box_type.unwrap_or_default();
|
||||
// Resolve method_id via PluginHost by name
|
||||
let mh = if let Ok(host) = nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host().read() {
|
||||
host.resolve_method(&box_type, method_str)
|
||||
} else { return 0 };
|
||||
let method_id = match mh { Ok(h) => h.method_id, Err(_) => return 0 } as u32;
|
||||
// Build TLV args from a1/a2 (no legacy in LLVM path)
|
||||
// argc is the number of explicit arguments (receiver excluded)
|
||||
let nargs = argc.max(0) as usize;
|
||||
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
|
||||
nyrt_encode_arg_or_legacy(&mut buf, a1, 1);
|
||||
if nargs >= 2 { nyrt_encode_arg_or_legacy(&mut buf, a2, 2); }
|
||||
// Execute
|
||||
let mut out = vec![0u8; 512]; let mut out_len: usize = out.len();
|
||||
let rc = unsafe { invoke.unwrap()(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
|
||||
if rc != 0 { return 0; }
|
||||
if let Some((tag, _sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
||||
match tag {
|
||||
3 => { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } }
|
||||
1 => { return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; }
|
||||
8 => { if payload.len()==8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type=u32::from_le_bytes(t); let r_inst=u32::from_le_bytes(i); let pb=nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2(box_type.clone(), r_type, r_inst, invoke.unwrap()); let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox>=std::sync::Arc::new(pb); let h=nyash_rust::jit::rt::handles::to_handle(arc); return h as i64; } }
|
||||
5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref()==Some("1") { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f=f64::from_le_bytes(b); return f as i64; } } }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// Tagged by-id invoke (supports f64/int/handle for first two args)
|
||||
// tag: 3=I64, 5=F64(bits), 8=Handle
|
||||
#[export_name = "nyash_plugin_invoke3_tagged_i64"]
|
||||
pub extern "C" fn nyash_plugin_invoke3_tagged_i64(
|
||||
type_id: i64,
|
||||
method_id: i64,
|
||||
argc: i64,
|
||||
a0: i64,
|
||||
a1: i64,
|
||||
tag1: i64,
|
||||
a2: i64,
|
||||
tag2: i64,
|
||||
a3: i64,
|
||||
tag3: i64,
|
||||
a4: i64,
|
||||
tag4: i64,
|
||||
) -> i64 {
|
||||
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
// Resolve receiver invoke and actual plugin type_id
|
||||
let mut instance_id: u32 = 0;
|
||||
let mut real_type_id: u32 = type_id as u32;
|
||||
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
|
||||
if a0 > 0 {
|
||||
if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) {
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
real_type_id = p.inner.type_id;
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
if invoke.is_none() { return 0; }
|
||||
// Build TLV from tags
|
||||
// argc is the number of explicit arguments (receiver excluded)
|
||||
let nargs = argc.max(0) as usize;
|
||||
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
|
||||
let mut enc = |val: i64, tag: i64| {
|
||||
match tag {
|
||||
3 => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, val),
|
||||
5 => { let bits = val as u64; let f = f64::from_bits(bits); nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, f); },
|
||||
8 => {
|
||||
if val > 0 {
|
||||
if let Some(obj) = nyash_rust::jit::rt::handles::get(val as u64) {
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.instance_id());
|
||||
} else {
|
||||
let s = obj.to_string_box().value;
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, 0);
|
||||
}
|
||||
}
|
||||
_ => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, val),
|
||||
}
|
||||
};
|
||||
if nargs >= 1 { enc(a1, tag1); }
|
||||
if nargs >= 2 { enc(a2, tag2); }
|
||||
if nargs >= 3 { enc(a3, tag3); }
|
||||
if nargs >= 4 { enc(a4, tag4); }
|
||||
// Invoke
|
||||
let mut out = vec![0u8; 512]; let mut out_len: usize = out.len();
|
||||
let rc = unsafe { invoke.unwrap()(real_type_id, method_id as u32, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
|
||||
if rc != 0 { return 0; }
|
||||
if let Some((tag, _sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
||||
match tag {
|
||||
3 => { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } }
|
||||
1 => { return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; }
|
||||
8 => { if payload.len()==8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type=u32::from_le_bytes(t); let r_inst=u32::from_le_bytes(i); let pb=nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2("PluginBox".into(), r_type, r_inst, invoke.unwrap()); let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox>=std::sync::Arc::new(pb); let h=nyash_rust::jit::rt::handles::to_handle(arc); return h as i64; } }
|
||||
5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref()==Some("1") { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f=f64::from_le_bytes(b); return f as i64; } } }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// Variable-length tagged invoke by-id
|
||||
// Exported as: nyash.plugin.invoke_tagged_v_i64(i64 type_id, i64 method_id, i64 argc, i64 recv_h, i64* vals, i64* tags) -> i64
|
||||
#[export_name = "nyash.plugin.invoke_tagged_v_i64"]
|
||||
pub extern "C" fn nyash_plugin_invoke_tagged_v_i64(
|
||||
type_id: i64,
|
||||
method_id: i64,
|
||||
argc: i64,
|
||||
recv_h: i64,
|
||||
vals: *const i64,
|
||||
tags: *const i64,
|
||||
) -> i64 {
|
||||
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
if recv_h <= 0 { return 0; }
|
||||
// Resolve receiver invoke
|
||||
let mut instance_id: u32 = 0;
|
||||
let mut real_type_id: u32 = 0;
|
||||
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
|
||||
if let Some(obj) = nyash_rust::jit::rt::handles::get(recv_h as u64) {
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
real_type_id = p.inner.type_id;
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
}
|
||||
}
|
||||
if invoke.is_none() { return 0; }
|
||||
let nargs = argc.saturating_sub(1).max(0) as usize;
|
||||
let (vals, tags) = if nargs > 0 && !vals.is_null() && !tags.is_null() {
|
||||
unsafe {
|
||||
(std::slice::from_raw_parts(vals, nargs), std::slice::from_raw_parts(tags, nargs))
|
||||
}
|
||||
} else { (&[][..], &[][..]) };
|
||||
|
||||
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
|
||||
for i in 0..nargs {
|
||||
match tags[i] {
|
||||
3 => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, vals[i]),
|
||||
5 => { let f = f64::from_bits(vals[i] as u64); nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, f); },
|
||||
8 => {
|
||||
if let Some(obj) = nyash_rust::jit::rt::handles::get(vals[i] as u64) {
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.instance_id());
|
||||
} else {
|
||||
let s = obj.to_string_box().value;
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s);
|
||||
}
|
||||
} else {
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, 0);
|
||||
}
|
||||
}
|
||||
_ => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, vals[i]),
|
||||
}
|
||||
}
|
||||
let mut out = vec![0u8; 1024]; let mut out_len: usize = out.len();
|
||||
let rc = unsafe { invoke.unwrap()(real_type_id, method_id as u32, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
|
||||
if rc != 0 { return 0; }
|
||||
if let Some((tag, _sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
||||
match tag {
|
||||
3 => { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } }
|
||||
1 => { return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; }
|
||||
8 => { if payload.len()==8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type=u32::from_le_bytes(t); let r_inst=u32::from_le_bytes(i); let pb=nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2("PluginBox".into(), r_type, r_inst, invoke.unwrap()); let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox>=std::sync::Arc::new(pb); let h=nyash_rust::jit::rt::handles::to_handle(arc); return h as i64; } }
|
||||
5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref()==Some("1") { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f=f64::from_le_bytes(b); return f as i64; } } }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// ---- Handle-based birth shims for AOT/JIT object linkage ----
|
||||
// These resolve symbols like "nyash.string.birth_h" referenced by ObjectModule.
|
||||
|
||||
@ -729,6 +932,35 @@ pub extern "C" fn nyash_string_new(ptr: *const u8, len: i32) -> *mut i8 {
|
||||
raw as *mut i8
|
||||
}
|
||||
|
||||
// ---- Unified semantics shims (handle-based) ----
|
||||
// Exported as: nyash.semantics.add_hh(i64 lhs_handle, i64 rhs_handle) -> i64 (NyashBox handle)
|
||||
#[export_name = "nyash.semantics.add_hh"]
|
||||
pub extern "C" fn nyash_semantics_add_hh_export(lhs_h: i64, rhs_h: i64) -> i64 {
|
||||
use nyash_rust::{box_trait::{StringBox, IntegerBox}, runtime::semantics};
|
||||
use nyash_rust::jit::rt::handles;
|
||||
if lhs_h <= 0 || rhs_h <= 0 { return 0; }
|
||||
let lhs = if let Some(obj) = handles::get(lhs_h as u64) { obj } else { return 0 };
|
||||
let rhs = if let Some(obj) = handles::get(rhs_h as u64) { obj } else { return 0 };
|
||||
let ls_opt = semantics::coerce_to_string(lhs.as_ref());
|
||||
let rs_opt = semantics::coerce_to_string(rhs.as_ref());
|
||||
if ls_opt.is_some() || rs_opt.is_some() {
|
||||
let ls = ls_opt.unwrap_or_else(|| lhs.to_string_box().value);
|
||||
let rs = rs_opt.unwrap_or_else(|| rhs.to_string_box().value);
|
||||
let s = format!("{}{}", ls, rs);
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::new(StringBox::new(s));
|
||||
return handles::to_handle(arc) as i64;
|
||||
}
|
||||
if let (Some(li), Some(ri)) = (semantics::coerce_to_i64(lhs.as_ref()), semantics::coerce_to_i64(rhs.as_ref())) {
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::new(IntegerBox::new(li + ri));
|
||||
return handles::to_handle(arc) as i64;
|
||||
}
|
||||
// Fallback: stringify both and concat to preserve total order
|
||||
let ls = lhs.to_string_box().value;
|
||||
let rs = rhs.to_string_box().value;
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::new(StringBox::new(format!("{}{}", ls, rs)));
|
||||
handles::to_handle(arc) as i64
|
||||
}
|
||||
|
||||
// ---- Array helpers for LLVM lowering (handle-based) ----
|
||||
// Exported as: nyash_array_get_h(i64 handle, i64 idx) -> i64
|
||||
#[no_mangle]
|
||||
@ -744,6 +976,177 @@ pub extern "C" fn nyash_array_get_h(handle: i64, idx: i64) -> i64 {
|
||||
0
|
||||
}
|
||||
|
||||
// ---- ExternCall helpers for LLVM lowering ----
|
||||
// Exported as: nyash.console.log(i8* cstr) -> i64
|
||||
#[export_name = "nyash.console.log"]
|
||||
pub extern "C" fn nyash_console_log_export(ptr: *const i8) -> i64 {
|
||||
if ptr.is_null() { return 0; }
|
||||
unsafe {
|
||||
let c = std::ffi::CStr::from_ptr(ptr);
|
||||
if let Ok(s) = c.to_str() {
|
||||
println!("{}", s);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// Exported as: nyash.console.warn(i8* cstr) -> i64
|
||||
#[export_name = "nyash.console.warn"]
|
||||
pub extern "C" fn nyash_console_warn_export(ptr: *const i8) -> i64 {
|
||||
if ptr.is_null() { return 0; }
|
||||
unsafe {
|
||||
let c = std::ffi::CStr::from_ptr(ptr);
|
||||
if let Ok(s) = c.to_str() {
|
||||
eprintln!("[warn] {}", s);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// Exported as: nyash.console.error(i8* cstr) -> i64
|
||||
#[export_name = "nyash.console.error"]
|
||||
pub extern "C" fn nyash_console_error_export(ptr: *const i8) -> i64 {
|
||||
if ptr.is_null() { return 0; }
|
||||
unsafe {
|
||||
let c = std::ffi::CStr::from_ptr(ptr);
|
||||
if let Ok(s) = c.to_str() {
|
||||
eprintln!("[error] {}", s);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// Exported as: nyash.debug.trace(i8* cstr) -> i64
|
||||
#[export_name = "nyash.debug.trace"]
|
||||
pub extern "C" fn nyash_debug_trace_export(ptr: *const i8) -> i64 {
|
||||
if ptr.is_null() { return 0; }
|
||||
unsafe {
|
||||
let c = std::ffi::CStr::from_ptr(ptr);
|
||||
if let Ok(s) = c.to_str() {
|
||||
eprintln!("[trace] {}", s);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// Exported as: nyash.console.readline() -> i8*
|
||||
#[export_name = "nyash.console.readline"]
|
||||
pub extern "C" fn nyash_console_readline_export() -> *mut i8 {
|
||||
use std::io::{self, Read};
|
||||
// Read a line from stdin; normalize to UTF-8 and strip trailing CR/LF
|
||||
let mut input = String::new();
|
||||
// Use read_to_end if stdin is not a TTY? Simpler: read_line through BufRead
|
||||
// For simplicity, read from stdin into buffer until newline or EOF
|
||||
let mut buf = String::new();
|
||||
let mut handle = io::stdin();
|
||||
// On failure or EOF, return empty string
|
||||
match io::stdin().read_line(&mut buf) {
|
||||
Ok(_n) => { input = buf; },
|
||||
Err(_) => { input.clear(); }
|
||||
}
|
||||
while input.ends_with('\n') || input.ends_with('\r') {
|
||||
input.pop();
|
||||
}
|
||||
// Allocate C string (null-terminated)
|
||||
let mut bytes = input.into_bytes();
|
||||
bytes.push(0);
|
||||
let boxed = bytes.into_boxed_slice();
|
||||
let raw = Box::into_raw(boxed) as *mut u8;
|
||||
raw as *mut i8
|
||||
}
|
||||
|
||||
// ---- String concat helpers for LLVM lowering ----
|
||||
// Exported as: nyash.string.concat_ss(i8* a, i8* b) -> i8*
|
||||
#[export_name = "nyash.string.concat_ss"]
|
||||
pub extern "C" fn nyash_string_concat_ss(a: *const i8, b: *const i8) -> *mut i8 {
|
||||
let mut s = String::new();
|
||||
unsafe {
|
||||
if !a.is_null() {
|
||||
if let Ok(sa) = std::ffi::CStr::from_ptr(a).to_str() { s.push_str(sa); }
|
||||
}
|
||||
if !b.is_null() {
|
||||
if let Ok(sb) = std::ffi::CStr::from_ptr(b).to_str() { s.push_str(sb); }
|
||||
}
|
||||
}
|
||||
let mut bytes = s.into_bytes();
|
||||
bytes.push(0);
|
||||
let boxed = bytes.into_boxed_slice();
|
||||
let raw = Box::into_raw(boxed) as *mut u8;
|
||||
raw as *mut i8
|
||||
}
|
||||
|
||||
// Exported as: nyash.string.concat_si(i8* a, i64 b) -> i8*
|
||||
#[export_name = "nyash.string.concat_si"]
|
||||
pub extern "C" fn nyash_string_concat_si(a: *const i8, b: i64) -> *mut i8 {
|
||||
let mut s = String::new();
|
||||
unsafe {
|
||||
if !a.is_null() {
|
||||
if let Ok(sa) = std::ffi::CStr::from_ptr(a).to_str() { s.push_str(sa); }
|
||||
}
|
||||
}
|
||||
s.push_str(&b.to_string());
|
||||
let mut bytes = s.into_bytes();
|
||||
bytes.push(0);
|
||||
let boxed = bytes.into_boxed_slice();
|
||||
let raw = Box::into_raw(boxed) as *mut u8;
|
||||
raw as *mut i8
|
||||
}
|
||||
|
||||
// Exported as: nyash.string.concat_is(i64 a, i8* b) -> i8*
|
||||
#[export_name = "nyash.string.concat_is"]
|
||||
pub extern "C" fn nyash_string_concat_is(a: i64, b: *const i8) -> *mut i8 {
|
||||
let mut s = a.to_string();
|
||||
unsafe {
|
||||
if !b.is_null() {
|
||||
if let Ok(sb) = std::ffi::CStr::from_ptr(b).to_str() { s.push_str(sb); }
|
||||
}
|
||||
}
|
||||
let mut bytes = s.into_bytes();
|
||||
bytes.push(0);
|
||||
let boxed = bytes.into_boxed_slice();
|
||||
let raw = Box::into_raw(boxed) as *mut u8;
|
||||
raw as *mut i8
|
||||
}
|
||||
|
||||
// ---- Instance field helpers for LLVM lowering (handle-based) ----
|
||||
// Exported as: nyash.instance.get_field_h(i64 handle, i8* name) -> i64
|
||||
#[export_name = "nyash.instance.get_field_h"]
|
||||
pub extern "C" fn nyash_instance_get_field_h(handle: i64, name: *const i8) -> i64 {
|
||||
if handle <= 0 || name.is_null() { return 0; }
|
||||
let name = unsafe { std::ffi::CStr::from_ptr(name) };
|
||||
let Ok(field) = name.to_str() else { return 0 };
|
||||
if let Some(obj) = nyash_rust::jit::rt::handles::get(handle as u64) {
|
||||
if let Some(inst) = obj.as_any().downcast_ref::<nyash_rust::instance_v2::InstanceBox>() {
|
||||
if let Some(shared) = inst.get_field(field) {
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::from(shared);
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// Exported as: nyash.instance.set_field_h(i64 handle, i8* name, i64 val_h) -> i64
|
||||
#[export_name = "nyash.instance.set_field_h"]
|
||||
pub extern "C" fn nyash_instance_set_field_h(handle: i64, name: *const i8, val_h: i64) -> i64 {
|
||||
if handle <= 0 || name.is_null() { return 0; }
|
||||
let name = unsafe { std::ffi::CStr::from_ptr(name) };
|
||||
let Ok(field) = name.to_str() else { return 0 };
|
||||
if let Some(obj) = nyash_rust::jit::rt::handles::get(handle as u64) {
|
||||
if let Some(inst) = obj.as_any().downcast_ref::<nyash_rust::instance_v2::InstanceBox>() {
|
||||
if val_h > 0 {
|
||||
if let Some(val) = nyash_rust::jit::rt::handles::get(val_h as u64) {
|
||||
let shared: nyash_rust::box_trait::SharedNyashBox = std::sync::Arc::clone(&val);
|
||||
let _ = inst.set_field(field, shared);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// Exported as: nyash_array_set_h(i64 handle, i64 idx, i64 val) -> i64
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_array_set_h(handle: i64, idx: i64, val: i64) -> i64 {
|
||||
@ -751,13 +1154,52 @@ pub extern "C" fn nyash_array_set_h(handle: i64, idx: i64, val: i64) -> i64 {
|
||||
if handle <= 0 || idx < 0 { return 0; }
|
||||
if let Some(obj) = handles::get(handle as u64) {
|
||||
if let Some(arr) = obj.as_any().downcast_ref::<nyash_rust::boxes::array::ArrayBox>() {
|
||||
let _ = arr.set(Box::new(IntegerBox::new(idx)), Box::new(IntegerBox::new(val)));
|
||||
let i = idx as usize;
|
||||
let len = arr.len();
|
||||
if i < len {
|
||||
let _ = arr.set(Box::new(IntegerBox::new(idx)), Box::new(IntegerBox::new(val)));
|
||||
} else if i == len {
|
||||
let _ = arr.push(Box::new(IntegerBox::new(val)));
|
||||
} else {
|
||||
// Do nothing for gaps (keep behavior conservative)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// Exported as: nyash_array_push_h(i64 handle, i64 val) -> i64 (returns new length)
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_array_push_h(handle: i64, val: i64) -> i64 {
|
||||
use nyash_rust::{jit::rt::handles, box_trait::{IntegerBox, NyashBox}};
|
||||
if handle <= 0 { return 0; }
|
||||
if let Some(obj) = handles::get(handle as u64) {
|
||||
if let Some(arr) = obj.as_any().downcast_ref::<nyash_rust::boxes::array::ArrayBox>() {
|
||||
// If val is handle, try to use it; otherwise treat as integer
|
||||
let vbox: Box<dyn NyashBox> = if val > 0 {
|
||||
if let Some(o) = handles::get(val as u64) { o.clone_box() } else { Box::new(IntegerBox::new(val)) }
|
||||
} else { Box::new(IntegerBox::new(val)) };
|
||||
let _ = arr.push(vbox);
|
||||
return arr.len() as i64;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// Exported as: nyash_array_length_h(i64 handle) -> i64
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_array_length_h(handle: i64) -> i64 {
|
||||
use nyash_rust::jit::rt::handles;
|
||||
if handle <= 0 { return 0; }
|
||||
if let Some(obj) = handles::get(handle as u64) {
|
||||
if let Some(arr) = obj.as_any().downcast_ref::<nyash_rust::boxes::array::ArrayBox>() {
|
||||
return arr.len() as i64;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// Convert a VM argument (param index or existing handle) into a runtime handle
|
||||
// Exported as: nyash.handle.of
|
||||
#[export_name = "nyash.handle.of"]
|
||||
|
||||
17
create_dummy_ffi.bat
Normal file
17
create_dummy_ffi.bat
Normal file
@ -0,0 +1,17 @@
|
||||
@echo off
|
||||
echo Creating dummy ffi.lib to bypass linker error...
|
||||
cd C:\LLVM-18\lib
|
||||
|
||||
REM Create empty C file
|
||||
echo // Dummy ffi functions > ffi_dummy.c
|
||||
echo void ffi_call() {} >> ffi_dummy.c
|
||||
echo void ffi_prep_cif() {} >> ffi_dummy.c
|
||||
echo void ffi_prep_closure_loc() {} >> ffi_dummy.c
|
||||
|
||||
REM Compile to object file
|
||||
cl /c ffi_dummy.c
|
||||
|
||||
REM Create library
|
||||
lib /OUT:ffi.lib ffi_dummy.obj
|
||||
|
||||
echo Done! Created C:\LLVM-18\lib\ffi.lib
|
||||
17
create_dummy_ffi_ps.ps1
Normal file
17
create_dummy_ffi_ps.ps1
Normal file
@ -0,0 +1,17 @@
|
||||
# Create dummy ffi.lib
|
||||
$dummyContent = @"
|
||||
// Dummy FFI functions for AOT-only build
|
||||
void ffi_call() {}
|
||||
void ffi_prep_cif() {}
|
||||
void ffi_prep_closure_loc() {}
|
||||
void ffi_type_void() {}
|
||||
"@
|
||||
|
||||
# Write C file
|
||||
$dummyContent | Out-File -FilePath "C:\LLVM-18\lib\ffi_dummy.c" -Encoding UTF8
|
||||
|
||||
Write-Host "Created dummy C file at C:\LLVM-18\lib\ffi_dummy.c"
|
||||
Write-Host "Now you need to compile it with Visual Studio Developer Command Prompt:"
|
||||
Write-Host " cd C:\LLVM-18\lib"
|
||||
Write-Host " cl /c ffi_dummy.c"
|
||||
Write-Host " lib /OUT:ffi.lib ffi_dummy.obj"
|
||||
@ -1,10 +1,11 @@
|
||||
# MIR 35→26命令削減: 詳細分析・移行戦略
|
||||
# MIR 35→26→15命令削減: 詳細分析・移行戦略
|
||||
|
||||
*実装ベース完全マッピング - 2025年8月17日版*
|
||||
*実装ベース完全マッピング - 2025年8月17日版*
|
||||
**注意**: このドキュメントは35→26命令への移行計画を記録しています。現在はさらに15命令に削減済みです。
|
||||
|
||||
## 🔍 **現在実装35命令 vs ChatGPT5仕様26命令の完全マッピング**
|
||||
## 🔍 **過去の実装35命令 vs 26命令仕様の完全マッピング(歴史的記録)**
|
||||
|
||||
### **維持する命令 (既存実装 → 26命令仕様)**
|
||||
### **維持する命令 (35命令実装 → 26命令仕様への移行計画)**
|
||||
|
||||
| 現在実装 | 26命令仕様 | 効果 | 変更 |
|
||||
|----------|------------|------|------|
|
||||
|
||||
@ -15,7 +15,7 @@ Purpose: Claude×Copilot×ChatGPT協調開発の総合ロードマップ
|
||||
| Phase | 状態 | 概要 | 詳細リンク |
|
||||
|-------|------|------|------------|
|
||||
| 8.4 | ✅完了 | AST→MIR Lowering完全実装 | [phase_8_4_ast_mir_lowering.md](phase-8/phase_8_4_ast_mir_lowering.md) |
|
||||
| 8.5 | ✅完了 | MIRダイエット(35→26命令) | [phase_8_5_mir_35_to_26_reduction.md](phase-8/phase_8_5_mir_35_to_26_reduction.md) |
|
||||
| 8.5 | ✅完了 | MIRダイエット(35→26→15命令) | [phase_8_5_mir_35_to_26_reduction.md](phase-8/phase_8_5_mir_35_to_26_reduction.md) |
|
||||
| 8.6 | 🔄進行中 | VM性能改善(0.9倍→2倍以上) | [phase_8_6_vm_performance_improvement.md](phase-8/phase_8_6_vm_performance_improvement.md) |
|
||||
| 9 | 📅予定 | JIT実装 | [phase-9/](phase-9/) |
|
||||
| 9.75g-0 | ✅完了 | BID-FFI Plugin System | [Phase-9.75g-0-BID-FFI-Developer-Guide.md](phase-9/Phase-9.75g-0-BID-FFI-Developer-Guide.md) |
|
||||
@ -114,7 +114,7 @@ nyash bid gen --target llvm bid.yaml # AOT用declare生成(LLVM実装時)
|
||||
- **🌟 NEW: GC切り替え可能ランタイム(世界初の柔軟なメモリ管理)**
|
||||
|
||||
**Start Gate(着手前の必須完了)**:
|
||||
- ✅ MIRダイエット(26命令)整合完了
|
||||
- ✅ MIRダイエット(15命令)整合完了
|
||||
- ✅ VM統計: `--vm-stats` でホット関数抽出可能
|
||||
- 🔄 Proof of Concept: MIR→CLIFの最小Lower
|
||||
- ❓ BoxCall/Array/MapのJIT最適化
|
||||
@ -185,7 +185,7 @@ nyash bid gen --target llvm bid.yaml # AOT用declare生成(LLVM実装時)
|
||||
|
||||
### 達成済み
|
||||
- 🏆 RefNew/RefGet/RefSet WASM完全動作
|
||||
- 🏆 26命令MIR階層化完了(Phase 8.5)
|
||||
- 🏆 MIR命令削減完了(35→26→15命令、Phase 8.5)
|
||||
- 🏆 Phase 9.75g-0 BID-FFI Plugin System完全完了
|
||||
- 🏆 警告削減100%達成(Phase 9.75j)
|
||||
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
# CURRENT TASK – Phase 11.7(JIT Complete / Semantics Layer)
|
||||
|
||||
Focus Window: Semantics導入+jit-direct安定化の確定報告 → GC/Sync/非同期のJIT降下着手
|
||||
|
||||
合意事項(要約)
|
||||
|
||||
- LLVM AOT は一旦クローズ。Windows 依存と重量を回避し、Cranelift に集中。
|
||||
- 単一意味論層(Semantics)を導入し、Nyashスクリプト/VM/JIT(exe)を同一動作に揃える。
|
||||
- VM は参照実装。JIT は実行/生成を担い、VM→JITのランタイムフォールバックは行わない。
|
||||
|
||||
現状ステータス(2025-09-01)
|
||||
|
||||
- jit-direct 分岐/PHI 合流:単一出口+BlockParam合流で安定化を確認。
|
||||
- テスト: `mir-branch-ret`, `mir-phi-min`, `mir-branch-multi`, `mir-nested-branch`, `mir-phi-two` で VM/JIT 一致(tag=201/200 一致)。
|
||||
- Semantics 層:`src/semantics/{mod.rs, eval.rs}` にトレイトとPoCインタプリタの骨組みを追加済(未配線)。
|
||||
- C ABI(NyRT):`crates/nyrt` の `libnyrt.a` に必要シンボル実装済(console/array/string/plugin_invoke/checkpoint/gc_barrier 等)。
|
||||
- VM 側:Safepoint/書込バリア/簡易スケジューラ(SingleThread)連携は稼働。
|
||||
- JIT 側:Safepoint/バリア/await はまだスタブまたは未emit(要降下)。
|
||||
|
||||
直近タスク(このフェーズでやること)
|
||||
|
||||
1) Semantics 実用化配線(VM/JITの動作一致の“芯”)
|
||||
- `SemanticsVM`(VM実行での実装)と `SemanticsClif`(LowerCore+IRBuilder委譲)を用意。
|
||||
- `semantics::MirInterpreter` で両者を同一MIRへ適用し、差分検出の土台を作る。
|
||||
2) JIT へ GC/Sync/非同期の降下
|
||||
- Safepoint: `I::Safepoint` を `nyash.rt.checkpoint` emit。`nyrt` 側で `gc.safepoint()` と `scheduler.poll()` に橋渡し。
|
||||
- Write Barrier: Array/Map の set/push 等をlowerする箇所で `nyash.gc.barrier_write` を emit(CountingGc で検証)。
|
||||
- Await: PoC として FutureBox の同期 get にlower(動作一致優先)。
|
||||
3) パリティ検証
|
||||
- `NYASH_GC_COUNTING=1` で VM/JIT ともに safepoint/barrier カウントが増えることを確認。
|
||||
- 既存 smokes(分岐/PHI/配列/外部呼び出し)で一致を継続監視。
|
||||
|
||||
実行メモ
|
||||
|
||||
- Build(JIT): `cargo build --release --features cranelift-jit`
|
||||
- jit-direct: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct <app>`
|
||||
- 追跡: `NYASH_JIT_TRACE_RET/SEL/BLOCKS=1`、GC: `NYASH_GC_COUNTING=1`(必要時)
|
||||
|
||||
備考
|
||||
|
||||
- LLVM AOT のドキュメント/ツールは維持するが、Windows 前提の依存導入は行わない。Cranelift で“がっちり作る”。
|
||||
@ -0,0 +1,46 @@
|
||||
# Design Notes – Phase 11.7 JIT Complete (Meeting Summary)
|
||||
|
||||
Date: 2025-09-01
|
||||
|
||||
Key Decisions
|
||||
|
||||
- Single Semantics Source: Introduce a MIR semantics layer (trait) as the single source of truth. All backends (VM/Cranelift/LLVM/WASM) implement this interface.
|
||||
- No Runtime Fallback: Remove VM→JIT fallback complexity. VM becomes the reference executor; codegen backends handle execution/AOT. JIT is compile-only/AOT‑assist when needed.
|
||||
- Handle‑First ABI: Unify handle/i64/ptr conversions, tag classification, concat/extern/boxcall via shared helpers; call into NyRT shims from backends.
|
||||
- GC Hooks: Insert sync barriers and async safepoints as MIR‑level hooks that each backend lowers appropriately.
|
||||
- Backends Roadmap: LLVM AOT は一旦アーカイブ。Cranelift を主線(JIT/軽量AOT)とし、WASM は同一セマンティクスで後段。Windows DX を軽く保つ。
|
||||
|
||||
Architecture Sketch
|
||||
|
||||
- MIR → Semantics<E>
|
||||
- VmSem: executes values (reference)
|
||||
- ClifSem: builds Cranelift IR (JIT/AOT)
|
||||
- LlvmSem: builds LLVM IR (AOT)
|
||||
- WasmSem: builds Wasm (future)
|
||||
- Shared ABI utilities: handle↔i64/ptr, to_bool, compare, tags, invoke variants, NyRT shims.
|
||||
|
||||
Implementation Plan (Progressive)
|
||||
|
||||
- Phase 1: Skeleton + minimal lowering (Const/Return/Add) → echo-lite returns 0 via Cranelift JIT skeleton.
|
||||
- Phase 2: Control (Jump/Branch/Phi), Load/Store, Compare, String concat via NyRT, Box/Extern by-id (fixed/vector).
|
||||
- Phase 3: GC barriers/safepoints; parity with VM(CountingGc での観測を含む)。
|
||||
- Phase 4: Stability, logs, strict/legacy guards; optional AOT via cranelift-object + link scripts.
|
||||
|
||||
Notes from Review
|
||||
|
||||
- Using Semantics trait enables zero‑cost abstractions with static dispatch.
|
||||
- Add optional debug hooks (location/value) and optimization hints (likely_branch, pure_function_hint) later.
|
||||
- Testing: MockSemantics for unit tests; parity tests VM vs CLIF.
|
||||
|
||||
Action Items
|
||||
|
||||
- Land Semantics trait + minimal MirInterpreter (skeleton added).
|
||||
- Implement ClifSem minimal lowering; wire Runner `--backend cranelift`.
|
||||
- Centralize ABI helpers; migrate existing scattered logic to shared module.
|
||||
- Emit `nyash.rt.checkpoint` and `nyash.gc.barrier_write` from LowerCore at appropriate sites; wire Await (PoC: blocking get).
|
||||
- Add build_cl scripts for AOT when ready.
|
||||
|
||||
Links
|
||||
|
||||
- PLAN.md – milestones and coverage
|
||||
- CURRENT_TASK.md – immediate focus
|
||||
@ -0,0 +1,81 @@
|
||||
# Phase 11.7 – JIT Complete Plan (Cranelift)
|
||||
|
||||
Goal
|
||||
|
||||
- Ship a complete JIT backend (Cranelift) for MIR Core‑15 with Semantics layer as the single source of truth, GC sync/async support, and full Box/Extern integration. Keep DX simple and cross‑platform.
|
||||
|
||||
Milestones
|
||||
|
||||
1) Bootstrap (Infra + Skeleton)
|
||||
- Add backend module: `src/backend/cranelift/{mod.rs,context.rs,lower.rs,jit.rs,object.rs}`
|
||||
- Context: host ISA, FunctionBuilder, Module (JIT/Object) setup helpers
|
||||
- Runner: `--backend cranelift` execution path; feature flag reuse (`cranelift-jit`)
|
||||
- Minimal ops: Const i64/f64/bool/null/string → CLIF values; Return; straight‑line add/sub
|
||||
- Smoke: `apps/tests/ny-echo-lite` returns 0 via JIT
|
||||
|
||||
2) Core MIR‑15 Lowering (Parity with VM)
|
||||
- Control: Jump/Branch/Phi, Load/Store (alloca on entry; i1↔i64 widen/narrow)
|
||||
- Unary/Binary/Compare: int/float/ptr eq/ne; logical and/or via to_bool
|
||||
- TypeOp (when needed) + pointer/int casts (handle→i64/i8* via ABI helpers)
|
||||
- Strings: concat via NyRT shims (`nyash.string.concat_*`)
|
||||
- BoxCall (by‑id): fixed/tagged args (<=4) + vector path; handle‑first returns (i64/ptr)
|
||||
- ExternCall: `env.console.*`, `env.debug.trace`, `console.readLine` via NyRT shims
|
||||
- Smokes: array/map/vinvoke(size/ret), echo; compare outputs with VM
|
||||
|
||||
3) GC Cooperation
|
||||
- Sync barriers: insert read/write barriers at Load/Store & NewBox as per VM semantics
|
||||
- Async safepoints: at call sites/loop backedges; NyRT entry glue to yield if required
|
||||
- Tests: targeted barrier smoke (array/map mutations) + perf sanity (no excessive barriers)
|
||||
|
||||
4) Parity + Stability
|
||||
- UnsupportedLegacyInstruction: maintain strict mode; allow env override for debug
|
||||
- Error reporting: source op pretty‑print, MIR value ids in error messages
|
||||
- Logging: `NYASH_CLI_VERBOSE=1` shows JIT compile stages + object sizes (optional)
|
||||
- Doc/Doctor script: `tools/doctor.ps1`/`.sh` for quick env checks (optional nice‑to‑have)
|
||||
|
||||
5) AOT (Optional within 11.7 if time)
|
||||
- `cranelift-object` emit: `.o` for main; link with NyRT → exe (`tools/build_cl.*`)
|
||||
- Windows: clang/lld link; Linux: cc link; parity with LLVM’s scripts
|
||||
|
||||
Instruction Coverage (MIR Core‑15)
|
||||
|
||||
- Const, UnaryOp, BinOp, Compare, TypeOp
|
||||
- Load, Store, Phi, Jump, Branch, Return
|
||||
- Call, NewBox, BoxCall, ExternCall
|
||||
|
||||
ABI & Shims
|
||||
|
||||
- Handle‑first: receiver/values normalized to i64; ptr/int casts via helpers
|
||||
- NyRT shims used: `nyash.console.*`, `nyash.debug.trace`, `nyash.console.readline`,
|
||||
`nyash_string_new`, `nyash.string.concat_*`, `nyash_array_*_h`, `nyash.instance.*_h`,
|
||||
plugin invoke (by‑id tagged, vector variants)
|
||||
|
||||
Semantics Integration (new in 11.7)
|
||||
|
||||
- Add `Semantics` trait as unified MIR semantics API.
|
||||
- Provide `SemanticsVM` (exec) and `SemanticsClif` (lower) so the same MIR walks yield identical behavior across VM and JIT.
|
||||
- Use `semantics::MirInterpreter` for parity tests; prefer zero‑cost abstractions with static dispatch.
|
||||
|
||||
Status Notes (2025‑09‑01)
|
||||
|
||||
- LLVM AOT: closed for now due to Windows dependency weight; Cranelift is the mainline.
|
||||
- VM: safepoint/barrier/scheduler wired and observable (CountingGc).
|
||||
- JIT: `nyash.rt.checkpoint`/`nyash.gc.barrier_write` symbols are available via NyRT; LowerCore needs to emit safepoints and barriers; Await lowering pending.
|
||||
|
||||
Deliverables
|
||||
|
||||
- Code: Cranelift backend files + runner integration
|
||||
- Tools: build/run scripts for JIT/AOT; updated smokes to exercise CL route
|
||||
- Docs: this plan + CURRENT_TASK.md; brief README how to run JIT
|
||||
|
||||
Risks & Mitigations
|
||||
|
||||
- Pointer/int mismatch → normalize via i64/ptr helpers in one place; add asserts
|
||||
- Barrier placement overhead → start with conservative placement; measure; trim if safe
|
||||
- Windows toolchain variance → Cranelift avoids external LLVM; keep MSVC only
|
||||
|
||||
Timeline (indicative)
|
||||
|
||||
- Week 1: Milestone 1 + 2 (most ops) → basic smokes green
|
||||
- Week 2: GC barriers + safepoints, full parity sweep; docs/tools polish
|
||||
- Optional: AOT via cl‑object emit & link scripts
|
||||
@ -0,0 +1,19 @@
|
||||
# Phase 11.7 – JIT Complete (MIR-15 + Semantics + Sync/Async GC)
|
||||
|
||||
Scope: Implement a production‑ready JIT backend for Nyash that fully supports the MIR Core‑15 instruction set, synchronous/async GC cooperation, and delivers a simple, reproducible developer experience across platforms (WSL/Linux/Windows).
|
||||
|
||||
Why now:
|
||||
- LLVM AOT は一度実装を進めたが、Windows 依存が重くサイズも大きい。Cranelift に回帰して、純Rustで“Just Works”のDXを取り戻し、高速な反復開発を実現する(AOT/LLVMは資料として保持)。
|
||||
|
||||
Outcomes (Definition of Done):
|
||||
- All MIR‑15 ops compile and execute via JIT with behavioral parity to VM.
|
||||
- BoxCall/ExternCall are handled through NyRT shims (handle‑first ABI) safely.
|
||||
- Sync GC barriers in place (read/write), async safepoints wired at call/loop edges.
|
||||
- Smokes: echo/array/map/vinvoke/extern pass; parity checks vs VM/JIT (logs included).
|
||||
- 1‑command setup and run on WSL + Windows Dev PowerShell; no external LLVM needed.
|
||||
|
||||
Backends Strategy:
|
||||
- LLVM AOT はアーカイブ(参照は可)。主線は Cranelift(JIT/軽量AOT)。
|
||||
- JIT 既定は Cranelift(feature: `cranelift-jit`)。AOT は必要に応じ `cranelift-object` を併用。
|
||||
|
||||
This folder contains the living plan (PLAN.md) and the rolling snapshot of the current task focus (CURRENT_TASK.md). Semantics 層の導入により、Nyash スクリプト/VM/JIT(exe)の動作を一致させる。
|
||||
@ -1,115 +1,107 @@
|
||||
# Phase 12: Nyashスクリプトプラグインシステム革命
|
||||
# Phase 12: Nyashコード共有エコシステム - Everything is Box の実現
|
||||
|
||||
## 🚀 概要
|
||||
## 🎯 重要な変更 (2025-09-01)
|
||||
|
||||
Nyashスクリプト自体でプラグインを作成できる革命的発見!ビルド不要で、既存のネイティブプラグインを組み合わせて新機能を作成可能。
|
||||
Phase 12の議論とビルトインBox廃止により、プラグインシステムが進化:
|
||||
|
||||
## 💡 発見の経緯
|
||||
|
||||
include/export仕様の検討中に、以下の重要な気づきが:
|
||||
**新しい3層プラグインシステムが確立されました!**
|
||||
|
||||
```nyash
|
||||
# custom_math_plugin.ny
|
||||
export box CustomMathPlugin {
|
||||
init {
|
||||
_math = new MathBox() # 既存プラグイン活用
|
||||
_cache = new MapBox() # 結果キャッシュ
|
||||
# Nyashスクリプトプラグイン(ユーザー定義Box)
|
||||
box DataProcessor {
|
||||
init {
|
||||
me.file = new FileBox() # C ABIプラグイン使用
|
||||
me.math = new MathBox() # C ABIプラグイン使用
|
||||
me.cache = new MapBox() # これもC ABIプラグイン(ビルトイン廃止)
|
||||
}
|
||||
|
||||
// カスタム拡張
|
||||
cached_sin(x) {
|
||||
local key = x.toString()
|
||||
if me._cache.has(key) {
|
||||
return me._cache.get(key)
|
||||
}
|
||||
local result = me._math.sin(x)
|
||||
me._cache.set(key, result)
|
||||
process(data) {
|
||||
local result = me.math.sin(data)
|
||||
me.file.write("log.txt", result.toString())
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
# 使用例
|
||||
local processor = new DataProcessor()
|
||||
processor.process(3.14) # すべてプラグインで動作!
|
||||
```
|
||||
|
||||
これにより、Rust/C++のビルドなしでプラグイン開発が可能に!
|
||||
## 📝 なぜ誤解が生まれたのか
|
||||
|
||||
## 🎯 統一Box ABI設計
|
||||
「プラグイン」という言葉から、特別な仕組みが必要だと考えてしまいましたが、Nyashの「Everything is Box」哲学により、ユーザー定義Boxこそが最高のプラグインシステムでした。
|
||||
|
||||
### 基本インターフェース
|
||||
詳細な分析:[なぜ天才AIたちは間違えたのか](./WHY-AIS-FAILED.md)
|
||||
|
||||
```rust
|
||||
// Rust側の統一インターフェース
|
||||
trait BoxInterface {
|
||||
fn invoke(&self, method_id: u32, args: NyashValue) -> NyashValue;
|
||||
fn get_methods(&self) -> Vec<MethodInfo>;
|
||||
fn init(&mut self, ctx: Context);
|
||||
fn drop(&mut self);
|
||||
}
|
||||
## 🚀 Phase 12の真の価値:コード共有エコシステム
|
||||
|
||||
### 本当に必要なもの
|
||||
|
||||
1. **export/import構文**
|
||||
```nyash
|
||||
# math_utils.ny
|
||||
export box MathUtils {
|
||||
factorial(n) { ... }
|
||||
fibonacci(n) { ... }
|
||||
}
|
||||
|
||||
# main.ny
|
||||
import { MathUtils } from "math_utils.ny"
|
||||
local utils = new MathUtils()
|
||||
```
|
||||
|
||||
2. **パッケージマネージャー**
|
||||
```bash
|
||||
nyash install awesome-math-utils
|
||||
nyash publish my-cool-box
|
||||
```
|
||||
|
||||
3. **ドキュメント生成**
|
||||
```nyash
|
||||
# @doc 素晴らしい数学ユーティリティ
|
||||
# @param n 計算したい数値
|
||||
# @return 階乗の結果
|
||||
export box MathUtils { ... }
|
||||
```
|
||||
|
||||
## 📊 新しい3層プラグインシステム
|
||||
|
||||
```
|
||||
Nyashエコシステム(ビルトインBox廃止後):
|
||||
├── Nyashスクリプトプラグイン(ユーザー定義Box)← .nyashファイル
|
||||
├── C ABIプラグイン(既存のまま使用)← シンプル・高速・安定
|
||||
└── Nyash ABIプラグイン(必要時のみ)← 言語間相互運用・将来拡張
|
||||
└── MIR命令は増やさない(BoxCallにabi_hint追加のみ)
|
||||
```
|
||||
|
||||
### Nyashスクリプトプラグインの要件
|
||||
### プラグイン選択の指針
|
||||
- **C ABIで済むなら、C ABIを使う**(シンプルイズベスト)
|
||||
- Nyash ABIは以下の場合のみ:
|
||||
- 他言語(Python/Go等)からの呼び出し
|
||||
- 複雑な型の相互運用が必要
|
||||
- 将来の拡張性を重視する場合
|
||||
|
||||
```nyash
|
||||
export box MyPlugin {
|
||||
// 必須:初期化
|
||||
init { ... }
|
||||
|
||||
// 推奨:FFI互換インターフェース
|
||||
invoke(method_id, args) {
|
||||
// method_idに基づいてディスパッチ
|
||||
}
|
||||
|
||||
// オプション:メソッド情報
|
||||
get_methods() {
|
||||
return [
|
||||
{ name: "method1", id: 1 },
|
||||
{ name: "method2", id: 2 }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
## 🛣️ 実装ロードマップ(修正版)
|
||||
|
||||
## 📊 エコシステムへの影響
|
||||
### Phase 12.1: export/import構文(2週間)
|
||||
- [ ] exportキーワードのパーサー実装
|
||||
- [ ] importステートメントの実装
|
||||
- [ ] モジュール解決システム
|
||||
- 📄 **[詳細仕様書](./export-import-spec.md)**
|
||||
|
||||
### 開発の民主化
|
||||
- **参入障壁の劇的低下**: Rust/C++環境不要
|
||||
- **即座の開発**: ビルド待ち時間ゼロ
|
||||
- **コミュニティ拡大**: より多くの開発者が参加可能
|
||||
### Phase 12.2: パッケージ管理(3週間)
|
||||
- [ ] nyash.tomlのdependencies対応
|
||||
- [ ] 中央リポジトリ設計
|
||||
- [ ] CLIツール(install/publish)
|
||||
- 📄 **[パッケージマネージャー設計書](./package-manager-design.md)**
|
||||
|
||||
### 新しい開発パターン
|
||||
1. **プラグインの合成**: 複数のネイティブプラグインを組み合わせ
|
||||
2. **ラピッドプロトタイピング**: アイデアを即座に実装
|
||||
3. **ホットリロード**: 実行中の更新が可能
|
||||
|
||||
## 🛣️ 実装ロードマップ
|
||||
|
||||
### Phase 12.1: 基盤構築
|
||||
- [ ] Box ABI仕様の最終決定
|
||||
- [ ] export box構文のパーサー実装
|
||||
- [ ] 基本的なPluginRegistry実装
|
||||
|
||||
### Phase 12.2: 統一インターフェース
|
||||
- [ ] FFIプラグインのBoxInterface対応
|
||||
- [ ] NyashスクリプトのBoxInterface実装
|
||||
- [ ] 相互運用テスト
|
||||
|
||||
### Phase 12.3: 動的機能
|
||||
- [ ] 動的ロード/アンロード機能
|
||||
- [ ] ホットリロード対応
|
||||
- [ ] プラグイン間依存関係管理
|
||||
|
||||
### Phase 12.4: セキュリティと最適化
|
||||
- [ ] サンドボックス実装
|
||||
- [ ] ケイパビリティベース権限
|
||||
- [ ] パフォーマンス最適化
|
||||
|
||||
## 📚 関連ドキュメント
|
||||
- [Gemini先生の分析](./gemini-analysis-script-plugins.md)
|
||||
- [Codex先生の技術提案](./codex-technical-proposal.md)
|
||||
- [統合分析まとめ](./synthesis-script-plugin-revolution.md)
|
||||
|
||||
## 🎯 次のアクション
|
||||
1. Box ABI仕様書の作成
|
||||
2. export box構文の実装開始
|
||||
3. 既存FFIプラグイン1つを統一インターフェースに移行
|
||||
### Phase 12.3: 開発者体験向上(継続的)
|
||||
- [ ] ドキュメント生成ツール
|
||||
- [ ] VSCode拡張(補完・定義ジャンプ)
|
||||
- [ ] サンプルパッケージ作成
|
||||
|
||||
---
|
||||
*Everything is Box - そしてプラグインもBoxになる!*
|
||||
|
||||
### 🗄️ 議論の過程
|
||||
|
||||
AIたちがなぜ複雑な解決策を提案したのか、その議論の過程は `archive/` ディレクトリに保存されています。良い教訓として残しておきます。
|
||||
|
||||
112
docs/development/roadmap/phases/phase-12/WHY-AIS-FAILED.md
Normal file
112
docs/development/roadmap/phases/phase-12/WHY-AIS-FAILED.md
Normal file
@ -0,0 +1,112 @@
|
||||
# なぜ天才AIたちは間違えたのか - Phase 12の教訓
|
||||
|
||||
## 🤔 根本的な誤解
|
||||
|
||||
### AIたちの誤解
|
||||
```
|
||||
「スクリプトプラグイン」という特別な仕組みが必要
|
||||
→ JIT/AOTから呼び出せない問題がある
|
||||
→ 複雑な解決策が必要
|
||||
```
|
||||
|
||||
### 実際の真実
|
||||
```
|
||||
Nyashスクリプト = 普通のユーザー定義Box
|
||||
→ すでに動いている
|
||||
→ 何も問題ない
|
||||
```
|
||||
|
||||
## 💡 なぜこんな誤解が生まれたのか
|
||||
|
||||
### 1. 「プラグイン」という言葉の罠
|
||||
- AI思考:「プラグイン = 外部DLL/SO = 特別な仕組み」
|
||||
- 実際:「プラグイン = 再利用可能なBox = 普通のユーザー定義Box」
|
||||
|
||||
### 2. 実装レイヤーの混同
|
||||
```
|
||||
AIの頭の中:
|
||||
┌─────────────────────┐
|
||||
│ JIT/AOT (最適化層) │ ← ここに固執
|
||||
├─────────────────────┤
|
||||
│ C ABI プラグイン │
|
||||
├─────────────────────┤
|
||||
│ スクリプトプラグイン │ ← 新しい層を作ろうとした
|
||||
└─────────────────────┘
|
||||
|
||||
実際のNyash:
|
||||
┌─────────────────────┐
|
||||
│ Nyash VM/インタープリター │
|
||||
├─────────────────────┤
|
||||
│ すべてがBox(統一層)│
|
||||
│ - ビルトインBox │
|
||||
│ - C ABIプラグイン │
|
||||
│ - ユーザー定義Box │ ← これだけで十分!
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
### 3. 過度な最適化思考
|
||||
- AI:「JIT/AOTで高速化しなければ!」
|
||||
- 実際:「インタープリター言語として十分高速」
|
||||
|
||||
### 4. Everything is Boxの哲学を忘れた
|
||||
```nyash
|
||||
# これが既に「スクリプトプラグイン」!
|
||||
box DataProcessor {
|
||||
init {
|
||||
me.file = new FileBox() # C ABIプラグイン
|
||||
me.math = new MathBox() # C ABIプラグイン
|
||||
me.cache = new MapBox() # ビルトインBox
|
||||
}
|
||||
|
||||
process(data) {
|
||||
# 全部普通に動く!
|
||||
local result = me.math.sin(data)
|
||||
me.file.write("log.txt", result.toString())
|
||||
return result
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎓 教訓
|
||||
|
||||
### 1. シンプルな解決策を見逃すな
|
||||
- 複雑な問題に見えても、既存の仕組みで解決できることが多い
|
||||
- 「Everything is Box」は強力な統一原理
|
||||
|
||||
### 2. 言葉に惑わされるな
|
||||
- 「プラグイン」→「特別な仕組み」という連想を避ける
|
||||
- 本質を見る:「再利用可能なコード」
|
||||
|
||||
### 3. ユーザー視点を忘れるな
|
||||
```nyash
|
||||
# ユーザーから見れば、これで十分!
|
||||
local processor = new DataProcessor()
|
||||
local result = processor.process(3.14)
|
||||
```
|
||||
|
||||
### 4. AIの弱点
|
||||
- 技術用語から過度に複雑な解釈をしがち
|
||||
- 既存の解決策より新しい仕組みを作りたがる
|
||||
- レイヤーを増やしたがる傾向
|
||||
|
||||
## 📝 Phase 12の本当の価値
|
||||
|
||||
### 実は必要なのは
|
||||
1. **export/import構文**(ファイル間でBoxを共有)
|
||||
2. **パッケージマネージャー**(Boxの配布・インストール)
|
||||
3. **ドキュメント生成**(Boxの使い方を共有)
|
||||
|
||||
### 必要ないもの
|
||||
- 特別な「スクリプトプラグイン」層
|
||||
- JIT/AOT統合
|
||||
- 複雑なトランスパイル
|
||||
|
||||
## 🚀 結論
|
||||
|
||||
**Nyashは最初から正しかった。Everything is Boxだから、すべてが統一的に動く。**
|
||||
|
||||
AIたちは「プラグイン」という言葉に惑わされて、存在しない問題を解決しようとしていた。
|
||||
|
||||
---
|
||||
|
||||
*時に、最も賢い解決策は「何もしないこと」である。*
|
||||
@ -0,0 +1,87 @@
|
||||
# Nyash ABI戦略議論まとめ (2025-09-01)
|
||||
|
||||
## 📋 概要
|
||||
|
||||
Phase 12のプラグインシステム実装において、C ABIとNyash ABIの選択について、AI先生方と深い技術議論を行いました。
|
||||
|
||||
## 🗂️ ドキュメント一覧
|
||||
|
||||
1. **[gemini-abi-analysis.md](gemini-abi-analysis.md)**
|
||||
- Gemini先生の長期エコシステム視点
|
||||
- ABI安定性の重要性
|
||||
- 段階的進化戦略(C ABI → SDK → WASM)
|
||||
|
||||
2. **[codex-abi-implementation.md](codex-abi-implementation.md)**
|
||||
- Codex先生の実装最適化視点
|
||||
- C呼出規約×Nyash値表現の提案
|
||||
- VM/JIT最適化の具体策
|
||||
|
||||
3. **[deep-analysis-synthesis.md](deep-analysis-synthesis.md)**
|
||||
- なぜ正解が難しいのかの深い分析
|
||||
- 時間軸のジレンマ
|
||||
- 「正解がない」ことが答え
|
||||
|
||||
## 🎯 結論:BoxCall拡張による統合案
|
||||
|
||||
### 最終的な実装方針
|
||||
|
||||
```rust
|
||||
// MIRレベル:BoxCallをちょっと拡張
|
||||
MirInstruction::BoxCall {
|
||||
receiver: Value,
|
||||
method: String,
|
||||
args: Vec<Value>,
|
||||
abi_hint: Option<AbiType>, // ← これだけ追加
|
||||
}
|
||||
```
|
||||
|
||||
### 利点
|
||||
|
||||
1. **MIR命令数は15個のまま**(美しさ維持)
|
||||
2. **既存コードは変更不要**(後方互換)
|
||||
3. **プラグインごとにABI選択可能**(段階的移行)
|
||||
4. **Everything is Box哲学の体現**
|
||||
|
||||
### 実装計画
|
||||
|
||||
```yaml
|
||||
Week 1: 基盤整備
|
||||
- PluginABI enum定義
|
||||
- nyash.tomlのabi field追加
|
||||
- NyashValue構造体作成
|
||||
|
||||
Week 2: VM統合
|
||||
- プラグインローダー拡張
|
||||
- VM実行時のABI分岐
|
||||
- pack/unpack実装
|
||||
|
||||
Week 3: 検証
|
||||
- 比較ベンチマーク
|
||||
- ドキュメント作成
|
||||
- 方向性判断
|
||||
```
|
||||
|
||||
## 🔑 重要な洞察
|
||||
|
||||
1. **両ABI共存が現実的**
|
||||
- C ABI:既存資産・高速・安定
|
||||
- Nyash ABI:型安全・拡張性・将来性
|
||||
|
||||
2. **適応的戦略の採用**
|
||||
- 3ヶ月ごとに測定・評価
|
||||
- データに基づく進化
|
||||
|
||||
3. **箱理論による差し替え可能性**
|
||||
- 実装を箱に切り出す
|
||||
- いつでも戻せる安心感
|
||||
|
||||
## 📊 次のステップ
|
||||
|
||||
1. このREADMEを起点に実装開始
|
||||
2. 最小限のプロトタイプ作成
|
||||
3. 性能・開発体験の比較データ収集
|
||||
4. Phase 12本実装への反映
|
||||
|
||||
---
|
||||
|
||||
*「間に挟むだけ」が最も難しい設計判断だった。しかし、BoxCall拡張という自然な解決策にたどり着いた。*
|
||||
@ -0,0 +1,124 @@
|
||||
# Codex先生のNyash ABI実装戦略 (2025-09-01)
|
||||
|
||||
## 質問内容
|
||||
|
||||
Nyashプラグインシステムの ABI戦略について技術的相談です。
|
||||
|
||||
【背景】
|
||||
Phase 12でNyashスクリプトプラグインシステムを実装中です。現在、プラグインインターフェースの設計で重要な判断が必要です。
|
||||
|
||||
【現状】
|
||||
- 既存: C ABI(シンプル、高速、実績あり)
|
||||
- 提案: Nyash ABI(3×u64構造体、型情報付き、拡張性高い)
|
||||
|
||||
【トレードオフ】
|
||||
C ABI:
|
||||
- 利点: ゼロオーバーヘッド、既存資産活用、シンプル
|
||||
- 欠点: 型情報なし、拡張性限定、エラー処理が原始的
|
||||
|
||||
Nyash ABI:
|
||||
- 利点: 型安全、メタデータ活用、将来性(async等)
|
||||
- 欠点: pack/unpackのオーバーヘッド、新規学習コスト
|
||||
|
||||
【具体的な選択肢】
|
||||
1. Nyash ABIのみ(統一性重視)
|
||||
2. C ABI + Nyash ABI両方(実用性重視)
|
||||
3. その他の革新的アプローチ?
|
||||
|
||||
【質問】
|
||||
1. どちらの戦略が長期的に見て賢明でしょうか?
|
||||
2. 両方サポートする場合の複雑性は管理可能でしょうか?
|
||||
3. 他の言語での成功例・失敗例から学べることは?
|
||||
4. VM/JIT最適化の観点から、どちらが有利でしょうか?
|
||||
|
||||
実装の実践的観点から、C ABIとNyash ABIの選択について深く分析してください。特にVM/JIT最適化や実装の複雑性を考慮して。
|
||||
|
||||
## Codex先生の回答
|
||||
|
||||
**結論**
|
||||
- 長期的には「C呼出規約 × Nyash値表現(3×u64)」を中核に、C ABIとNyash ABIの両方をサポートする二層戦略が最も堅実。
|
||||
- まずは二者併存で運用し、JIT/VMでの最適化余地と実用性を確保しながら、将来的にNyash ABIを第一級に育てるのが現実的。
|
||||
|
||||
**推奨戦略**
|
||||
- 両対応(2)を採用。ただし"呼出規約はC、値表現はNyash"を共通土台にする。
|
||||
- 共通の値ABI: `#[repr(C)] struct NyValue { u64 tag; u64 payload; u64 aux; }`
|
||||
- C ABI関数でもNyValueを受け渡しできるため、型安全・メタデータ拡張の道を確保しつつ、Cの安定性・高速性を得る。
|
||||
- プラグイン初期化は「バージョン付き関数テーブル(vtable)+機能フラグ」の交渉型にする。
|
||||
- 例: `NyPluginInitV1(NyRtFnsV1* rt, NyPluginV1* out)` → `NyPluginV1 { u32 abi_mask; u32 size; NyFnDesc* fns; ... }`
|
||||
- `abi_mask`で `NY_ABI_C` と `NY_ABI_NYV1` の両方可否を示す。将来の `NY_ABI_NYV2` も追加可能。
|
||||
- エラーモデルは二段階:
|
||||
- 速経路: `NyStatus`(enum, small int)を戻す。失敗時は`NyValue`でエラーオブジェクトをout-param返却。
|
||||
- 詳細情報は`NyError`ヘルパ(`NyValue`として表現)に格納。FFI境界でpanic禁止・例外横断なし。
|
||||
- メモリ/所有権はハンドル/関数テーブル経由で統一(NyRTのalloc/free、string/slice作成API)。内部構造体への直接依存は不可。
|
||||
|
||||
**複雑性と管理**
|
||||
- 追加コストは主にローダとアダプタ(トランポリン)実装+テスト行列の増加。ただし次の工夫で管理可能:
|
||||
- 機能交渉を一本化(init関数とマニフェスト/nyash.tomlで表明)。ロード時に一度だけ決定。
|
||||
- 呼出アダプタをテンプレート化/JIT時に生成・キャッシュ(型配列→シグネチャ→thunkアドレス)。
|
||||
- 型表(`NyFnDesc`に引数/戻りの型ID配列、純関数/readonly/aliasing属性)を持ち、最適化と検証を兼用。
|
||||
- 見積り:ローダ+vtable+アダプタの骨格は数百行〜千行弱で収まり、以降は関数種別追加が足し算。
|
||||
|
||||
**他言語の事例からの学び**
|
||||
- 成功例
|
||||
- Node N-API: バージョン化された関数テーブルでABIを安定化。実装側の進化と互換の両立に有効。
|
||||
- Lua C API: C呼出規約+独自値表現で長期安定。ただしstackベースは学習コストあり。
|
||||
- 反省点
|
||||
- CPython C API: 内部構造体露出で進化の足枷に。HPyはハンドル化で再設計。
|
||||
- JNI: 汎用だが冗長・低速傾向。最適化しづらい。
|
||||
- 教訓
|
||||
- 内部レイアウト非公開(ハンドル/関数経由)・バージョン化vtable・拡張フラグが長期安定の鍵。
|
||||
- 値ABIは固定サイズ・レジスタ渡し最適化可能な形が吉(3×u64は合理的)。
|
||||
|
||||
**VM/JIT最適化の観点**
|
||||
- 呼出オーバーヘッド
|
||||
- 3×u64はSysV/Win64いずれもレジスタ渡しが効きやすく、C ABIと同等のコールコストに収束。
|
||||
- 本質的ボトルネックは間接呼出ミス予測と型検査。JITで型が既知ならチェックを埋め込み/削減可能。
|
||||
- 直呼 vs トランポリン
|
||||
- 署名が一致する場合は直接呼出(JITに外部シンボルとして登録)。
|
||||
- 型昇降/借用が必要な時のみthunkを噛ませる。JITでモノモーフィック化・デッドパス除去。
|
||||
- 最適化ヒント(Cranelift/LLVM)
|
||||
- 関数属性: `nounwind`, `readonly`/`readnone`, `nocapture`, `nonnull`, `cold`(エラーパス)
|
||||
- 戻り値はout-paramにしてtailcall最適化(`musttail`適用可否は要検証)。
|
||||
- シンボルはAOT時`dso_local`、JITは`CallConv::SystemV/WindowsFast`で3×i64に整合。
|
||||
- ホットループ対策
|
||||
- スカラー跨ぎは避け、スライス/バルクAPIを定義(例: vector add)。boundary跨ぎ回数を削減。
|
||||
- 将来は静的リンクモード(first-party plugins)でLTO許可→AOTでインライン・DCE可。
|
||||
|
||||
**Nyash ABIのみ vs 併存の比較**
|
||||
- Nyash ABIのみ(1)
|
||||
- 一貫性と将来性は高いが、既存C資産/ツール連携コストと立上げ摩擦が大きい。
|
||||
- 併存(2, 推奨)
|
||||
- 早期エコシステム拡大と最適化の両取り。複雑性はvtable+アダプタの設計で抑え込み可能。
|
||||
- その他(3)
|
||||
- N-API風ハンドル専一は安定するが、オーバーヘッド/冗長性が増える。Nyashのパフォーマンス志向には過剰。
|
||||
|
||||
**実装メモ(具体)**
|
||||
- 値ABI
|
||||
- `tag`: 上位ビットに種別・フラグ(own/borrow、small/heap、nullable等)
|
||||
- `payload`: ポインタor即値、`aux`: len/stride/arena_id等
|
||||
- `NySlice`: `tag=SLICE`で`payload=ptr`, `aux=len`。borrow時は生存期間制約、必要なら`nyrt_clone_slice`で所有権取得。
|
||||
- 初期化/交渉
|
||||
- `NyPluginInitV1(NyRtFnsV1*, NyPluginV1*) -> NyStatus`
|
||||
- `NyRtFnsV1`: alloc/free、string/slice/future、log、error造作API。
|
||||
- `NyPluginV1`: `abi_mask`, `size`, `features`, `fn_count`, `NyFnDesc*`.
|
||||
- エラー/非同期
|
||||
- `NyStatus` + `NyValue err_out`。非同期は `NyFuture*`を返却、`NyRtFnsV1`で`poll/wake`を橋渡し。
|
||||
- VMのスケジューラとWakerのブリッジはvtableで隠蔽(ABI非依存)。
|
||||
- 安定性
|
||||
- すべての公開構造体に`size`を先頭フィールドに持たせ将来拡張。未理解フィールドは無視。
|
||||
- ABIバージョンは数値+機能フラグで条件分岐(ビルド時にも`#[cfg(feature)]`で絞る)。
|
||||
|
||||
**質問への回答**
|
||||
- 1) 長期的に賢明か: 併存+共通値ABIが最善。Cの互換性とNyashの型安全/拡張性を両立。
|
||||
- 2) 複雑性は管理可能か: はい。vtableの一本化、JIT生成thunk、機能交渉で抑制できる。
|
||||
- 3) 学び: 内部構造体を外部に晒さない・ハンドル経由・バージョン化テーブル・豊富な属性メタデータ。CPythonの反省とN-APIの成功を取り込む。
|
||||
- 4) VM/JIT最適化: 3×u64のNyash値はレジスタ渡しでC ABIと同等に速く、型既知箇所はJITで直呼/チェック省略可能。最適化の自由度はNyash ABIの方が高い。
|
||||
|
||||
**次のアクション**
|
||||
- 値ABIと関数テーブル仕様を`crates/nyrt/include/nyrt.h`に最小ドラフト化(`NyValue`, `NyStatus`, `NyRtFnsV1`, `NyPluginV1`, `NyFnDesc`)。
|
||||
- ローダにinit/交渉実装(`nyash.toml`のabi種別・型情報読み込み→関数登録)。
|
||||
- JIT/VMに外部関数署名登録とthunk生成を追加(Cranelift/LLVM兼用)。
|
||||
- バルク演算プラグインのスモークを作成(C ABI版とNyash ABI版を比較ベンチ)。
|
||||
- ドキュメント化(`docs/plugins/abi.md`)とサンプル(`plugins/`、`apps/`)追加。
|
||||
|
||||
必要なら、ドラフトの`NyValue`と`NyPluginInitV1`の最小Cヘッダ案もすぐ出します。
|
||||
@ -0,0 +1,188 @@
|
||||
# Codex先生のBoxCall拡張分析 (2025-09-01)
|
||||
|
||||
## 質問内容
|
||||
|
||||
Nyash ABI実装の具体的アプローチについて技術相談です。
|
||||
|
||||
【背景】
|
||||
前回の議論で、C ABIとNyash ABIの両方をサポートする方向性が見えました。実装方法について新しいアイデアがあります。
|
||||
|
||||
【BoxCall拡張による統合案】
|
||||
MIR命令を増やさず、既存のBoxCallを自然に拡張する案です:
|
||||
|
||||
```rust
|
||||
// 現在のBoxCall
|
||||
MirInstruction::BoxCall {
|
||||
receiver: Value,
|
||||
method: String,
|
||||
args: Vec<Value>,
|
||||
}
|
||||
|
||||
// 提案:最小限の拡張
|
||||
MirInstruction::BoxCall {
|
||||
receiver: Value,
|
||||
method: String,
|
||||
args: Vec<Value>,
|
||||
abi_hint: Option<AbiType>, // 追加
|
||||
}
|
||||
```
|
||||
|
||||
【実装の流れ】
|
||||
1. MIRビルダー:プラグインBoxならABIヒント付与
|
||||
2. VM実行時:receiver.type_idから適切なABI選択
|
||||
3. プラグインローダー:nyash.tomlでABI指定可能
|
||||
|
||||
【この案の利点】
|
||||
- MIR命令数は15個維持(Everything is Box哲学)
|
||||
- 既存コード無変更(後方互換)
|
||||
- プラグインごとに段階的移行可能
|
||||
- JITでABI別最適化も可能
|
||||
|
||||
【質問】
|
||||
1. このBoxCall拡張案は設計として健全でしょうか?
|
||||
2. abi_hintは本当に必要?VM実行時の型判定で十分?
|
||||
3. 将来的な拡張(WASM等)にも対応できる設計か?
|
||||
4. パフォーマンス影響をどう評価すべきか?
|
||||
|
||||
実装の観点から、MIR層でのabi_hint追加とVM層でのABI判定について深く分析してください。特に既存実装への影響を最小化する方法を考慮して。
|
||||
|
||||
## Codex先生の回答
|
||||
|
||||
### コードベース分析
|
||||
|
||||
まず、現在のMIR/VM実装を確認:
|
||||
|
||||
```rust
|
||||
// 現在のMirInstruction::BoxCall定義
|
||||
BoxCall {
|
||||
dst: Option<ValueId>,
|
||||
box_val: ValueId,
|
||||
method: String,
|
||||
method_id: Option<u16>, // Universal method用
|
||||
args: Vec<ValueId>,
|
||||
effects: EffectMask,
|
||||
}
|
||||
```
|
||||
|
||||
プラグインローダーはすでにnyash.tomlから型情報を読み込んでおり、VM実行時に利用可能。
|
||||
|
||||
### 核心的な洞察
|
||||
|
||||
**abi_hintは実は不要かもしれない!**
|
||||
|
||||
理由:
|
||||
1. VM実行時にreceiver.type_idから型情報を取得可能
|
||||
2. プラグインローダーが既にABI情報を保持している
|
||||
3. MIR層を変更せずに済む(15命令維持)
|
||||
|
||||
### 実装提案
|
||||
|
||||
#### Option A: 最小限実装(MIR無変更)
|
||||
|
||||
```rust
|
||||
// VM層での実装
|
||||
fn execute_boxcall(&mut self, dst: Option<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> {
|
||||
let receiver = self.get_value(box_val)?;
|
||||
let type_id = receiver.get_type_id();
|
||||
|
||||
// プラグインローダーから型情報取得
|
||||
if let Some(plugin_info) = self.runtime.get_plugin_info(type_id) {
|
||||
match plugin_info.abi.as_str() {
|
||||
"c" => self.call_c_abi_plugin(plugin_info, method, args),
|
||||
"nyash" => self.call_nyash_abi_plugin(plugin_info, method, args),
|
||||
_ => self.call_c_abi_plugin(plugin_info, method, args), // fallback
|
||||
}
|
||||
} else {
|
||||
// ビルトインBox処理(既存のまま)
|
||||
self.execute_builtin_method(receiver, method, args)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Option B: 将来の拡張性を考慮
|
||||
|
||||
```rust
|
||||
// MIRに汎用メタデータ追加(ABIに限定しない)
|
||||
MirInstruction::BoxCall {
|
||||
receiver: Value,
|
||||
method: String,
|
||||
args: Vec<Value>,
|
||||
metadata: Option<u32>, // 汎用的な最適化ヒント
|
||||
}
|
||||
```
|
||||
|
||||
### JIT/AOT最適化の観点
|
||||
|
||||
```rust
|
||||
// JIT時の特化
|
||||
match type_info.abi {
|
||||
Some("c") => {
|
||||
// 直接call命令生成
|
||||
builder.emit_direct_call(symbol, args);
|
||||
}
|
||||
Some("nyash") => {
|
||||
// NyashValue pack/unpackを含めてインライン化
|
||||
let packed = builder.emit_pack_nyash_values(args);
|
||||
builder.emit_call_with_abi(symbol, packed);
|
||||
}
|
||||
_ => {
|
||||
// 汎用パス(VM呼び出し)
|
||||
builder.emit_vm_boxcall(receiver, method, args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### パフォーマンス評価方法
|
||||
|
||||
```rust
|
||||
// ベンチマーク設計
|
||||
fn benchmark_abi_comparison() {
|
||||
// 1. 高頻度呼び出し(math operations)
|
||||
measure_time("C ABI", || {
|
||||
for _ in 0..1_000_000 {
|
||||
math_add_c_abi(1.0, 2.0);
|
||||
}
|
||||
});
|
||||
|
||||
measure_time("Nyash ABI", || {
|
||||
for _ in 0..1_000_000 {
|
||||
math_add_nyash_abi(pack(1.0), pack(2.0));
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 複雑な型(string operations)
|
||||
// 3. プラグイン初期化コスト
|
||||
}
|
||||
```
|
||||
|
||||
### 結論と推奨事項
|
||||
|
||||
1. **abi_hintは不要** - VM実行時の型判定で十分
|
||||
2. **MIR層は無変更** - 15命令の美しさ維持
|
||||
3. **将来拡張は型システム経由** - WASMもプラグイン型として追加
|
||||
4. **段階的実装**:
|
||||
- Week 1: nyash.toml拡張 + VM分岐
|
||||
- Week 2: ベンチマーク + 最適化
|
||||
- Week 3: JIT特化(必要なら)
|
||||
|
||||
### 実装の具体的ステップ
|
||||
|
||||
```yaml
|
||||
Step 1: nyash.toml拡張
|
||||
- LibraryDefinition に abi: Option<String> 追加
|
||||
- デフォルトは "c"
|
||||
|
||||
Step 2: プラグインローダー
|
||||
- PluginInfo 構造体に abi フィールド追加
|
||||
- ロード時に保存
|
||||
|
||||
Step 3: VM execute_boxcall
|
||||
- 型判定でABI分岐
|
||||
- call_c_abi / call_nyash_abi メソッド追加
|
||||
|
||||
Step 4: テスト
|
||||
- 同一機能の C/Nyash 両実装
|
||||
- 性能比較測定
|
||||
```
|
||||
|
||||
この最小限アプローチなら、既存コードへの影響を最小化しつつ、将来の拡張性も確保できます。
|
||||
@ -0,0 +1,164 @@
|
||||
# Nyash ABI戦略の深い分析 - なぜ正解が難しいのか (2025-09-01)
|
||||
|
||||
## 🤔 両先生の回答から見えた根本的な難しさ
|
||||
|
||||
### 1. 時間軸のジレンマ
|
||||
|
||||
**Gemini先生の視点**(長期エコシステム):
|
||||
- 10年後も動くプラグインを作りたい
|
||||
- 破壊的変更は絶対避けたい
|
||||
- 開発者の信頼が最重要
|
||||
|
||||
**Codex先生の視点**(現実的最適化):
|
||||
- 今すぐ高速に動かしたい
|
||||
- JIT/VMの最適化余地を残したい
|
||||
- 実装の複雑性を抑えたい
|
||||
|
||||
→ **この2つは本質的に矛盾する!**
|
||||
|
||||
### 2. 抽象化レベルの選択
|
||||
|
||||
```
|
||||
高レベル抽象化(Gemini案)
|
||||
↑
|
||||
SDK層
|
||||
↑
|
||||
C ABI(安定境界)
|
||||
|
||||
vs
|
||||
|
||||
低レベル統合(Codex案)
|
||||
↓
|
||||
共通値表現(3×u64)
|
||||
↓
|
||||
C呼出規約で統一
|
||||
```
|
||||
|
||||
どちらが正解? **状況による!**
|
||||
|
||||
### 3. 隠れた複雑性の罠
|
||||
|
||||
**表面的には単純に見える選択**:
|
||||
- C ABI only → シンプル!
|
||||
- Nyash ABI only → 統一的!
|
||||
- 両方サポート → 柔軟!
|
||||
|
||||
**実際の複雑さ**:
|
||||
- C ABI only → 型情報なし、拡張困難
|
||||
- Nyash ABI only → 既存資産切り捨て
|
||||
- 両方サポート → **複雑性が2倍...ではなく4倍!**
|
||||
|
||||
### 4. なぜ複雑性が爆発するのか
|
||||
|
||||
```
|
||||
組み合わせ爆発:
|
||||
- 2つのABI ×
|
||||
- 3つのバックエンド(Interpreter/VM/JIT) ×
|
||||
- N個のプラグイン型 ×
|
||||
- M個の最適化レベル
|
||||
= 指数関数的複雑性
|
||||
```
|
||||
|
||||
## 🎯 深い洞察:本当の問題は何か
|
||||
|
||||
### 技術的正解 vs ビジネス的正解
|
||||
|
||||
**技術的に美しい解**:
|
||||
- Nyash ABI一本化
|
||||
- 型安全、拡張可能、統一的
|
||||
|
||||
**ビジネス的に賢い解**:
|
||||
- C ABI + 段階的移行
|
||||
- 既存資産活用、リスク分散
|
||||
|
||||
→ **どちらも「正解」であり「不正解」**
|
||||
|
||||
### 隠れた第3の選択肢
|
||||
|
||||
両先生が暗黙的に示唆している:
|
||||
|
||||
```
|
||||
時期による使い分け:
|
||||
Phase 1: C ABIで素早くエコシステム立ち上げ
|
||||
Phase 2: SDK層で開発体験向上
|
||||
Phase 3: Nyash ABIで技術的優位性確立
|
||||
Phase 4: 統合または選択的廃止
|
||||
```
|
||||
|
||||
## 💡 究極の洞察:「正解がない」ことが答え
|
||||
|
||||
### なぜ正解が難しいのか
|
||||
|
||||
1. **未来は予測不可能**
|
||||
- NyashがRustを超えるか?
|
||||
- WASMが世界標準になるか?
|
||||
- 新しいABI標準が生まれるか?
|
||||
|
||||
2. **トレードオフは価値観次第**
|
||||
- 速度 vs 安全性
|
||||
- シンプル vs 機能性
|
||||
- 互換性 vs 革新性
|
||||
|
||||
3. **成功の定義が人による**
|
||||
- 多くのプラグイン?
|
||||
- 高速な実行?
|
||||
- 美しいコード?
|
||||
|
||||
## 🚀 実践的な答え:適応的戦略
|
||||
|
||||
### 推奨アプローチ
|
||||
|
||||
```rust
|
||||
// 初期実装:両対応だが内部統一
|
||||
enum PluginABI {
|
||||
C(CPlugin), // 既存資産
|
||||
Nyash(NyashPlugin), // 新規開発
|
||||
}
|
||||
|
||||
// 共通インターフェース
|
||||
trait Plugin {
|
||||
fn invoke(&self, args: &[Value]) -> Result<Value>;
|
||||
}
|
||||
|
||||
// 将来の拡張ポイント
|
||||
impl PluginABI {
|
||||
fn optimize_for_jit(&self) -> Option<DirectCall> {
|
||||
// JIT時に最適化可能なら直接呼び出しに変換
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 段階的進化の道筋
|
||||
|
||||
1. **観察期間**(6ヶ月)
|
||||
- 両ABI並行運用
|
||||
- 使用パターン分析
|
||||
- パフォーマンス測定
|
||||
|
||||
2. **最適化期間**(次の6ヶ月)
|
||||
- 頻出パターンの高速化
|
||||
- SDK層の洗練
|
||||
- ドキュメント充実
|
||||
|
||||
3. **判断期間**(1年後)
|
||||
- データに基づく選択
|
||||
- 片方を非推奨に?
|
||||
- それとも永続的共存?
|
||||
|
||||
## 🌟 結論:「正解がない」を受け入れる勇気
|
||||
|
||||
**Nyashの哲学**:
|
||||
- Everything is Box → Everything has Trade-offs
|
||||
- 完璧より進捗(80/20ルール)
|
||||
- 箱理論で差し替え可能に
|
||||
|
||||
**最終提案**:
|
||||
1. 両方実装するが、**内部アーキテクチャは統一**
|
||||
2. **測定可能な成功指標**を先に定義
|
||||
3. **3ヶ月ごとに振り返り**、方向修正
|
||||
|
||||
正解がないからこそ、**適応し続けることが正解**。
|
||||
|
||||
---
|
||||
|
||||
*「間に挟むだけ」のABI層が、実は最も難しい設計判断の一つだった。*
|
||||
@ -0,0 +1,115 @@
|
||||
# BoxCall拡張によるABI戦略 - 最終実装決定 (2025-09-01)
|
||||
|
||||
## 🎯 両先生の回答から得た重要な洞察
|
||||
|
||||
### Gemini先生の誤解が示す重要な点
|
||||
- 先生は「BoxCall = Box<dyn Trait>を渡す」と誤解
|
||||
- しかし、この誤解が**abi_stable**の重要性を教えてくれた
|
||||
- 将来的にRustトレイトオブジェクトを扱う際の指針に
|
||||
|
||||
### Codex先生の実践的分析
|
||||
1. **abi_hintは実は不要かもしれない**
|
||||
- VM実行時にtype_idから判定可能
|
||||
- プラグインローダーが型情報を保持
|
||||
- MIR層を汚さない
|
||||
|
||||
2. **既存実装への影響最小化**
|
||||
- TypeMetaにABI情報を含める
|
||||
- nyash.tomlから読み込み済み
|
||||
- VM側で判定するだけ
|
||||
|
||||
## 🚀 最終実装方針
|
||||
|
||||
### Option A: 最小限実装(推奨)
|
||||
|
||||
```rust
|
||||
// MIR層:変更なし!
|
||||
MirInstruction::BoxCall {
|
||||
receiver: Value,
|
||||
method: String,
|
||||
args: Vec<Value>,
|
||||
// abi_hint不要!
|
||||
}
|
||||
|
||||
// VM層:型判定でABI選択
|
||||
fn execute_boxcall(...) {
|
||||
let type_id = receiver.get_type_id();
|
||||
|
||||
// プラグインローダーから型情報取得
|
||||
if let Some(plugin_info) = get_plugin_info(type_id) {
|
||||
match plugin_info.abi {
|
||||
"c" => call_c_abi(...),
|
||||
"nyash" => call_nyash_abi(...),
|
||||
_ => fallback_to_c_abi(...),
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Option B: 明示的ヒント(将来の拡張性)
|
||||
|
||||
```rust
|
||||
// MIR層:最小限の拡張
|
||||
MirInstruction::BoxCall {
|
||||
receiver: Value,
|
||||
method: String,
|
||||
args: Vec<Value],
|
||||
metadata: Option<u32>, // 汎用メタデータ(ABIヒント含む)
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 比較表
|
||||
|
||||
| 観点 | Option A(最小限) | Option B(ヒント付き) |
|
||||
|------|------------------|---------------------|
|
||||
| MIR変更 | なし ✅ | 最小限 |
|
||||
| 実装工数 | 1週間 | 2週間 |
|
||||
| JIT最適化 | VM時判定 | ビルド時判定可能 |
|
||||
| 将来拡張 | 型情報経由 | メタデータ活用 |
|
||||
|
||||
## 🎯 結論:Option Aで始める
|
||||
|
||||
### 理由
|
||||
1. **80/20ルール**: まず動くものを作る
|
||||
2. **MIR無変更**: 15命令の美しさ維持
|
||||
3. **段階的進化**: 必要になったらOption Bへ
|
||||
|
||||
### 実装ステップ(1週間)
|
||||
|
||||
```yaml
|
||||
Day 1-2: nyash.toml拡張
|
||||
- abi: "c" | "nyash" field追加
|
||||
- プラグインローダー対応
|
||||
|
||||
Day 3-4: VM実行時分岐
|
||||
- execute_boxcall内でABI判定
|
||||
- NyashValue pack/unpack実装
|
||||
|
||||
Day 5-6: テスト・ベンチマーク
|
||||
- 同一機能の2種類プラグイン
|
||||
- 性能比較測定
|
||||
|
||||
Day 7: 判断
|
||||
- データに基づく方向性決定
|
||||
- Option B移行の必要性評価
|
||||
```
|
||||
|
||||
## 💡 深い洞察
|
||||
|
||||
**「abi_hintは不要」という発見が示すもの**:
|
||||
- 型システムが既に十分な情報を持っている
|
||||
- MIRは純粋な意図の表現で良い
|
||||
- 実行時の賢い判断に任せる
|
||||
|
||||
これこそ「Everything is Box」の真髄 - Boxが自分の実装方法を知っている!
|
||||
|
||||
## 🚀 次のアクション
|
||||
|
||||
1. nyash.tomlパーサーに`abi` field追加
|
||||
2. プラグインローダーでABI情報保持
|
||||
3. VM execute_boxcallで分岐実装
|
||||
4. 最小限のテストプラグイン作成
|
||||
|
||||
---
|
||||
|
||||
*最小限から始めて、データで判断する。これがNyashの道。*
|
||||
@ -0,0 +1,179 @@
|
||||
# Gemini先生のNyash ABI戦略分析 (2025-09-01)
|
||||
|
||||
## 質問内容
|
||||
|
||||
Nyashプラグインシステムの ABI戦略について技術的相談です。
|
||||
|
||||
【背景】
|
||||
Phase 12でNyashスクリプトプラグインシステムを実装中です。現在、プラグインインターフェースの設計で重要な判断が必要です。
|
||||
|
||||
【現状】
|
||||
- 既存: C ABI(シンプル、高速、実績あり)
|
||||
- 提案: Nyash ABI(3×u64構造体、型情報付き、拡張性高い)
|
||||
|
||||
【トレードオフ】
|
||||
C ABI:
|
||||
- 利点: ゼロオーバーヘッド、既存資産活用、シンプル
|
||||
- 欠点: 型情報なし、拡張性限定、エラー処理が原始的
|
||||
|
||||
Nyash ABI:
|
||||
- 利点: 型安全、メタデータ活用、将来性(async等)
|
||||
- 欠点: pack/unpackのオーバーヘッド、新規学習コスト
|
||||
|
||||
【具体的な選択肢】
|
||||
1. Nyash ABIのみ(統一性重視)
|
||||
2. C ABI + Nyash ABI両方(実用性重視)
|
||||
3. その他の革新的アプローチ?
|
||||
|
||||
【質問】
|
||||
1. どちらの戦略が長期的に見て賢明でしょうか?
|
||||
2. 両方サポートする場合の複雑性は管理可能でしょうか?
|
||||
3. 他の言語での成功例・失敗例から学べることは?
|
||||
4. VM/JIT最適化の観点から、どちらが有利でしょうか?
|
||||
|
||||
## Gemini先生の回答
|
||||
|
||||
### NyashプラグインシステムにおけるABI戦略の技術的考察
|
||||
|
||||
Nyashプラグインシステムの長期的なエコシステム発展を見据えたABI(Application Binary Interface)戦略について、深く技術的に考察します。
|
||||
|
||||
### 1. ABI安定性の重要性:エコシステム成功の礎
|
||||
|
||||
プラグインシステムの成功は、**ABIの鉄壁の安定性**にかかっています。ホストアプリケーション(Nyash)とプラグインは、別々にコンパイル・配布されるバイナリです。両者が正しく協調動作できるのは、関数呼び出し規約、データ型表現、メモリアロケーションといった低レベルな規約、すなわちABIが厳密に一致しているからです。
|
||||
|
||||
もしNyashのバージョンアップでABIに破壊的変更が生じれば、既存の全プラグインは動作しなくなり、再コンパイルが必要になります。これはエコシステムにとって致命的です。開発者は追従を諦め、ユーザーはプラグインが使えなくなることを恐れてNyashのアップデートをためらうでしょう。
|
||||
|
||||
したがって、ABI戦略の最優先目標は**「前方互換性(新しいNyashが古いプラグインを動かせる)と、可能な限りの後方互換性(古いNyashが新しいプラグインの機能を限定的にでも利用できる)」**を保証することです。
|
||||
|
||||
### 2. 現状の分析と潜在的リスク
|
||||
|
||||
現在のNyashのコードベースを調査し、プラグイン関連の実装を特定する必要があります。`Cargo.toml`やソースコードから、おそらくRustのダイナミックライブラリ(`.so`, `.dll`, `.dylib`)を動的に読み込む形式が採用されていると推測されます。
|
||||
|
||||
ここで最も警戒すべきは、**Rust固有のABIに依存することのリスク**です。Rustコンパイラは、パフォーマンス最適化のために、構造体のメモリレイアウトや関数呼び出し規約をバージョン間で予告なく変更します。したがって、`extern "Rust"`(デフォルト)の関数をプラグイン境界として公開するのは、エコシステムの観点からは**絶対に避けなければなりません**。
|
||||
|
||||
素晴らしい!検索結果から、Nyashはすでに `extern "C"` を広範囲に利用していることが明確にわかります。これは非常に良い出発点です。`plugin_loader_v2.rs` や `bid/plugin_api.rs` などを見ると、プラグインの初期化(`nyash_plugin_init`)や関数呼び出し(`invoke_fn`)のシグネチャがC ABIで定義されています。
|
||||
|
||||
これは、ABI安定性に対する基本的な理解がプロジェクトに既に存在することを示しています。しかし、長期的なエコシステムを築くには、これをさらに推し進め、より洗練された戦略を立てる必要があります。
|
||||
|
||||
### 3. 長期的な発展のためのABI戦略提案
|
||||
|
||||
現在の `extern "C"` ベースのアプローチを基礎としつつ、以下の戦略を段階的に導入することを提案します。
|
||||
|
||||
#### 戦略1:C ABIを「公式な安定境界」として厳格に定義・文書化する
|
||||
|
||||
これは最も重要です。`extern "C"` を使っているというだけでは不十分で、それを**公式な規約**として確立する必要があります。
|
||||
|
||||
**具体的なアクション:**
|
||||
|
||||
1. **ABI仕様書の作成:**
|
||||
- `nyash-abi.h`のようなCヘッダーファイル形式で、プラグインが実装すべき関数シグネチャと、Nyashが提供するホスト関数(コールバック)をすべて定義します。
|
||||
- `invoke_fn` で使われている `*const u8`, `*mut u8` のようなバイトポインタが、具体的にどのようなデータを指すのか(シリアライズ形式、レイアウトなど)を厳密に文書化します。
|
||||
- `NyashHostVtable` のような構造体は、フィールドの順序と型が未来永劫変わらないことを保証しなければなりません。新しいホスト関数を追加する場合は、末尾にのみ追加します。
|
||||
- エラー報告の規約(戻り値の `i32` が何を意味するのか)を定義します。
|
||||
|
||||
2. **FFI(Foreign Function Interface)層の明確化:**
|
||||
- Nyash内部のRustコードと、C ABI境界を明確に分離します。`plugin_loader_v2.rs` はこの役割を担っているようですが、この層は「ABI変換」にのみ責任を持つべきです。内部的なリファクタリングがABIに影響を与えないようにします。
|
||||
|
||||
**利点:**
|
||||
- **言語非依存:** C ABIはあらゆる主要言語(C++, Zig, Go, Python, etc.)から利用可能で、プラグイン開発者の裾野を広げます。
|
||||
- **究極の安定性:** C ABIは数十年にわたり安定しています。
|
||||
|
||||
**欠点:**
|
||||
- **表現力の低さ:** Rustの`Result`や`Option`、ライフタイムといった豊かな型情報を直接渡せず、`i32`のエラーコードやポインタのNULLチェックといったプリミティブな方法に頼らざるを得ません。
|
||||
- **手作業と`unsafe`:** データのシリアライズ・デシリアライズやポインタ操作が必須となり、`unsafe`コードが増え、バグの温床になりやすいです。
|
||||
|
||||
#### 戦略2:高レベルな「Rust向けラッパーSDK」を提供する
|
||||
|
||||
C ABIの欠点を補うため、プラグイン開発者が直接C ABIを触らなくても済むように、公式のRust製SDK(クレート)を提供します。
|
||||
|
||||
**具体的なアクション:**
|
||||
|
||||
1. **`nyash-plugin-sdk` クレートの作成:**
|
||||
- このSDKは、低レベルなC ABIのやり取りをすべてカプセル化します。
|
||||
- プラグイン開発者は、`Result<MyData, MyError>` のような自然なRustの型を扱うだけで、SDKが自動的にC ABI準拠のデータ(バイト列やエラーコード)との相互変換を行います。
|
||||
- 定型的なコードを自動生成するマクロ(例: `#[nyash_plugin]`)を提供し、開発体験を向上させます。
|
||||
|
||||
**SDK利用者のコード(イメージ):**
|
||||
```rust
|
||||
// プラグイン開発者が書くコード
|
||||
use nyash_plugin_sdk::{plugin, NyashResult, NyashError};
|
||||
|
||||
struct MyPlugin;
|
||||
|
||||
#[plugin]
|
||||
impl Plugin for MyPlugin {
|
||||
fn handle_request(&self, input: &str) -> NyashResult<String> {
|
||||
if input == "hello" {
|
||||
Ok("world".to_string())
|
||||
} else {
|
||||
Err(NyashError::new("Invalid input"))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**SDK内部の実装(イメージ):**
|
||||
```rust
|
||||
// #[plugin] マクロが生成するコード
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn nyash_plugin_invoke(
|
||||
/* C ABI引数 */
|
||||
) -> i32 {
|
||||
// 1. C ABIのバイト列から &str へデシリアライズ
|
||||
// 2. MyPlugin::handle_request を呼び出し
|
||||
// 3. 結果の Result<String, NyashError> をC ABIのエラーコードとバイト列にシリアライズ
|
||||
// 4. 結果を返す
|
||||
// ... この部分はSDKが隠蔽する ...
|
||||
}
|
||||
```
|
||||
|
||||
**利点:**
|
||||
- **高い開発者体験:** Rust開発者は、安全で慣用的なRustコードを書くことに集中できます。
|
||||
- **安全性の向上:** `unsafe`コードをSDKに集約させることで、プラグイン開発者がメモリ安全性を破壊するリスクを大幅に低減できます。
|
||||
|
||||
#### 戦略3:データ交換フォーマットの標準化
|
||||
|
||||
`invoke_fn` でやり取りされる `*const u8` の中身を標準化します。これにより、堅牢性と前方互換性が向上します。
|
||||
|
||||
**候補:**
|
||||
|
||||
- **Protocol Buffers / FlatBuffers:**
|
||||
- **利点:** スキーマ定義が明確で、前方・後方互換性を考慮した設計が容易。多言語対応のコード生成ツールが豊富。パフォーマンスも高い。
|
||||
- **欠点:** ビルドプロセスに一手間加わる。
|
||||
|
||||
- **JSON / MessagePack:**
|
||||
- **利点:** 動的で柔軟。デバッグが容易。
|
||||
- **欠点:** スキーマがないため、実行時エラーになりやすい。シリアライズ・デシリアライズのオーバーヘッドが比較的多きい。
|
||||
|
||||
**推奨:**
|
||||
長期的なエコシステムの安定性を考えると、スキーマベースの **Protocol Buffers** または **FlatBuffers** が最適です。
|
||||
|
||||
#### 戦略4:WebAssembly (Wasm) の採用検討
|
||||
|
||||
究極の目標として、プラグインをWebAssemblyモジュールとして実行する方式を検討する価値があります。
|
||||
|
||||
**利点:**
|
||||
- **サンドボックス化:** Wasmランタイム(Wasmtimeなど)上でプラグインを実行することで、Nyash本体から完全に隔離された安全な環境を提供できます。悪意のあるプラグインやバグのあるプラグインがホストをクラッシュさせるのを防ぎます。
|
||||
- **真の言語非依存:** C ABIよりもさらに多くの言語(コンパイルターゲットにWASIを持つもの)がプラグイン開発に利用可能になります。
|
||||
- **WASIとコンポーネントモデル:** WASI(WebAssembly System Interface)やWasm Component Modelの標準化が進むことで、ファイルアクセスやネットワークなどのホスト機能へのアクセスが標準化され、より洗練されたABIを構築できます。
|
||||
|
||||
**欠点:**
|
||||
- **パフォーマンスオーバーヘッド:** C ABIのダイナミックライブラリ呼び出しに比べ、Wasmランタイムを介在させることによるオーバーヘッドが存在します。
|
||||
- **実装の複雑さ:** Wasmランタイムの組み込みと、ホスト・ゲスト間のデータ共有(特に複雑なデータ構造)の実装は複雑になります。
|
||||
|
||||
### まとめと提言
|
||||
|
||||
Nyashは `extern "C"` の採用により、ABI安定化への正しい一歩を踏み出しています。長期的なエコシステムの繁栄のため、以下のロードマップを提案します。
|
||||
|
||||
1. **短期(すぐ着手すべき):**
|
||||
- **C ABIの公式化:** `nyash-abi.h`相当の仕様を定義し、文書化する。
|
||||
- **データフォーマットの選定:** `invoke_fn` で交換されるデータのシリアライズ形式としてProtocol Buffers等を導入する。
|
||||
|
||||
2. **中期(エコシステムの成長期):**
|
||||
- **Rust向けSDKの提供:** `nyash-plugin-sdk` クレートを開発し、Rust開発者の体験を向上させる。
|
||||
- **他言語向けSDKの提供:** 需要に応じて、PythonやGo向けのラッパーライブラリを提供する。
|
||||
|
||||
3. **長期(未来のビジョン):**
|
||||
- **WebAssemblyへの移行:** パフォーマンス要件とセキュリティ要件を天秤にかけ、プラグイン実行環境をWasmに移行することを検討する。これにより、究極の安全性と移植性を獲得できる。
|
||||
|
||||
この戦略により、Nyashは安定性、開発者体験、安全性を段階的に向上させ、活発で持続可能なプラグインエコシステムを構築できると確信します。
|
||||
@ -1,8 +1,17 @@
|
||||
# 🚨 重大な設計問題:スクリプトプラグインとMIR/EXEの非互換性
|
||||
# 🚨 AI議論の誤解:存在しない問題を解決しようとした記録
|
||||
|
||||
## 問題の本質
|
||||
## 誤解の本質
|
||||
|
||||
**MIR/JIT/AOT(EXE)は、C ABIの関数しか呼び出せない。**
|
||||
**この文書は、AIたちが「スクリプトプラグイン」という存在しない問題を作り出し、複雑な解決策を提案した記録です。**
|
||||
|
||||
### 実際の事実
|
||||
- Nyashスクリプト = 普通のユーザー定義Box
|
||||
- ユーザー定義Boxは既に完璧に動作している
|
||||
- 特別な仕組みは一切不要
|
||||
|
||||
## なぜこんな誤解をしたのか
|
||||
|
||||
**「プラグイン」という言葉に引っ張られて、C ABIの外部DLLのような特別な仕組みが必要だと思い込んでしまった。**
|
||||
|
||||
```
|
||||
現実のフロー:
|
||||
@ -0,0 +1,48 @@
|
||||
# Phase 12 Archive - AIたちの誤解の記録
|
||||
|
||||
## 🚨 重要:このフォルダの内容について
|
||||
|
||||
このフォルダには、Phase 12の議論で生まれた**誤解に基づいたドキュメント**が保存されています。
|
||||
|
||||
### なぜ誤解が生まれたのか
|
||||
|
||||
AIたちは「プラグイン」という言葉から、以下のような複雑な仕組みが必要だと考えました:
|
||||
- トランスパイル(Nyash→C)
|
||||
- 埋め込みVM
|
||||
- 特別なABI変換
|
||||
- JIT/AOT統合
|
||||
|
||||
### 実際の真実
|
||||
|
||||
**Nyashスクリプト = 普通のユーザー定義Box**
|
||||
|
||||
```nyash
|
||||
# これで十分!特別な仕組みは不要
|
||||
box MyPlugin {
|
||||
init {
|
||||
me.file = new FileBox() # C ABIプラグイン使える
|
||||
}
|
||||
process(data) {
|
||||
return me.file.read(data)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 教訓として
|
||||
|
||||
これらのドキュメントは、以下の教訓を示すために残しています:
|
||||
1. シンプルな解決策を見逃してはいけない
|
||||
2. 技術用語に惑わされてはいけない
|
||||
3. Everything is Boxの哲学を忘れてはいけない
|
||||
|
||||
## 📁 アーカイブ内容
|
||||
|
||||
- `CRITICAL-ISSUE.md` - 存在しない問題を解決しようとした記録
|
||||
- `01_roadmap_final.md` - 不要なトランスパイル実装計画
|
||||
- `02_spec_embedded_vm.md` - 不要な埋め込みVM仕様
|
||||
- `03_spec_box_arguments.md` - 不要なBox受け渡し仕様
|
||||
- その他、AI会議での誤解に基づく提案
|
||||
|
||||
---
|
||||
|
||||
*「時に、最も賢い解決策は何もしないことである」*
|
||||
230
docs/development/roadmap/phases/phase-12/archive/README.md
Normal file
230
docs/development/roadmap/phases/phase-12/archive/README.md
Normal file
@ -0,0 +1,230 @@
|
||||
# Phase 12: プラグインシステムの進化 - 既存C ABIとの共存戦略
|
||||
|
||||
## 🚀 概要
|
||||
|
||||
**重要**: 既存のC ABIプラグインはそのまま使い続けられます!その上で、以下の2つの新機能を追加:
|
||||
|
||||
1. **Nyash ABIサポート** - より型安全で拡張性の高い新しいABI(オプション)
|
||||
2. **Nyashスクリプトプラグイン** - ビルド不要でプラグイン作成可能
|
||||
|
||||
### なぜ既存C ABIを残すのか?
|
||||
|
||||
- **実績と安定性**: 現在動いているFileBox、NetBox等はそのまま
|
||||
- **ゼロオーバーヘッド**: 高頻度呼び出しには最速のC ABI
|
||||
- **段階的移行**: 必要に応じて選択的にNyash ABIへ移行可能
|
||||
|
||||
## 💡 発見の経緯
|
||||
|
||||
include/export仕様の検討中に、以下の重要な気づきが:
|
||||
|
||||
```nyash
|
||||
# custom_math_plugin.ny
|
||||
export box CustomMathPlugin {
|
||||
init {
|
||||
_math = new MathBox() # 既存プラグイン活用
|
||||
_cache = new MapBox() # 結果キャッシュ
|
||||
}
|
||||
|
||||
// カスタム拡張
|
||||
cached_sin(x) {
|
||||
local key = x.toString()
|
||||
if me._cache.has(key) {
|
||||
return me._cache.get(key)
|
||||
}
|
||||
local result = me._math.sin(x)
|
||||
me._cache.set(key, result)
|
||||
return result
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
これにより、Rust/C++のビルドなしでプラグイン開発が可能に!
|
||||
|
||||
## 🎯 ABI共存戦略 - 適材適所の選択
|
||||
|
||||
### 三層構造のプラグインエコシステム
|
||||
|
||||
```
|
||||
1. C ABIプラグイン(既存・継続)
|
||||
- FileBox, NetBox, MathBox等
|
||||
- 高速・安定・実績あり
|
||||
- 変更なし!そのまま使える
|
||||
|
||||
2. Nyash ABIプラグイン(新規追加)
|
||||
- 型安全・拡張性重視の新規開発向け
|
||||
- async/await対応などの将来機能
|
||||
|
||||
3. Nyashスクリプトプラグイン(革命的)
|
||||
- ビルド不要・即座に開発
|
||||
- 上記1,2を組み合わせて利用可能
|
||||
```
|
||||
|
||||
### 使い分けの指針
|
||||
|
||||
| 用途 | 推奨ABI | 理由 |
|
||||
|------|---------|------|
|
||||
| 数値計算(高頻度) | C ABI | ゼロオーバーヘッド |
|
||||
| ファイルI/O | C ABI | 既存実装が安定 |
|
||||
| 複雑な型操作 | Nyash ABI | 型安全性重視 |
|
||||
| プロトタイプ | Nyashスクリプト | 即座に試せる |
|
||||
|
||||
### 設定例(nyash.toml v2.1)
|
||||
|
||||
```toml
|
||||
# nyash.toml v2.1
|
||||
[plugin.math]
|
||||
path = "plugins/math.so"
|
||||
abi = "c" # 高速・安定(デフォルト)
|
||||
|
||||
[plugin.advanced_math]
|
||||
path = "plugins/advanced_math.so"
|
||||
abi = "nyash" # 型安全・拡張性
|
||||
```
|
||||
|
||||
### BoxCall拡張による実装
|
||||
|
||||
**重要な発見**:MIR層の変更は不要!VM実行時の型判定で十分。
|
||||
|
||||
```rust
|
||||
// MIR層:変更なし(15命令維持)
|
||||
MirInstruction::BoxCall {
|
||||
receiver: Value,
|
||||
method: String,
|
||||
args: Vec<Value>,
|
||||
}
|
||||
|
||||
// VM層:賢い判定
|
||||
fn execute_boxcall(...) {
|
||||
let type_info = get_plugin_info(receiver.type_id);
|
||||
match type_info.abi {
|
||||
"c" => call_c_abi(...), // 既存プラグイン
|
||||
"nyash" => call_nyash_abi(...), // 新プラグイン
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Nyash ABI仕様
|
||||
|
||||
```c
|
||||
// 3×u64構造体による型安全な値渡し
|
||||
typedef struct NyashValue {
|
||||
uint64_t type_id; // 型識別子
|
||||
uint64_t box_handle; // Boxハンドル
|
||||
uint64_t metadata; // 拡張用(async flag等)
|
||||
} NyashValue;
|
||||
|
||||
// 統一関数シグネチャ
|
||||
typedef NyashValue (*NyashFunc)(
|
||||
uint32_t argc,
|
||||
NyashValue* args,
|
||||
void* context
|
||||
);
|
||||
```
|
||||
|
||||
### 基本インターフェース(内部)
|
||||
|
||||
```rust
|
||||
// Rust側の統一インターフェース
|
||||
trait BoxInterface {
|
||||
fn invoke(&self, method_id: u32, args: NyashValue) -> NyashValue;
|
||||
fn get_methods(&self) -> Vec<MethodInfo>;
|
||||
fn init(&mut self, ctx: Context);
|
||||
fn drop(&mut self);
|
||||
}
|
||||
```
|
||||
|
||||
### Nyashスクリプトプラグインの要件
|
||||
|
||||
```nyash
|
||||
export box MyPlugin {
|
||||
// 必須:初期化
|
||||
init { ... }
|
||||
|
||||
// 推奨:FFI互換インターフェース
|
||||
invoke(method_id, args) {
|
||||
// method_idに基づいてディスパッチ
|
||||
}
|
||||
|
||||
// オプション:メソッド情報
|
||||
get_methods() {
|
||||
return [
|
||||
{ name: "method1", id: 1 },
|
||||
{ name: "method2", id: 2 }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 エコシステムへの影響
|
||||
|
||||
### 開発の民主化
|
||||
- **参入障壁の劇的低下**: Rust/C++環境不要
|
||||
- **即座の開発**: ビルド待ち時間ゼロ
|
||||
- **コミュニティ拡大**: より多くの開発者が参加可能
|
||||
|
||||
### 新しい開発パターン
|
||||
1. **プラグインの合成**: 複数のネイティブプラグインを組み合わせ
|
||||
2. **ラピッドプロトタイピング**: アイデアを即座に実装
|
||||
3. **ホットリロード**: 実行中の更新が可能
|
||||
|
||||
## 🛣️ 実装ロードマップ(2025-09-01更新)
|
||||
|
||||
**重要**: 既存のC ABIプラグインは一切変更不要!追加機能として実装します。
|
||||
|
||||
### Phase 12.1: ABI共存基盤(1週間)
|
||||
- [ ] nyash.toml v2.1仕様(abi fieldサポート、デフォルト="c")
|
||||
- [ ] プラグインローダーのABI判定実装(後方互換性保証)
|
||||
- [ ] VM execute_boxcallのABI分岐追加(C ABIは現状維持)
|
||||
- [ ] 同一機能のC ABI / Nyash ABI比較ベンチマーク
|
||||
|
||||
### Phase 12.2: Nyash ABI基盤(2週間)
|
||||
- [ ] NyashValue構造体定義(crates/nyrt/include/nyash_abi.h)
|
||||
- [ ] pack/unpack関数実装
|
||||
- [ ] 既存プラグイン1つをNyash ABI移行(実証実験)
|
||||
- [ ] JIT最適化(型既知時の特化コード生成)
|
||||
|
||||
### Phase 12.3: スクリプトプラグイン対応(3週間)
|
||||
- [ ] export box構文のパーサー実装
|
||||
- [ ] BoxInterface trait実装
|
||||
- [ ] NyashスクリプトのVM内実行環境
|
||||
- [ ] 相互運用テスト(C/Nyash/Script混在)
|
||||
|
||||
### Phase 12.4: 動的機能とエコシステム(継続的)
|
||||
- [ ] ホットリロード対応
|
||||
- [ ] プラグイン間依存関係管理
|
||||
- [ ] プラグインマーケットプレイス構想
|
||||
- [ ] セキュリティサンドボックス
|
||||
|
||||
### 実装優先順位(短期)
|
||||
1. **Week 1**: nyash.tomlのabi field + VM分岐(動作確認)
|
||||
2. **Week 2**: 性能測定 + 方向性判断
|
||||
3. **Week 3**: 本格実装 or 方針転換
|
||||
|
||||
## 📚 関連ドキュメント
|
||||
|
||||
### 初期設計
|
||||
- [Gemini先生の分析](./gemini-analysis-script-plugins.md)
|
||||
- [Codex先生の技術提案](./codex-technical-proposal.md)
|
||||
- [統合分析まとめ](./synthesis-script-plugin-revolution.md)
|
||||
|
||||
### ABI戦略議論(2025-09-01)
|
||||
- [議論まとめ](./abi-strategy-discussion/README.md)
|
||||
- [Gemini先生のABI分析](./abi-strategy-discussion/gemini-abi-analysis.md)
|
||||
- [Codex先生のABI実装戦略](./abi-strategy-discussion/codex-abi-implementation.md)
|
||||
- [Codex先生のBoxCall拡張分析](./abi-strategy-discussion/codex-boxcall-extension-analysis.md)
|
||||
- [深い分析と結論](./abi-strategy-discussion/deep-analysis-synthesis.md)
|
||||
- [最終実装決定](./abi-strategy-discussion/final-implementation-decision.md)
|
||||
|
||||
## 🎯 次のアクション(優先順位順)
|
||||
1. nyash.toml v2.1パーサーに`abi` field追加(Day 1-2)
|
||||
2. VM execute_boxcallでABI判定分岐実装(Day 3-4)
|
||||
3. SimpleMathプラグインで両ABI比較テスト(Day 5-6)
|
||||
4. ベンチマーク結果に基づく方向性判断(Day 7)
|
||||
|
||||
### 将来的なアクション
|
||||
- Nyash ABI仕様書(nyash_abi.h)作成
|
||||
- export box構文の実装
|
||||
- プラグインSDK(#[nyash_plugin]マクロ)開発
|
||||
|
||||
---
|
||||
*Everything is Box - そしてプラグインもBoxになる!*
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user