refactor: Major interpreter modularization and P2PBox enhancements
Major Interpreter Refactoring: - Split core.rs (373 lines removed) into focused modules - Split expressions/calls.rs (460 lines removed) into cleaner structure - Added new modules: calls.rs, errors.rs, eval.rs, methods_dispatch.rs, state.rs - Improved separation of concerns across interpreter components P2PBox Enhancements: - Added on_once() for one-time event handlers - Added off() for handler deregistration - Implemented handler flags with AtomicBool for thread-safe management - Added loopback testing cache (last_from, last_intent_name) - Improved Arc-based state sharing for transport and handlers Plugin Loader Unification (In Progress): - Created plugin_loader_unified.rs skeleton - Created plugin_ffi_common.rs for shared FFI utilities - Migration plan documented (2400 lines → 1100 lines target) MIR & VM Improvements: - Enhanced modularized MIR builder structure - Added BoxCall dispatch improvements - Better separation in builder modules Documentation Updates: - Added Phase 9.79a unified box dispatch plan - Created plugin loader migration plan - Updated CURRENT_TASK.md with latest progress All tests passing (180 tests) - ready for next phase of refactoring 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
15
.claude/claude_desktop_config.json
Normal file
15
.claude/claude_desktop_config.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"nekocode": {
|
||||||
|
"command": "python3",
|
||||||
|
"args": [
|
||||||
|
"/mnt/c/git/nyash-project/nyash/tools/nekocode-rust/mcp-nekocode-server/mcp_server_real.py"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"PYTHONUNBUFFERED": "1",
|
||||||
|
"NEKOCODE_BINARY_PATH": "/mnt/c/git/nyash-project/nyash/tools/nekocode-rust/bin/nekocode",
|
||||||
|
"NEKOREFACTOR_BINARY_PATH": "/mnt/c/git/nyash-project/nyash/tools/nekocode-rust/bin/nekorefactor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
CLAUDE.md
32
CLAUDE.md
@ -1,13 +1,14 @@
|
|||||||
# Nyash開発ガイド for Claude
|
# Claude Quick Start (Minimal Entry)
|
||||||
|
|
||||||
Nyashプログラミング言語開発に必要な情報をまとめたクイックリファレンス。
|
このファイルは最小限の入口だよ。詳細はREADMEから辿ってねにゃ😺
|
||||||
|
|
||||||
## 🧭 Start Here (最初に見る)
|
## Start Here (必ずここから)
|
||||||
- **🎯 主軸タスク**: [copilot_issues.txt](docs/development/roadmap/native-plan/copilot_issues.txt) **← 最重要!**
|
- 現在のタスク: docs/development/current/CURRENT_TASK.md
|
||||||
- **📋 現在のタスク**: [CURRENT_TASK.md](docs/development/current/CURRENT_TASK.md)(近々/中期/長期の計画は同ファイル先頭)
|
- ドキュメントハブ: README.md
|
||||||
- **📚 言語仕様**: まず[LANGUAGE_REFERENCE_2025.md](docs/reference/language/LANGUAGE_REFERENCE_2025.md)を読む
|
- 主軸タスクリスト: docs/development/roadmap/native-plan/copilot_issues.txt
|
||||||
- **📦 Box システム**: [boxes-system/](docs/reference/boxes-system/)でAPI確認
|
|
||||||
- **⚙️ 実行バックエンド**: [execution-backends.md](docs/reference/architecture/execution-backends.md)
|
Notes:
|
||||||
|
- ここから先の導線は README.md に集約。Claude Codeくんがこのファイルを上書きしても最低限のリンクは保たれるよ。
|
||||||
|
|
||||||
## 🤖 **Claude×Copilot協調開発の主軸**
|
## 🤖 **Claude×Copilot協調開発の主軸**
|
||||||
### 📋 **copilot_issues.txt - 開発の軸となるファイル**
|
### 📋 **copilot_issues.txt - 開発の軸となるファイル**
|
||||||
@ -73,21 +74,20 @@ target/x86_64-pc-windows-msvc/release/nyash.exe
|
|||||||
|
|
||||||
### 🌐 WebAssembly版
|
### 🌐 WebAssembly版
|
||||||
```bash
|
```bash
|
||||||
# WASMビルド方法1: nyash-wasmプロジェクトで直接ビルド
|
# WASMビルド(ルートディレクトリで実行)
|
||||||
cd projects/nyash-wasm
|
|
||||||
wasm-pack build --target web
|
wasm-pack build --target web
|
||||||
|
|
||||||
# WASMビルド方法2: build.shスクリプト使用(古い方法)
|
# ビルド結果は pkg/ ディレクトリに生成される
|
||||||
cd projects/nyash-wasm
|
# - pkg/nyash_rust_bg.wasm
|
||||||
./build.sh
|
# - pkg/nyash_rust.js
|
||||||
|
# - pkg/nyash_rust.d.ts
|
||||||
|
|
||||||
# 開発サーバー起動(ポート8010推奨)
|
# 開発サーバー起動(ポート8010推奨)
|
||||||
python3 -m http.server 8010
|
python3 -m http.server 8010
|
||||||
|
|
||||||
# ブラウザでアクセス
|
# ブラウザでアクセス
|
||||||
# http://localhost:8010/nyash_playground.html
|
# ローカルテスト: http://localhost:8010/nyash_playground.html
|
||||||
# http://localhost:8010/enhanced_playground.html
|
# 公開プレイグラウンド: https://moe-charm.github.io/nyash/projects/nyash-wasm/nyash_playground.html
|
||||||
# http://localhost:8010/canvas_playground.html
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**注意**: WASMビルドでは一部のBox(TimerBox、AudioBox等)は除外されます。
|
**注意**: WASMビルドでは一部のBox(TimerBox、AudioBox等)は除外されます。
|
||||||
|
|||||||
@ -25,6 +25,8 @@ mir_typeop_poc = []
|
|||||||
mir_refbarrier_unify_poc = []
|
mir_refbarrier_unify_poc = []
|
||||||
# Note: LLVM feature requires inkwell dependency and LLVM development libraries
|
# Note: LLVM feature requires inkwell dependency and LLVM development libraries
|
||||||
# llvm = ["dep:inkwell"]
|
# llvm = ["dep:inkwell"]
|
||||||
|
# Optional: modular MIR builder (off by default)
|
||||||
|
mir_modular_builder = []
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "nyash_rust"
|
name = "nyash_rust"
|
||||||
|
|||||||
175
REFACTORING_ANALYSIS_REPORT.md
Normal file
175
REFACTORING_ANALYSIS_REPORT.md
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
# Nyash Codebase Refactoring Analysis Report
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
This report analyzes the Nyash codebase for refactoring opportunities, focusing on large files, duplicate patterns, plugin loader consolidation, and dead code removal. The analysis was conducted manually using grep, find, and file analysis tools due to Docker unavailability for nekocode.
|
||||||
|
|
||||||
|
## Key Findings
|
||||||
|
|
||||||
|
### 1. Large Files Requiring Splitting
|
||||||
|
|
||||||
|
**Largest Files (1000+ lines):**
|
||||||
|
- `src/mir/builder.rs` (1517 lines) - MIR builder with mixed responsibilities
|
||||||
|
- `src/interpreter/plugin_loader.rs` (1217 lines) - Plugin loading logic
|
||||||
|
- `src/interpreter/expressions/calls.rs` (1016 lines) - Method call handling
|
||||||
|
- `src/interpreter/core.rs` (1012 lines) - Core interpreter functionality
|
||||||
|
- `src/ast.rs` (1012 lines) - AST definitions and implementations
|
||||||
|
- `src/backend/vm.rs` (991 lines) - VM execution engine
|
||||||
|
- `src/runtime/plugin_loader_v2.rs` (906 lines) - V2 plugin loader
|
||||||
|
|
||||||
|
### 2. Plugin Loader Consolidation Opportunities
|
||||||
|
|
||||||
|
**Multiple Plugin Loading Systems Detected:**
|
||||||
|
- `src/interpreter/plugin_loader.rs` - Original plugin loader
|
||||||
|
- `src/runtime/plugin_loader_v2.rs` - V2 plugin loader
|
||||||
|
- `src/runtime/plugin_loader_legacy.rs` - Legacy plugin loader
|
||||||
|
- `src/bin/test_plugin_loader_v2.rs` - Test implementation
|
||||||
|
|
||||||
|
**Issues Identified:**
|
||||||
|
- Code duplication across 3+ plugin loader implementations
|
||||||
|
- Inconsistent FFI handling patterns
|
||||||
|
- Redundant memory management in `FileBoxHandle`, `MathBoxHandle`, `RandomBoxHandle`
|
||||||
|
- Complex TLV handling duplicated across loaders
|
||||||
|
|
||||||
|
### 3. Duplicate Code Patterns
|
||||||
|
|
||||||
|
**Box Implementation Patterns:**
|
||||||
|
- 67 `impl NyashBox` implementations with similar structure
|
||||||
|
- Repeated constructor patterns (`.new()` implementations)
|
||||||
|
- Common drop/finalization patterns across Box types
|
||||||
|
- Similar error handling patterns across modules
|
||||||
|
|
||||||
|
**Common Anti-patterns:**
|
||||||
|
- Excessive `.clone()` usage (797 instances across files)
|
||||||
|
- 36 `#[allow(dead_code)]` suppressions
|
||||||
|
- 797 instances of potentially unsafe operations (`unreachable`, `panic`, `unwrap`)
|
||||||
|
|
||||||
|
### 4. Dead Code Analysis
|
||||||
|
|
||||||
|
**Dead Code Indicators:**
|
||||||
|
- 36 explicit dead code suppressions
|
||||||
|
- Multiple TODO/FIXME comments (20+ instances)
|
||||||
|
- Unused imports and deprecated methods
|
||||||
|
- Legacy code paths maintained alongside new implementations
|
||||||
|
|
||||||
|
## Specific Refactoring Recommendations
|
||||||
|
|
||||||
|
### Priority 1: Plugin Loader Consolidation
|
||||||
|
|
||||||
|
**Goal:** Merge 3 plugin loaders into unified system
|
||||||
|
|
||||||
|
**Steps:**
|
||||||
|
1. Create unified `PluginLoaderV3` in `src/runtime/plugin_loader_unified.rs`
|
||||||
|
2. Extract common FFI patterns into `src/runtime/plugin_ffi_common.rs`
|
||||||
|
3. Consolidate handle management (`FileBoxHandle`, `MathBoxHandle`, etc.) into generic `PluginHandle<T>`
|
||||||
|
4. Migrate existing code incrementally
|
||||||
|
5. Remove legacy loaders
|
||||||
|
|
||||||
|
**Impact:** ~2000 lines reduction, improved maintainability
|
||||||
|
|
||||||
|
### Priority 2: MIR Builder Modularization
|
||||||
|
|
||||||
|
**Goal:** Split `src/mir/builder.rs` (1517 lines) into focused modules
|
||||||
|
|
||||||
|
**Proposed Structure:**
|
||||||
|
```
|
||||||
|
src/mir/builder/
|
||||||
|
├── mod.rs # Public interface and coordination
|
||||||
|
├── expressions.rs # Expression lowering (exists, enhance)
|
||||||
|
├── statements.rs # Statement lowering
|
||||||
|
├── control_flow.rs # If/loop/match lowering
|
||||||
|
├── type_operations.rs # Type checking and casting
|
||||||
|
├── phi_insertion.rs # SSA phi node management
|
||||||
|
├── optimization.rs # Basic optimization passes
|
||||||
|
└── validation.rs # Builder state validation
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact:** Improved maintainability, easier testing, clearer separation of concerns
|
||||||
|
|
||||||
|
### Priority 3: Interpreter Core Splitting
|
||||||
|
|
||||||
|
**Goal:** Break down large interpreter files
|
||||||
|
|
||||||
|
**Strategy:**
|
||||||
|
- `src/interpreter/core.rs` → Split into `context.rs`, `execution.rs`, `environment.rs`
|
||||||
|
- `src/interpreter/expressions/calls.rs` → Extract static methods, stdlib integration
|
||||||
|
- Create `src/interpreter/dispatch/` module for method dispatch logic
|
||||||
|
|
||||||
|
### Priority 4: Box Pattern Standardization
|
||||||
|
|
||||||
|
**Goal:** Reduce duplication in Box implementations
|
||||||
|
|
||||||
|
**Actions:**
|
||||||
|
1. Create `BoxMacros` proc-macro for common patterns
|
||||||
|
2. Standardize constructor patterns
|
||||||
|
3. Extract common drop/finalization logic
|
||||||
|
4. Create Box trait bounds helpers
|
||||||
|
|
||||||
|
## Technical Debt Remediation
|
||||||
|
|
||||||
|
### Error Handling Improvements
|
||||||
|
- Replace `unwrap()` with proper error propagation (797 instances)
|
||||||
|
- Standardize error types across modules
|
||||||
|
- Implement consistent error context
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
- Audit `.clone()` usage for performance impact
|
||||||
|
- Implement `Cow<T>` where appropriate
|
||||||
|
- Optimize Arc/Rc usage patterns
|
||||||
|
|
||||||
|
### Dead Code Removal
|
||||||
|
- Remove 36 dead code suppressions systematically
|
||||||
|
- Clean up TODO/FIXME comments (convert to GitHub issues)
|
||||||
|
- Remove deprecated method implementations
|
||||||
|
|
||||||
|
## Implementation Roadmap
|
||||||
|
|
||||||
|
### Phase 1: Foundation (Week 1-2)
|
||||||
|
- [ ] Create unified plugin loader interfaces
|
||||||
|
- [ ] Extract common FFI patterns
|
||||||
|
- [ ] Set up modular structure for MIR builder
|
||||||
|
|
||||||
|
### Phase 2: Migration (Week 3-4)
|
||||||
|
- [ ] Migrate existing plugin usage to unified loader
|
||||||
|
- [ ] Split MIR builder into modules
|
||||||
|
- [ ] Begin interpreter core modularization
|
||||||
|
|
||||||
|
### Phase 3: Optimization (Week 5-6)
|
||||||
|
- [ ] Implement Box pattern standardization
|
||||||
|
- [ ] Remove dead code systematically
|
||||||
|
- [ ] Performance audit and optimization
|
||||||
|
|
||||||
|
### Phase 4: Validation (Week 7-8)
|
||||||
|
- [ ] Comprehensive testing of refactored code
|
||||||
|
- [ ] Performance benchmarking
|
||||||
|
- [ ] Documentation updates
|
||||||
|
|
||||||
|
## Risk Assessment
|
||||||
|
|
||||||
|
**Low Risk:**
|
||||||
|
- Dead code removal
|
||||||
|
- Documentation improvements
|
||||||
|
- Box pattern standardization
|
||||||
|
|
||||||
|
**Medium Risk:**
|
||||||
|
- MIR builder modularization
|
||||||
|
- Interpreter splitting
|
||||||
|
- Memory management optimizations
|
||||||
|
|
||||||
|
**High Risk:**
|
||||||
|
- Plugin loader consolidation (affects external plugins)
|
||||||
|
- Error handling refactor (wide-reaching changes)
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
- **Code Quality:** Reduce cyclomatic complexity by 30%
|
||||||
|
- **Maintainability:** Reduce average file size from 500+ to <300 lines
|
||||||
|
- **Performance:** No regression in benchmark performance
|
||||||
|
- **Reliability:** Maintain 100% test coverage
|
||||||
|
- **Developer Experience:** Reduce onboarding time for new contributors
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The Nyash codebase shows signs of rapid development with opportunities for significant refactoring. The plugin loader consolidation offers the highest impact for maintenance improvement, while the MIR builder modularization will improve long-term extensibility. A phased approach is recommended to minimize disruption while delivering incremental benefits.
|
||||||
|
|
||||||
|
The analysis reveals a well-architected system that would benefit from tactical refactoring to improve maintainability without compromising the innovative "Everything is Box" design philosophy.
|
||||||
@ -1,475 +1,74 @@
|
|||||||
# 🎯 CURRENT TASK - 2025年8月26日(状況整理&再起動ショートカット)
|
# 🎯 CURRENT TASK - 2025-08-26(Context Reset / Fresh Focus)
|
||||||
|
|
||||||
## ⏱️ 再開ショートカット(今日のフォーカス)
|
コンテキストを「0%」にリセットし、いま必要なことだけに集中するにゃ。
|
||||||
- フォーカス: VM比較経路の安定化後片付け + 1000行分解の下準備
|
|
||||||
- 目標: 一時ログ抑制・Phi正規化・基本ボックス統一(String/Bool)・VM分割の導線作成
|
|
||||||
- 参照: `docs/development/roadmap/phases/phase-9/phase_9_78h_mir_pipeline_stabilization.md`
|
|
||||||
|
|
||||||
### 直近の実行タスク(9.78h)
|
## ⏱️ 今日のフォーカス(Phase 9.79a: Unified Dispatch + P2P Polish)
|
||||||
1) 一時デバッグログの抑制(`NYASH_VM_DEBUG_*`のみ)
|
- 判断: 統一Box設計は「非侵襲のディスパッチ統一」から入る → P2PBox磨きを同時並行
|
||||||
- 進捗: Runnerのバナー/プラグイン初期化ログは `NYASH_CLI_VERBOSE`/`NYASH_DEBUG_PLUGIN` のみで出力。
|
- 目的: ユニバーサルメソッド(toString/type/equals/clone)をVM/Interpreter前段で統一 + P2PBoxのmulti-node/async UX安定化
|
||||||
VMの逐次ログは `NYASH_VM_DEBUG[_EXEC|_CMP|_ANDOR|_PHI]` に限定。
|
|
||||||
2) Phi正規化(LoopExecutorの借用衝突解消 → 正しい選択へ復帰)
|
|
||||||
- 進捗: VM側の選択を `previous_block` 基準に復帰(fallback: 先頭)。`NYASH_VM_DEBUG_PHI=1` でログ。
|
|
||||||
- 設計: docs/development/current/PHI_NORMALIZATION_PLAN.md を参照(段階プラン/次アクション)。
|
|
||||||
3) 基本ボックス統一(StringBox/BoolBoxもre-export化)
|
|
||||||
4) VM分割の導線(control_flow/dispatch/frameへ分離設計)
|
|
||||||
- 進捗: `src/backend/{control_flow.rs,dispatch.rs,frame.rs}` を追加(骨組み)。ビルド通過。
|
|
||||||
5) 代表スナップショット追加(compare/loop/typeop_mixed)
|
|
||||||
|
|
||||||
### すぐ試せるコマンド
|
### 直近の実行タスク(小さく早く)
|
||||||
|
1) ユニバーサルメソッドの前段ディスパッチ(非侵襲)
|
||||||
|
- VM/Interpreterで`toString/type/equals/clone`を共通ヘルパにマップ(トレイト変更なし)
|
||||||
|
2) P2PBox磨き(multi-node/async/解除)
|
||||||
|
- share/cloneセマンティクス:share=共有, clone=新規(実装済みの明文化)
|
||||||
|
- unregisterの安全化(endpoint一致 or refcount)
|
||||||
|
- onOnce/off のE2Eテスト追加
|
||||||
|
- VM表示整合(getLast*/debug_* の toString/Console)
|
||||||
|
3) E2Eスモーク更新
|
||||||
|
- self→self, two-node ping-pong(安定)
|
||||||
|
- asyncデモ(TimeBox併用で確実に出力)
|
||||||
|
|
||||||
|
### すぐ試せるコマンド(最小)
|
||||||
```bash
|
```bash
|
||||||
|
# Rust(Release)
|
||||||
cargo build --release -j32
|
cargo build --release -j32
|
||||||
nyash --dump-mir --mir-verbose local_tests/typeop_is_as_func_poc.nyash | sed -n '1,160p'
|
./target/release/nyash --help
|
||||||
NYASH_OPT_DIAG_FAIL=1 nyash --dump-mir --mir-verbose local_tests/typeop_diag_fail.nyash || echo DIAG BLOCKED
|
|
||||||
tools/ci_check_golden.sh # 代表ケースのMIR含有チェック
|
|
||||||
|
|
||||||
# 比較・論理のスモーク(VM)
|
# Plugin デバッグ実行(任意)
|
||||||
nyash --backend vm local_tests/compare_box_vm.nyash # 期待: true
|
NYASH_DEBUG_PLUGIN=1 ./target/release/nyash --backend vm local_tests/extern_console_log.nyash || true
|
||||||
nyash --backend vm local_tests/and_or_vm.nyash # 期待: false\ntrue
|
|
||||||
nyash --backend vm local_tests/and_or_truthy_vm.nyash # 期待: false,true,false,true,false
|
# WASM(Web配布)
|
||||||
|
cd projects/nyash-wasm && wasm-pack build --target web --out-dir pkg
|
||||||
```
|
```
|
||||||
|
|
||||||
### 重要リンク(唯一参照/ゲート)
|
## 現在の地図(Done / Doing / Next)
|
||||||
- 命令セット(唯一出典・26命令): `docs/reference/mir/INSTRUCTION_SET.md`
|
|
||||||
- 9.78h(本フェーズ詳細): `docs/development/roadmap/phases/phase-9/phase_9_78h_mir_pipeline_stabilization.md`
|
### ✅ 完了
|
||||||
- 9.79(P2P本体 前提: 9.78h完了): `docs/development/roadmap/phases/phase-9/phase_9_79_p2pbox_rebuild.md`
|
- PluginHostファサード導入・移行(create/invoke/extern)
|
||||||
|
- TLVヘッダ/引数/ハンドルの共通化(`plugin_ffi_common.rs`)
|
||||||
|
- Interpreter分割の導線: `eval.rs` / `calls.rs` / `methods_dispatch.rs` 抽出
|
||||||
|
- ログ静音の基盤: `idebug!`(NYASH_DEBUG=1 で有効)を calls/core/statements に適用
|
||||||
|
- MIR modular builder ゲート追加(feature: `mir_modular_builder`)/ 整合パッチ投入
|
||||||
|
|
||||||
|
### 🚧 進行中(小タスク)
|
||||||
|
- Interpreterログ統一の残り(`delegation.rs` など)
|
||||||
|
- PluginHost の `resolve_method` キャッシュ化(I/O削減)
|
||||||
|
|
||||||
|
### ⏭️ 次アクション(今日~明日)
|
||||||
|
- 9.79a-M1: ユニバーサル前段ディスパッチ(VM/Interpreter)/ 回帰確認
|
||||||
|
- 9.79a-M2: P2P unregister安全化 + onOnce/off E2E + async安定
|
||||||
|
- 9.79a-M3: VM表示整合/ Docs更新(言語ガイド・P2Pリファレンス)
|
||||||
|
|
||||||
|
## 決定事項(Unified Box設計メモ)
|
||||||
|
- ドキュメント: `docs/ideas/other/2025-08-25-unified-box-design-deep-analysis.md`
|
||||||
|
- 判断: まずはディスパッチャ層でユニバーサルメソッドを統一(トレイト変更なし)
|
||||||
|
- P2Pは共有セマンティクス(share=共有, clone=新規)を維持しつつ unregister 正式化へ
|
||||||
|
|
||||||
|
## 参考リンク(唯一参照/ゲート)
|
||||||
|
- MIR命令セット(26命令): `docs/reference/mir/INSTRUCTION_SET.md`
|
||||||
|
- Phase 9.79(P2P): `docs/development/roadmap/phases/phase-9/phase_9_79_p2pbox_rebuild.md`
|
||||||
|
- Phase 9.79a(Unified Dispatch + P2P Polish): `docs/development/roadmap/phases/phase-9/phase_9_79a_unified_box_dispatch_and_p2p_polish.md`
|
||||||
|
- Phase 9.78h(前段完了): `docs/development/roadmap/phases/phase-9/phase_9_78h_mir_pipeline_stabilization.md`
|
||||||
- Phase 10(Cranelift JIT主経路): `docs/development/roadmap/phases/phase-10/phase_10_cranelift_jit_backend.md`
|
- Phase 10(Cranelift JIT主経路): `docs/development/roadmap/phases/phase-10/phase_10_cranelift_jit_backend.md`
|
||||||
|
|
||||||
### 🧩 今日の再開ポイント(2025-08-26)
|
## Doneの定義(P2PBox 最小)
|
||||||
- 完了(適度な分解): runnerを`modes/`へ分離(mir/vm/llvm/bench/wasm/aot/common)、objectsを`objects/{ops,methods,fields}.rs`へ、VMに`frame.rs`/`control_flow::record_transition`/`dispatch::execute_instruction`導入。ログ抑制(NYASH_VM_DEBUG_* / NYASH_CLI_VERBOSE / NYASH_DEBUG_PLUGIN)、Phi正規化Step1(previous_block基準)。
|
- `LocalLoopback` で ping/pong が安定
|
||||||
- 次アクション(小さく前進):
|
- P2PBox API(start/stop/send/broadcast/reply/on)が固まる
|
||||||
1) MIR26命令「総数一致」チェック(コード≡ドキュメント)
|
- ResultBox経由でエラーが伝搬(E2E テスト含む)
|
||||||
2) Loop SSA Step2(seal/pred更新のスケルトン+Verifierのphi系文言強化)
|
- ログは既定静音(環境変数でデバッグオン)
|
||||||
3) 代表スナップショット再確認(TypeOp/extern_call/loop/await/boxcall)
|
|
||||||
- クイックコマンド: `cargo build --release -j32` → `nyash --backend vm local_tests/compare_box_vm.nyash` → `./tools/ci_check_golden.sh`
|
## Parking Lot(後でやる)
|
||||||
|
- NyashValue enum導入(即値最適化)
|
||||||
|
- トレイト階層化(Comparable/Arithmetic etc.)
|
||||||
## 🚨 現在の状況(2025-08-25)
|
- メタプログラミング・パイプライン演算子
|
||||||
|
- `mir_modular_builder` をデフォルト化(パリティ後)
|
||||||
### ✅ 完了したタスク
|
|
||||||
|
|
||||||
1. **✅ MIRビルダーリファクタリング完了🎉**(2025-08-25)
|
|
||||||
- mir/builder.rs: 1547行の大規模モジュール → **モジュール分割完了**
|
|
||||||
- 新構造: `src/mir/builder/` ディレクトリ
|
|
||||||
- `mod.rs`: 公開API定義
|
|
||||||
- `core.rs`: MirBuilder本体 + コア機能 (205行)
|
|
||||||
- `expressions.rs`: 式変換処理 (621行) - 18関数実装済み
|
|
||||||
- `statements.rs`: 文変換処理 (165行) - 6関数実装済み
|
|
||||||
- `control_flow.rs`: 制御フロー構築 (194行) - 4関数実装済み
|
|
||||||
- `box_handlers.rs`: Box関連処理 (73行) - 2関数実装済み
|
|
||||||
- **ビルド確認**: 新構造でコンパイル正常完了 ✅
|
|
||||||
- **総移動関数数**: 30関数(ヘルパー関数含む)
|
|
||||||
- **行数削減**: 元の1547行から分割により読みやすさ大幅向上
|
|
||||||
- **コミット完了**:
|
|
||||||
- Phase 3-5: cc2a5c2 (expressions.rs)
|
|
||||||
- Phase 6-8: 29af5e1 (statements/control_flow/box_handlers)
|
|
||||||
|
|
||||||
2. **✅ VMモジュールのリファクタリング完了**(2025-08-25)
|
|
||||||
- execute_instruction関数を29個のハンドラーに分割
|
|
||||||
- vm_instructions.rsモジュール作成(487行)
|
|
||||||
- execute_instruction_old削除(691行削減)
|
|
||||||
- vm.rs: 2075行→1382行(33%削減)
|
|
||||||
|
|
||||||
### 🆕 進捗(2025-08-25 夜)
|
|
||||||
- VM and/or 対応の実装を追加(短絡評価は `as_bool()` 強制で統一、`BoxRef(IntegerBox)` の四則演算にも対応)。
|
|
||||||
- ゴールデン(TypeOp代表2ケース)は緑のまま、Optimizer診断(未lowering検知)も動作確認済み。
|
|
||||||
- ただし、VM実行時に `And` が `execute_binop` の短絡ルートに入らず、`execute_binary_op` の汎用経路で `Type error: Unsupported binary operation: And ...` が発生。
|
|
||||||
- 兆候: `NYASH_VM_DEBUG_ANDOR` のデバッグ出力が出ない=`execute_binop` に入っていない、または別実装ルートを通過している可能性。
|
|
||||||
- 仮説: 古いVM経路の残存/リンク切替、もしくは実行時に別ビルド/別profileが使用されている。
|
|
||||||
|
|
||||||
### 🆕 進捗(2025-08-26 早朝)
|
|
||||||
- and/or 真理値強制の最終化: `VMValue::as_bool()` を拡張(`BoxRef(IntegerBox)`→非0でtrue、`Void`→false)。
|
|
||||||
- テスト追加: `local_tests/and_or_truthy_vm.nyash`(期待: `false,true,false,true,false`)。緑を確認。
|
|
||||||
- 基本ボックス統一(第一弾): `IntegerBox` を正典に一本化。
|
|
||||||
- `src/boxes/integer_box.rs` は `pub use crate::box_trait::IntegerBox;` に置換し、実体の二重化を排除。
|
|
||||||
- 既知の未解決: ループ比較で `BoxRef(IntegerBox) < BoxRef(IntegerBox)` が TypeError。
|
|
||||||
- 対応中: 比較前に i64 へ正規化するフォールバックをVMに実装(downcast→toString→parse)。
|
|
||||||
- 80/20ポリシー: 数値にパース可能なら比較継続、失敗時のみTypeError。
|
|
||||||
|
|
||||||
### 🆕 進捗(2025-08-26 午前)
|
|
||||||
- TypeError(`And/Or` 経路)再発なしを確認(3スモーク緑: compare/and_or/and_or_truthy)。
|
|
||||||
- ログ抑制の徹底: Runner/VMのデバッグ出力を既定で静音、環境変数でのみ有効化。
|
|
||||||
- Phi正規化 Step1: `previous_block` によるPhi入力選択をVMに実装(`NYASH_VM_DEBUG_PHI=1`)。
|
|
||||||
- VM分割の骨組み: `control_flow.rs`/`dispatch.rs`/`frame.rs` 追加(今後段階移動)。
|
|
||||||
- レガシー削除: `src/mir/builder_old.rs`, `src/mir/builder.rs.backup`, `src/parser.rs.backup`, `src/instance.rs.backup`, `src/box_trait.rs.backup` を削除。
|
|
||||||
- objects.rs 分解 Step1: `execute_new` をヘルパ(三分割)へ抽出しスリム化(等価挙動)。
|
|
||||||
|
|
||||||
### 🎯 次の優先タスク(更新)
|
|
||||||
|
|
||||||
1. **copilot_issues.txtの確認**
|
|
||||||
- Phase 8.4: AST→MIR Lowering完全実装(最優先)
|
|
||||||
- Phase 8.5: MIRダイエット(35命令→20命令)
|
|
||||||
- Phase 8.6: VM性能改善(0.9倍 → 2倍以上)
|
|
||||||
|
|
||||||
1.5 **VM実行経路の特定と修正(最優先)**
|
|
||||||
- `runner.rs → VM::execute_module → execute_instruction → execute_binop` の各段でデバッグログを追加し、`BinOp(And/Or)` がどこで分岐しているか特定。
|
|
||||||
- バイナリ一致確認: `strings` によるシグネチャ(デバッグ文字列)含有の照合で実行バイナリを同定。
|
|
||||||
- 代替経路の洗い出し: `src/` 全体で `execute_binop`/`And`/`Unsupported binary operation` を再走査し、影響箇所を一掃。
|
|
||||||
- 修正後、`local_tests/and_or_vm.nyash` で `false/true` の出力を確認。
|
|
||||||
- ルート確定: Compare経路はBuilder側のCast導線で安定。VM側は保険フォールバックを維持しつつ一時ログを抑制へ。
|
|
||||||
1.6 **objects.rs 分解 Step2(安全にファイル分割)**
|
|
||||||
- `objects_impl.rs` を導入し、抽出済みヘルパを移動。本体は薄いラッパに。
|
|
||||||
- 以降: `objects/{fields.rs,methods.rs,ops.rs}` への段階分解。
|
|
||||||
|
|
||||||
1.7 **runner.rs 分離**
|
|
||||||
- `init_bid_plugins` を `runner/plugin_init.rs` へ抽出。各モードを `runner/modes/*.rs` に。
|
|
||||||
|
|
||||||
1.8 **VM分割の段階移動**
|
|
||||||
- ブロック遷移を `control_flow.rs`、フレーム状態を `frame.rs` に移し、`dispatch.rs` の導線を準備。
|
|
||||||
|
|
||||||
2. **MIR26命令対応**
|
|
||||||
- TypeOp/WeakRef/Barrierのプリンタ拡張
|
|
||||||
- スナップショット整備(extern_call/loop/boxcall/typeop_mixed 追加済)
|
|
||||||
- vm-stats差分確認
|
|
||||||
|
|
||||||
3. **Builder適用拡大**
|
|
||||||
- 言語 `is/as` 導線の実装
|
|
||||||
- 弱参照フィールドのWeakLoad/WeakNew対応
|
|
||||||
- 関数スタイル `isType/asType` の早期lowering強化
|
|
||||||
|
|
||||||
## 🗺️ 計画の粒度(近々/中期/長期)
|
|
||||||
|
|
||||||
### 近々(1–2週間)
|
|
||||||
- Loop SSA復帰: `loop_api` 小APIを活用し、Phi挿入・seal・predecessor更新を段階適用。簡易loweringを正しいSSAに置換。
|
|
||||||
- Builder移行完了: `builder.rs`の機能を`builder_modularized/*`へ移し切り、両者の差分(命令フィールド名・効果)を完全一致。
|
|
||||||
- TypeOp網羅: `is/as`/`isType/asType`の早期loweringを再点検、Optimizer診断(未lowering検出)を有効化し回帰を防止。
|
|
||||||
- 軽量スナップショット: MIRダンプ(verbose)に対する含有チェックを代表ケース(TypeOp/extern_call/loop/await/boxcall)へ拡張。
|
|
||||||
|
|
||||||
### 中期(3–4週間)
|
|
||||||
- WeakRef/Barrier統合: lowering(WeakNew/WeakLoad/Barrier)導線を整理、統合命令フラグON/OFFでMIR差を固定化。Verifierに整合チェック追加。
|
|
||||||
- MIR26整合化: Printer/Verifier/Optimizerの26命令前提をそろえ、効果の表記・検証を強化。
|
|
||||||
- CLI分離テスト: ワークスペース分割 or lib/binaryテスト分離で、`cargo test -p core` のみで回る導線を確立(CLI構成変更で止まらない)。
|
|
||||||
- ResultBox移行: `box_trait::ResultBox` 参照を `boxes::ResultBox` へ一掃し、レガシー実装を段階的に削除。
|
|
||||||
|
|
||||||
### 長期(Phase 8.4–8.6連動)
|
|
||||||
- AST→MIR Lowering完全化(8.4): すべての言語要素を新MIRへ安定lower。未対応分岐をゼロに。
|
|
||||||
- MIRダイエット(8.5): 命令の統一・簡素化(TypeOp/WeakRef/Barrierの統合効果)を活用し最小集合へ。
|
|
||||||
- VM性能改善(8.6): Hot-path(BoxCall/Array/Map)高速化、And/OrのVM未実装の解消、BoxRef演算の抜けを補完。
|
|
||||||
|
|
||||||
### Phase 10 着手ゲート(Cranelift前提条件)
|
|
||||||
- [ ] MIR26整合化完了(Printer/Verifier/Optimizerが26命令で一致・効果表記統一)
|
|
||||||
- [ ] Loop SSA復帰(Phi/Seal/Pred更新がVerifierで合格)
|
|
||||||
- [ ] TypeOp網羅(is/as/isType/asTypeの早期lowering+Optimizer診断ONで回帰ゼロ)
|
|
||||||
- [ ] 軽量スナップショット緑(TypeOp/extern_call/loop/await/boxcall 代表ケース)
|
|
||||||
- [ ] P2PBox再設計(Phase 9.79)完了・E2Eパス
|
|
||||||
- [ ] CLI分離テスト(`cargo test -p core`)が安定実行
|
|
||||||
|
|
||||||
### ⚠️ トラブルノート(VM and/or 追跡用)
|
|
||||||
- 症状: `--backend vm` で `And` が `execute_binop` の短絡パスを経由せず、`execute_binary_op` 汎用パスで `Type error`。
|
|
||||||
- 状況: `MIR --dump-mir` では `bb0: %X = %A And %B` を出力(BuilderはOK)。
|
|
||||||
- 差分検証: `execute_instruction`/`execute_binop` に挿入したデバッグ出力が未出力→実行経路の相違が濃厚。
|
|
||||||
- 対応: 実行バイナリの署名チェックとコードパスの網羅的ログ追加でルート確定→修正。
|
|
||||||
|
|
||||||
### 🔧 再開ガイド(最短手順)
|
|
||||||
- ログ抑制: 一時ログはそのままでもOK。静かにしたい場合は `NYASH_VM_DEBUG_CMP=0 NYASH_VM_DEBUG_ANDOR=0` で実行。
|
|
||||||
- 検証: 上記スモーク3本(compare/and_or/and_or_truthy)→`tools/ci_check_golden.sh`。
|
|
||||||
- 進める順: Phi正規化 → String/Bool re-export → VM分割設計 → スナップショット追加。
|
|
||||||
|
|
||||||
### ✅ 小タスク完了(2025-08-25 深夜)
|
|
||||||
- Verifier: Barrierの軽い文脈診断を追加(`NYASH_VERIFY_BARRIER_STRICT=1`で有効)。
|
|
||||||
- ResultBox移行TODOを追加(`docs/development/current/RESULTBOX_MIGRATION_TODO.md`)。
|
|
||||||
- VM: 旧`box_trait::ResultBox`扱いのデプリケーション警告を最小抑制(完全移行までの暫定)。
|
|
||||||
|
|
||||||
### ✅ 小タスク完了(2025-08-26 早朝)
|
|
||||||
- VM: `as_bool()` の拡張(IntegerBox/ Void を真理値化)。
|
|
||||||
- テスト: `and_or_truthy_vm.nyash` 追加。
|
|
||||||
- 基本ボックス統一(第一弾): `IntegerBox` 実体の一本化(re-export)。
|
|
||||||
|
|
||||||
### ▶ リファクタリング開始(控えめ)
|
|
||||||
- 方針: 肥大化防止のため`src/backend/vm.rs`を2分割のみ実施。
|
|
||||||
- `vm_values.rs`: `execute_binary_op`/`execute_unary_op`/`execute_compare_op` を移動。
|
|
||||||
- `vm_boxcall.rs`: `call_box_method` を移動(`call_unified_method`は現状のまま)。
|
|
||||||
- 影響最小・挙動非変更で、可読性と責務分離を先行する。
|
|
||||||
|
|
||||||
## 🧭 リファクタリングゴール(AI一目見て分かる導線)
|
|
||||||
|
|
||||||
### 目的/原則
|
|
||||||
- 明確責務: 各モジュールが1行で説明できる目的・責務を持つ
|
|
||||||
- 入口統一: VM/MIRの読む順番が固定され、迷わない
|
|
||||||
- ファイル肥大防止: 1000行超は段階分割(まずVMホットパス)
|
|
||||||
- ドキュメント導線: quick-referenceで最短経路を提示
|
|
||||||
- 振る舞い不変: すべてのゴールデン/CIが緑
|
|
||||||
|
|
||||||
### マイルストーン(受入基準)
|
|
||||||
- M1: VM導線完成(達成)
|
|
||||||
- vm.rs → vm_instructions.rs → vm_values.rs / vm_boxcall.rs / vm_stats.rs / vm_phi.rs
|
|
||||||
- code-map(docs/quick-reference/code-map.md)で入口と責務を明記
|
|
||||||
- 受入: build OK / golden 緑 / 実行変化なし
|
|
||||||
- M2: モジュールヘッダ(着手)
|
|
||||||
- Purpose/Responsibilities/Key APIs/Typical Callers を各VMモジュール先頭に追記
|
|
||||||
- 受入: 各モジュール先頭4行程度の要約とcode-mapの整合
|
|
||||||
- M3: 1000行級の導線整備(設計メモ)
|
|
||||||
- interpreter/plugin_loader.rs: v2主導の導線明文化(削除は後)
|
|
||||||
- interpreter/objects.rs: 可視性/フィールド/ライフサイクルの分割指針をTODO化
|
|
||||||
- M4: レガシー移行(土台)
|
|
||||||
- RESULTBOX_MIGRATION_TODOの維持、VMは新旧両対応のまま
|
|
||||||
|
|
||||||
### 実行順(小さく確実に)
|
|
||||||
1) M2ヘッダ追加(VM系)
|
|
||||||
2) M3 plugin_loader 導線の明文化(コメント/TODO/コードマップ)
|
|
||||||
3) M3 objects.rs 分割設計メモ
|
|
||||||
4) 用語の統一・code-map追従
|
|
||||||
5) golden + 重要E2Eを確認
|
|
||||||
|
|
||||||
### ⚠️ MIRビルダー引き継ぎポイント(ChatGPT5さんへ)
|
|
||||||
- **状況**: MIRビルダーのモジュール化完了(Phase 1-8コミット済み)
|
|
||||||
- **問題**: MIR命令構造の変更により、expressions.rsでエラー発生
|
|
||||||
- `Call`命令: `function`→`func`, `arguments`→`args`
|
|
||||||
- `ArrayAccess`, `ArrayLiteral`, `Await`ノードが削除?
|
|
||||||
- effectsフィールドの有無が命令により異なる
|
|
||||||
- TypeOpKindのインポートパスエラー
|
|
||||||
- loop_builder.rsでのプライベートフィールドアクセス問題
|
|
||||||
- **現在の対応**:
|
|
||||||
- builder_modularizedディレクトリに一時退避
|
|
||||||
- 元のbuilder.rsでフルビルド可能な状態に復帰
|
|
||||||
- ChatGPT5さんのMIR命令変更に合わせた調整が必要
|
|
||||||
|
|
||||||
2. **VMの既知の問題**
|
|
||||||
- 論理演算子(and, or)がBinOpとして未実装
|
|
||||||
- エラー: `Type error: Unsupported binary operation: And on Bool(true) and Bool(false)`
|
|
||||||
- インタープリターでは動作するがVMで動作しない
|
|
||||||
- **新発見**: BoxRef(IntegerBox) + BoxRef(IntegerBox)のような演算も未対応
|
|
||||||
- execute_binary_opにBoxRef同士の演算ケースが不足
|
|
||||||
|
|
||||||
## ✅ 直近の完了
|
|
||||||
1. VMモジュールのリファクタリング完了(2025-08-25)
|
|
||||||
- execute_instruction関数を29個のハンドラーに分割
|
|
||||||
- vm_instructions.rsモジュール作成(487行)
|
|
||||||
- execute_instruction_old削除(691行削減)
|
|
||||||
- vm.rs: 2075行→1382行(33%削減)
|
|
||||||
2. ドキュメント再編成の完了(構造刷新)
|
|
||||||
2. VM×プラグインのE2E整備(FileBox/Net)
|
|
||||||
- FileBox: open/write/read, copyFrom(handle)(VM)
|
|
||||||
- Net: GET/POST(VM)、404/500(Ok(Response))、unreachable(Err(ErrorBox))
|
|
||||||
3. VM命令カウンタ+時間計測のCLI化(`--vm-stats`, `--vm-stats-json`)とJSON出力対応
|
|
||||||
- サンプル/スクリプト整備(tools/run_vm_stats.sh、local_tests/vm_stats_*.nyash)
|
|
||||||
4. MIR if-merge 修正(retがphi dstを返す)+ Verifier強化(mergeでのphi未使用検知、支配関係チェック導入)
|
|
||||||
5. VMの健全化(分岐・比較・Result)
|
|
||||||
- Compare: Void/BoolのEq/Ne定義(順序比較はTypeError)
|
|
||||||
- Branch条件: `BoxRef(BoolBox)→bool`/`BoxRef(VoidBox)→false`/`Integer≠0→true`
|
|
||||||
- ResultBox: 新旧両実装への動的ディスパッチ統一(isOk/getValue/getError)
|
|
||||||
6. VMビルトイン強化(Array/Map/Socket)
|
|
||||||
- ArrayBox/MapBox: 代表メソッドをVM統合ディスパッチで実装(push/get/set/size等)
|
|
||||||
- SocketBox: `acceptTimeout(ms)`(void)/ `recvTimeout(ms)`(空文字)を追加
|
|
||||||
- E2E追加: `socket_timeout_server.nyash` / `socket_timeout_client.nyash`
|
|
||||||
7. E2E拡張(Net/Socket)
|
|
||||||
- HTTP: 大ボディ取得クライアント `local_tests/http_big_body_client.nyash`
|
|
||||||
- Socket: 反復タイムアウト検証 `local_tests/socket_repeated_timeouts.nyash`
|
|
||||||
- インタープリタ: SocketBoxの `acceptTimeout/recvTimeout` を結線
|
|
||||||
8. VM/MIRの健全化(Builder/VM)
|
|
||||||
- Compare拡張: Float/Int-Float混在をサポート(Eq/Ne/Lt/Le/Gt/Ge)
|
|
||||||
- TypeOp(Check)最小意味論実装(Integer/Float/Bool/String/Void/Box名)
|
|
||||||
- ArrayGet/ArraySet(VM)本実装(ArrayBox.get/setへ橋渡し)
|
|
||||||
- Array/Mapをidentity扱い(clone_or_shareがshareを選択)
|
|
||||||
- BoxCallにArrayBox fast-path(BoxRefからget/set直呼び)
|
|
||||||
- me参照の安定化(fallback時に一度だけConstを発行しvariable_mapに保持)
|
|
||||||
- デバッグ: `NYASH_VM_DEBUG_BOXCALL=1` でBoxCallの受け手/引数/経路/結果型を標準エラーに出力
|
|
||||||
9. ドキュメント追加・更新
|
|
||||||
- MIR→VMマッピング(分岐条件の動的変換、Void/Bool比較)
|
|
||||||
- VM README(SocketBoxタイムアウト/E2E導線・HTTP Result整理)
|
|
||||||
- 26命令ダイエット: PoCフラグと進捗追記(TypeOp/WeakRef/Barrier)
|
|
||||||
10. CI: plugins E2E ジョブ(Linux)を追加
|
|
||||||
|
|
||||||
### 📊 大規模ファイルのリファクタリング候補
|
|
||||||
1. src/interpreter/objects.rs (1,272行) - オブジェクト処理の分割
|
|
||||||
2. src/interpreter/plugin_loader.rs (1,217行) - v2があるので削除候補?
|
|
||||||
3. src/interpreter/expressions/calls.rs (1,016行) - 関数呼び出し処理の分割
|
|
||||||
|
|
||||||
## 🚧 次にやること(再開方針)
|
|
||||||
|
|
||||||
1) MIR26 前進(短期)
|
|
||||||
- プリンタ拡張: `TypeOp/WeakRef/Barrier` を `--mir-verbose` に明示表示
|
|
||||||
- スナップショット整備: 代表ケースで flag ON/OFF のMIR差分固定化
|
|
||||||
- vm-stats差分: `weak_field_poc.nyash` 等で JSON 取得・比較(キー: TypeOp/WeakRef/Barrier)
|
|
||||||
- 注: `mir_typeop_poc` / `mir_refbarrier_unify_poc` は非推奨(no-op)。Builderは統合命令(TypeOp/WeakRef/Barrier)を常に生成する。Optimizerに旧命令→統合命令の正規化パスあり。
|
|
||||||
|
|
||||||
2) Builder適用拡大(短期〜中期)
|
|
||||||
- 言語 `is/as` 導線(最小でも擬似ノード)→ `emit_type_check/emit_cast` へ配線
|
|
||||||
- 弱参照: 既存の `RefGet/RefSet` パスは弱フィールドで `WeakLoad/WeakNew`+Barrier(flag ONで統合命令)
|
|
||||||
- 関数スタイル `isType/asType` の早期loweringを強化(`Literal("T")` と `new StringBox("T")` を確実に検出)
|
|
||||||
- `print(isType(...))` の未定義SSA回避(print直前で必ず `TypeOp` のdstを生成)
|
|
||||||
|
|
||||||
補足: 近々/中期で並行対応する項目
|
|
||||||
- Loop SSA復帰(Phi挿入/Seal/Pred更新の段階適用、簡易loweringの置換)
|
|
||||||
- Builder移行完了(`builder.rs` → `builder_modularized/*`、命令フィールド名・効果の完全一致)
|
|
||||||
- CLI分離テスト導線(`cargo test -p core` 単独で回る構成)
|
|
||||||
- ResultBox移行(`box_trait::ResultBox` → `boxes::ResultBox`、レガシー段階削除)
|
|
||||||
|
|
||||||
3) VM/Verifierの補強(中期)
|
|
||||||
- `TypeOp(Cast)` の数値キャスト(Int/Float)安全化、誤用時TypeError整備
|
|
||||||
- Verifierに26命令整合(Barrier位置、WeakRef整合、支配関係)チェックを追加
|
|
||||||
|
|
||||||
4) VM×プラグインE2Eの維持(短期)
|
|
||||||
- HTTP/Socketの回帰確認(Void防御・遅延サーバ軽量化は済)
|
|
||||||
- 必要に応じて `VM_README.md` にTips追記
|
|
||||||
|
|
||||||
5) BoxCall高速化(性能段階)
|
|
||||||
- `--vm-stats` ホットパス特定後、Fast-path/キャッシュ適用
|
|
||||||
|
|
||||||
## 🐛 既知の問題(要フォロー)
|
|
||||||
- 関数スタイル `isType(value, "Integer")` が一部ケースで `TypeOp` にloweringされず、`print %X` が未定義参照になる事象
|
|
||||||
- 現状: `asType` は `typeop cast` に変換されるが、`isType` が欠落するケースあり
|
|
||||||
- 仮対処: Interpreterに `is/isType/as/asType` のフォールバックを実装済(実行エラー回避)
|
|
||||||
- 恒久対処(次回対応):
|
|
||||||
- Builderの早期loweringをFunctionCall分岐で強化(`Literal`/`StringBox`両対応、`print(...)` 内でも確実にdst生成)
|
|
||||||
- Optimizerの安全ネット(BoxCall/Call→TypeOp)を `isType` パターンでも確実に発火させる(テーブル駆動の判定)
|
|
||||||
|
|
||||||
- 命令の二重化(Legacy vs Unified)
|
|
||||||
- 状況: `MirInstruction` に旧系(`TypeCheck/Cast/WeakNew/WeakLoad/BarrierRead/BarrierWrite`)と統合系(`TypeOp/WeakRef/Barrier`)が併存。
|
|
||||||
- リスク: ドキュメントは26命令(統合系)で凍結のため、実装との二重化がバグ温床に。
|
|
||||||
- 当面のガード: 26命令同期テストを追加(`src/mir/instruction_introspection.rs`)。ドキュメント≡実装名がズレたら赤。
|
|
||||||
- 次アクション:
|
|
||||||
1) Optimizer診断に「旧命令検出」フラグを追加(`NYASH_OPT_DIAG_FORBID_LEGACY=1`で旧命令が存在したら失敗)。
|
|
||||||
2) Printer/Verifier/Optimizerの表記・効果を統合側に寄せる(表示も統一)。
|
|
||||||
3) 段階的に旧命令をcfgで囲む or 内部で統合命令へアダプト(最終的に旧命令経路を削除)。
|
|
||||||
|
|
||||||
## ⏸️ セッション再開メモ(次にやること)
|
|
||||||
- [ ] Builder: `extract_string_literal` の `StringBox`対応は導入済 → `FunctionCall` 早期loweringの再検証(`print(isType(...))` 直下)
|
|
||||||
- [ ] Optimizer: `Call` 形式(関数呼び出し)でも `isType/asType` を検出して `TypeOp(Check/Cast)` に置換する安全ネットの強化とテスト
|
|
||||||
- [ ] MIRダンプの確認:`local_tests/typeop_is_as_func_poc.nyash` に `typeop check/cast` が両方出ることを確認
|
|
||||||
- [ ] スナップショット化:`typeop_is_as_*_poc.nyash` のダンプを固定し回帰検出
|
|
||||||
|
|
||||||
## ▶ 補助コマンド(検証系)
|
|
||||||
```bash
|
|
||||||
# リビルド
|
|
||||||
cargo build --release -j32
|
|
||||||
|
|
||||||
# 関数スタイルのMIRダンプ確認(isType/asType)
|
|
||||||
./target/release/nyash --dump-mir --mir-verbose local_tests/typeop_is_as_func_poc.nyash | sed -n '1,200p'
|
|
||||||
|
|
||||||
# メソッドスタイルのMIRダンプ確認(is/as)
|
|
||||||
./target/release/nyash --dump-mir --mir-verbose local_tests/typeop_is_as_poc.nyash | sed -n '1,200p'
|
|
||||||
```
|
|
||||||
|
|
||||||
### 🆕 開発時の可視化・診断(最小追加)
|
|
||||||
- `--mir-verbose-effects`: MIRダンプ行末に効果カテゴリを表示(`pure|readonly|side`)
|
|
||||||
- 例: `nyash --dump-mir --mir-verbose --mir-verbose-effects local_tests/typeop_is_as_func_poc.nyash`
|
|
||||||
- `NYASH_OPT_DIAG_FAIL=1`: Optimizer診断で未lowering(`is|isType|as|asType`)検知時にエラー終了(CI向け)
|
|
||||||
- 例: `NYASH_OPT_DIAG_FAIL=1 nyash --dump-mir --mir-verbose local_tests/typeop_diag_fail.nyash`
|
|
||||||
- Builder生MIRスナップショット: `tools/snapshot_mir.sh <input.nyash> [output.txt]`
|
|
||||||
- 例: `tools/snapshot_mir.sh local_tests/typeop_is_as_func_poc.nyash docs/status/golden/typeop_is_as_func_poc.mir.txt`
|
|
||||||
- ゴールデン比較(ローカル/CI): `tools/ci_check_golden.sh`(代表2ケースを比較)
|
|
||||||
- 例: `./tools/ci_check_golden.sh`(差分があれば非ゼロ終了)
|
|
||||||
|
|
||||||
補足: ASTの形状確認は `--dump-ast` を使用。
|
|
||||||
|
|
||||||
#### 代表スナップショット対象(拡張)
|
|
||||||
- TypeOp系: `typeop_is_as_poc.nyash`, `typeop_is_as_func_poc.nyash`
|
|
||||||
- extern_call: `http_get_*.nyash`, `filebox_copy_from_handle.nyash`
|
|
||||||
- loop/await: `loop_phi_seal_poc.nyash`, `await_http_timeout_poc.nyash`
|
|
||||||
- boxcall: `array_map_fastpath_poc.nyash`, `boxcall_identity_share_poc.nyash`
|
|
||||||
|
|
||||||
## ▶ 実行コマンド例
|
|
||||||
|
|
||||||
計測実行:
|
|
||||||
```bash
|
|
||||||
tools/run_vm_stats.sh local_tests/vm_stats_http_ok.nyash vm_stats_ok.json
|
|
||||||
tools/run_vm_stats.sh local_tests/vm_stats_http_err.nyash vm_stats_err.json
|
|
||||||
tools/run_vm_stats.sh local_tests/vm_stats_http_404.nyash vm_stats_404.json
|
|
||||||
tools/run_vm_stats.sh local_tests/vm_stats_http_500.nyash vm_stats_500.json
|
|
||||||
```
|
|
||||||
|
|
||||||
VM×プラグインE2E:
|
|
||||||
```bash
|
|
||||||
cargo test -q --features plugins e2e_interpreter_plugin_filebox_close_void
|
|
||||||
cargo test -q --features plugins e2e_vm_plugin_filebox_close_void
|
|
||||||
```
|
|
||||||
|
|
||||||
MIR26 PoC(弱参照・Barrier統合):
|
|
||||||
```bash
|
|
||||||
# 弱フィールドPoC(flag OFF: WeakNew/WeakLoad/BarrierRead/Write)
|
|
||||||
NYASH_VM_STATS=1 NYASH_VM_STATS_JSON=1 ./target/release/nyash --backend vm --vm-stats --vm-stats-json local_tests/weak_field_poc.nyash > vm_stats_weak_default.json
|
|
||||||
|
|
||||||
# flag ON: WeakRef/Barrier 統合
|
|
||||||
cargo build --release --features mir_refbarrier_unify_poc -q
|
|
||||||
NYASH_VM_STATS=1 NYASH_VM_STATS_JSON=1 ./target/release/nyash --backend vm --vm-stats --vm-stats-json local_tests/weak_field_poc.nyash > vm_stats_weak_unified.json
|
|
||||||
```
|
|
||||||
|
|
||||||
MIRダンプ(プリンタ拡張後の確認):
|
|
||||||
```bash
|
|
||||||
./target/release/nyash --dump-mir --mir-verbose local_tests/weak_field_poc.nyash | sed -n '1,200p'
|
|
||||||
```
|
|
||||||
|
|
||||||
MIRダンプ/検証:
|
|
||||||
```bash
|
|
||||||
nyash --dump-mir --mir-verbose examples/plugin_box_sample.nyash
|
|
||||||
nyash --verify examples/plugin_box_sample.nyash
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔭 26命令ターゲット(合意ドラフト)
|
|
||||||
- コア: Const / Copy / Load / Store / BinOp / UnaryOp / Compare / Jump / Branch / Phi / Return / Call / BoxCall / NewBox / ArrayGet / ArraySet / RefNew / RefGet / RefSet / Await / Print / ExternCall(最小) / TypeOp(=TypeCheck/Cast統合) / WeakRef(=WeakNew/WeakLoad統合) / Barrier(=Read/Write統合)
|
|
||||||
- メタ降格: Debug / Nop / Safepoint(ビルドモードで制御)
|
|
||||||
|
|
||||||
---
|
|
||||||
最終更新: 2025年8月25日(MIR26整合確認・VM and/or実装・Loop補強・CLI分離テスト導線/ResultBox移行TODO追加/スナップショット最小運用)
|
|
||||||
|
|
||||||
## 🔁 再起動後の再開手順(ショート)
|
|
||||||
```bash
|
|
||||||
# 1) ビルド
|
|
||||||
cargo build --release -j32
|
|
||||||
|
|
||||||
# 2) plugins E2E(Linux)
|
|
||||||
cargo test --features plugins -q -- --nocapture
|
|
||||||
|
|
||||||
# 3) VM Stats 代表値の再取得(任意)
|
|
||||||
tools/run_vm_stats.sh local_tests/vm_stats_http_ok.nyash vm_stats_ok.json
|
|
||||||
tools/run_vm_stats.sh local_tests/vm_stats_http_err.nyash vm_stats_err.json
|
|
||||||
|
|
||||||
# 4) SocketBox タイムアウト確認(任意)
|
|
||||||
./target/release/nyash local_tests/socket_timeout_server.nyash
|
|
||||||
./target/release/nyash local_tests/socket_timeout_client.nyash
|
|
||||||
|
|
||||||
# 5) 反復タイムアウト確認(任意)
|
|
||||||
./target/release/nyash local_tests/socket_repeated_timeouts.nyash
|
|
||||||
|
|
||||||
# 6) HTTP 大ボディ確認(任意)
|
|
||||||
./target/release/nyash local_tests/http_big_body_client.nyash
|
|
||||||
|
|
||||||
# 7) VM BoxCall デバッグ(任意)
|
|
||||||
NYASH_VM_DEBUG_BOXCALL=1 ./target/release/nyash --backend vm local_tests/test_vm_array_getset.nyash
|
|
||||||
```
|
|
||||||
- MIR26 整合(候補1)確認:
|
|
||||||
- Printer: `--mir-verbose-effects` の `pure|readonly|side` 表記と TypeOp/WeakRef/Barrier/ExternCall の表示が整合。
|
|
||||||
- Verifier: SSA/支配/CFG/merge-phi に加え WeakRef/Barrier の最小検証+Strict Barrier診断を実装(環境変数でON)。
|
|
||||||
- Optimizer: 未lowering検知(is/as/isType/asType)をBoxCall/Call両経路で検出、`NYASH_OPT_DIAG_FAIL=1` と連携。
|
|
||||||
- 代表スナップショット: extern_call/loop/boxcall/typeop_mixed をCIに追加、全件緑。
|
|
||||||
- 注: WeakRef/Barrier の“統合”はPoCフラグで切替可能(レガシー命令も支援)—MIR26はドキュメントの正典、実装は互換を維持。
|
|
||||||
|
|
||||||
|
|
||||||
## ✅ 本日の成果(9.78h)
|
|
||||||
- MIR26命令のコード≡ドキュメント同期テストを追加(tests/mir_instruction_set_sync.rs)→ 緑
|
|
||||||
- スナップショット安定化(tools/snapshot_mir.sh に静音フィルタ、golden全緑)
|
|
||||||
- Phi選択の委譲スケルトンをVMへ実装(LoopExecutor::execute_phi)
|
|
||||||
- ResultBox移行スイープ(VMのレガシーbox_trait::ResultBox経路を削除、boxes::ResultBoxに統一)
|
|
||||||
- VM BoxRef演算フォールバック拡張(toString→parse<i64>、比較/四則/混在を広くカバー)
|
|
||||||
- ロガー導入(環境変数ONでのみ出力):
|
|
||||||
- NYASH_DEBUG_EFFECTS / NYASH_DEBUG_VERIFIER / NYASH_DEBUG_MIR_PRINTER / NYASH_DEBUG_TRYCATCH
|
|
||||||
- NYASH_VM_DEBUG_REF / NYASH_VM_DEBUG_BIN / 既存の NYASH_VM_DEBUG_* 系
|
|
||||||
- VMユーザBoxのBoxCallをInstance関数へ橋渡し(InstanceBox → Class.method/arity へ委譲)
|
|
||||||
|
|
||||||
## 🧪 テスト概況(要点)
|
|
||||||
- 緑: 175件 / 赤: 1件(`mir::verification::tests::test_if_merge_uses_phi_not_predecessor`)
|
|
||||||
- 失敗要旨: Mergeブロックで前任値使用と判定(DominatorViolation/MergeUsesPredecessorValue)
|
|
||||||
- 生成MIR(CLI/Builder)はmergeにphiを含むため、Verifier側の検査条件かvariable_map束縛の拾い漏れの可能性。
|
|
||||||
|
|
||||||
### デバッグ用コマンド(ログON例)
|
|
||||||
```bash
|
|
||||||
# Effects純度
|
|
||||||
NYASH_DEBUG_EFFECTS=1 cargo test --lib mir::effect::tests::test_effect_mask_creation -- --nocapture
|
|
||||||
|
|
||||||
# Verifier(phi/merge)
|
|
||||||
NYASH_DEBUG_VERIFIER=1 cargo test --lib mir::verification::tests::test_if_merge_uses_phi_not_predecessor -- --nocapture
|
|
||||||
|
|
||||||
# Try/CatchのLowering/Printer
|
|
||||||
NYASH_DEBUG_TRYCATCH=1 NYASH_DEBUG_MIR_PRINTER=1 cargo test --lib mir::tests::test_try_catch_compilation -- --nocapture
|
|
||||||
|
|
||||||
# VM 参照/演算
|
|
||||||
NYASH_VM_DEBUG_REF=1 cargo test --lib backend::vm::tests::test_vm_user_box_birth_and_method -- --nocapture
|
|
||||||
NYASH_VM_DEBUG_BIN=1 cargo test --lib tests::mir_vm_poc::test_boxref_arith -- --nocapture
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎯 次の着手(残1の精密駆逐)
|
|
||||||
1) Verifierのmerge-phi検査を補強(phi dst/variable束縛の対応づけ強化 or 条件緩和の適用)
|
|
||||||
2) Builderのvariable_map束縛拾い漏れがあれば補修(Program直下パターンの明示)
|
|
||||||
3) 代表MIRダンプをfixture化して回帰チェック(phi存在の有無を機械判定)
|
|
||||||
|
|||||||
116
docs/development/current/PLUGIN_LOADER_MIGRATION_PLAN.md
Normal file
116
docs/development/current/PLUGIN_LOADER_MIGRATION_PLAN.md
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# Plugin Loader Migration Plan
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Consolidate three plugin loader implementations into a single unified system.
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
1. **plugin_loader_v2.rs** (906 lines) - Main BID-FFI plugin system
|
||||||
|
2. **plugin_loader.rs** (1217 lines) - Builtin box dynamic loading
|
||||||
|
3. **plugin_loader_legacy.rs** (299 lines) - Legacy host vtable system
|
||||||
|
|
||||||
|
Total: ~2400 lines of code with significant duplication
|
||||||
|
|
||||||
|
## Target State
|
||||||
|
- **plugin_loader_unified.rs** - Single unified loader (~800 lines)
|
||||||
|
- **plugin_ffi_common.rs** - Shared FFI utilities (~300 lines)
|
||||||
|
- Total: ~1100 lines (55% reduction)
|
||||||
|
|
||||||
|
## Migration Steps
|
||||||
|
|
||||||
|
### Phase 1: Infrastructure (DONE)
|
||||||
|
- [x] Create plugin_loader_unified.rs skeleton
|
||||||
|
- [x] Create plugin_ffi_common.rs for shared types
|
||||||
|
- [x] Update runtime/mod.rs
|
||||||
|
- [x] Basic compilation check
|
||||||
|
|
||||||
|
### Phase 2: Common Pattern Extraction
|
||||||
|
- [ ] Extract TLV encoding/decoding to plugin_ffi_common
|
||||||
|
- [ ] Extract handle management patterns
|
||||||
|
- [ ] Extract memory management utilities
|
||||||
|
- [ ] Extract error handling patterns
|
||||||
|
|
||||||
|
### Phase 3: BID Plugin Migration
|
||||||
|
- [ ] Port PluginBoxV2 implementation
|
||||||
|
- [ ] Port BID FFI invoke mechanism
|
||||||
|
- [ ] Port plugin loading logic
|
||||||
|
- [ ] Port configuration parsing
|
||||||
|
- [ ] Migrate tests
|
||||||
|
|
||||||
|
### Phase 4: Builtin Plugin Migration
|
||||||
|
- [ ] Port FileBoxProxy and related proxies
|
||||||
|
- [ ] Port dynamic library loading for builtins
|
||||||
|
- [ ] Port builtin-specific FFI patterns
|
||||||
|
- [ ] Migrate feature flags handling
|
||||||
|
|
||||||
|
### Phase 5: Legacy Plugin Migration
|
||||||
|
- [ ] Port host vtable implementation
|
||||||
|
- [ ] Port legacy box creation
|
||||||
|
- [ ] Decide on deprecation timeline
|
||||||
|
|
||||||
|
### Phase 6: Integration
|
||||||
|
- [ ] Update all references to old loaders
|
||||||
|
- [ ] Ensure backward compatibility
|
||||||
|
- [ ] Performance benchmarking
|
||||||
|
- [ ] Documentation update
|
||||||
|
|
||||||
|
### Phase 7: Cleanup
|
||||||
|
- [ ] Remove old plugin loaders
|
||||||
|
- [ ] Remove redundant tests
|
||||||
|
- [ ] Update CLAUDE.md
|
||||||
|
- [ ] Final code review
|
||||||
|
|
||||||
|
## Technical Decisions
|
||||||
|
|
||||||
|
### Unified Plugin Type Detection
|
||||||
|
```rust
|
||||||
|
fn detect_plugin_type(path: &str) -> PluginType {
|
||||||
|
// 1. Check file extension (.bid, .legacy, .builtin)
|
||||||
|
// 2. Check nyash-box.toml for type field
|
||||||
|
// 3. Probe for known symbols
|
||||||
|
// 4. Default to BID
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Handle Management Strategy
|
||||||
|
- Use generic `PluginHandle<T>` for all plugin types
|
||||||
|
- Centralized handle registry in plugin_ffi_common
|
||||||
|
- Reference counting with Arc<Mutex<T>>
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
- Convert all errors to Result<T, String> at boundaries
|
||||||
|
- Plugin-specific errors wrapped with context
|
||||||
|
- Consistent error messages across all plugin types
|
||||||
|
|
||||||
|
## Risk Mitigation
|
||||||
|
|
||||||
|
### Backward Compatibility
|
||||||
|
- Keep old loaders under feature flags during migration
|
||||||
|
- Provide compatibility shims if needed
|
||||||
|
- Extensive testing with existing plugins
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- Benchmark before and after migration
|
||||||
|
- Profile hot paths (box creation, method dispatch)
|
||||||
|
- Optimize only after correctness verified
|
||||||
|
|
||||||
|
### Testing Strategy
|
||||||
|
1. Unit tests for each component
|
||||||
|
2. Integration tests with real plugins
|
||||||
|
3. E2E tests with full applications
|
||||||
|
4. Regression tests for edge cases
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
- [ ] Code reduction: 50%+ fewer lines
|
||||||
|
- [ ] All existing tests pass
|
||||||
|
- [ ] No performance regression
|
||||||
|
- [ ] Cleaner API surface
|
||||||
|
- [ ] Better error messages
|
||||||
|
- [ ] Easier to add new plugin types
|
||||||
|
|
||||||
|
## Timeline
|
||||||
|
- Phase 1-2: Day 1 (Today)
|
||||||
|
- Phase 3-4: Day 2-3
|
||||||
|
- Phase 5-6: Day 4
|
||||||
|
- Phase 7: Day 5
|
||||||
|
|
||||||
|
Total: 5 days for complete migration
|
||||||
28
docs/development/mir/MIR_BUILDER_MIGRATION.md
Normal file
28
docs/development/mir/MIR_BUILDER_MIGRATION.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# MIR Builder Migration Plan (builder -> builder_modularized)
|
||||||
|
|
||||||
|
Goal: Gradually switch from `src/mir/builder.rs` to the modular split in
|
||||||
|
`src/mir/builder_modularized/` without breaking default builds.
|
||||||
|
|
||||||
|
## Phases
|
||||||
|
|
||||||
|
1) Compatibility layer (done)
|
||||||
|
- Keep default export `MirBuilder` from `builder.rs`.
|
||||||
|
- Gate modularized builder behind feature `mir_modular_builder`.
|
||||||
|
- Add helpers (`emit_type_check/cast/weak_new/weak_load/barrier_*`) in `builder.rs`.
|
||||||
|
|
||||||
|
2) Field-name alignment (next)
|
||||||
|
- `builder_modularized/core.rs` uses provisional field names for instructions.
|
||||||
|
- Align to current `MirInstruction`:
|
||||||
|
- TypeOp: `{ op, value, ty }` instead of `{ operation, operand, type_info }`.
|
||||||
|
- WeakRef/Barrier: use `WeakRef { dst, op, value }` and `Barrier { op, ptr }` forms if present.
|
||||||
|
- Import enums via `use crate::mir::{TypeOpKind, WeakRefOp, BarrierOp};`.
|
||||||
|
|
||||||
|
3) Swap export (opt-in → default)
|
||||||
|
- With `--features mir_modular_builder`, ensure `cargo check` passes.
|
||||||
|
- After parity tests (printer/optimizer/verifier), flip default export to modularized.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- No behavior changes intended during migration — only structural split.
|
||||||
|
- Keep logs behind `NYASH_BUILDER_DEBUG=1` to avoid noise.
|
||||||
|
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
# Phase 9.79a: Unified Box Dispatch (Minimal) + P2PBox Polish
|
||||||
|
|
||||||
|
Status: Planned
|
||||||
|
Last Updated: 2025-08-26
|
||||||
|
Owner: core-runtime
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
- Unify basic Box methods (toString/type/equals/clone) at the dispatcher level without breaking NyashBox trait.
|
||||||
|
- Simplify VM/Interpreter method routing with a single, predictable path for “universal” methods.
|
||||||
|
- Polish P2PBox: formalize share/clone semantics and safe unregister; finalize multi-node + async UX.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
1) Dispatcher-level unification (非侵襲)
|
||||||
|
- VM: universal methods (toString/type/equals/clone) を型分岐前に一括処理。
|
||||||
|
- Interpreter: 同様の「ユニバーサル優先→型別詳細」パターンを採用。
|
||||||
|
- NyashBoxトレイトは現状の`to_string_box/type_name/equals/clone_box/share_box`を維持。
|
||||||
|
|
||||||
|
2) P2PBox磨き(Phase 9.79 継続)
|
||||||
|
- 共有セマンティクス: share_box()はtransport/handlers/flags共有、clone_box()は新規transport(済)。
|
||||||
|
- InProcess unregister: 一致解除 or refcount で安全に戻す(暫定停止の解除)。
|
||||||
|
- Transport API: `register_intent_handler`は導入済。WS/WebRTCの下準備(薄いshim)を設計。
|
||||||
|
- E2E: on()/onOnce()/off()、self-ping、two-node ping-pong、TimeBox併用のasyncデモ。
|
||||||
|
- VM: P2Pヘルパ(getLast*/debug_*)のtoString/Console出力をInterpreterに寄せて整合。
|
||||||
|
|
||||||
|
## Out of Scope(今回はやらない)
|
||||||
|
- 全BoxにUnifiedBoxトレイトを適用する大改修(段階的移行のため見送り)。
|
||||||
|
- ビルトインBoxの完全プラグイン化(Phase 10+ 候補)。
|
||||||
|
- NyashValueの全面置換(機会見つけて漸進導入)。
|
||||||
|
|
||||||
|
## Deep Analysis Docとの整合
|
||||||
|
- まずはディスパッチャで統一(トレイト変更なし)→ 破壊的変更を避けつつ美しさを担保。
|
||||||
|
- Nyash言語の`toString/type/equals/clone`はVM/Interpreterで中央集約的にRust側APIへ橋渡し。
|
||||||
|
- 「Everything is Box」を壊さずに一貫した呼び出し体験を提供する。
|
||||||
|
|
||||||
|
## Milestones
|
||||||
|
- M1(Day 1–2)
|
||||||
|
- VM: universal methods 前置ディスパッチ
|
||||||
|
- Interpreter: 同様の前置ディスパッチ
|
||||||
|
- スモーク:既存演算子/print動作の回帰なし
|
||||||
|
- M2(Day 3–4)
|
||||||
|
- P2PBox unregister安全化(endpoint一致 or refcount)
|
||||||
|
- E2E: onOnce/off 追加、two-node ping-pong 安定、asyncデモが確実に出力
|
||||||
|
- M3(Day 5)
|
||||||
|
- VM表示整合:P2PヘルパのtoString/ConsoleをInterpreterと一致
|
||||||
|
- Docs更新:言語ガイド/P2Pリファレンス反映
|
||||||
|
|
||||||
|
## リスクと対策
|
||||||
|
- VM分岐に触るリスク → 型別分岐の“前段”に追加、既存分岐はフォールバックとして維持
|
||||||
|
- unregister回りの退行 → 一致解除テスト/順次Dropテスト(clone/share/Drop順の組み合わせ)を追加
|
||||||
|
|
||||||
|
## 受け入れ基準
|
||||||
|
- VM/Interpreterの両方で toString/type/equals/clone が統一パスで動作
|
||||||
|
- P2PBox: multi-node ping-pong + onOnce/off E2Eが通り、asyncデモが確実にログ出力
|
||||||
|
- 既存スモークに回帰なし、Docs更新完了
|
||||||
|
|
||||||
|
## 備考
|
||||||
|
- UnifiedBox.dispatch_methodはPhase 10での検討項目として温存。
|
||||||
|
- NyashValueの活用はMIR/VM安定化と歩調を合わせて拡大。
|
||||||
@ -241,8 +241,122 @@ nyash-box-gen migrate \
|
|||||||
- [ ] ホットリロード機能
|
- [ ] ホットリロード機能
|
||||||
- [ ] デバッグサポート
|
- [ ] デバッグサポート
|
||||||
|
|
||||||
|
## 使い分けの指針
|
||||||
|
|
||||||
|
### ビルトインBox(静的リンク)が適する場合
|
||||||
|
- **パフォーマンスクリティカル**: StringBox, IntegerBox等の基本型
|
||||||
|
- **起動時必須**: ConsoleBox等のコア機能
|
||||||
|
- **セキュリティ重視**: 改竄されると致命的なBox
|
||||||
|
|
||||||
|
### プラグインBoxが適する場合
|
||||||
|
- **多言語連携**: Python, C, Go等で実装されたBox
|
||||||
|
- **サードパーティ提供**: コミュニティ製Box
|
||||||
|
- **実験的機能**: 頻繁に更新されるBox
|
||||||
|
- **プラットフォーム依存**: OS固有機能のラッパー
|
||||||
|
|
||||||
|
### 多言語プラグインの例
|
||||||
|
|
||||||
|
#### Python製プラグイン
|
||||||
|
```toml
|
||||||
|
# plugins/numpy-box/nyash-box.toml
|
||||||
|
[box]
|
||||||
|
id = "numpy"
|
||||||
|
version = "1.0.0"
|
||||||
|
language = "python"
|
||||||
|
runtime = "python3.11"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
numpy = "1.24.0"
|
||||||
|
pyo3 = "0.19.0"
|
||||||
|
|
||||||
|
[wrapper]
|
||||||
|
entry = "numpy_box_wrapper.py"
|
||||||
|
ffi_bridge = "libpython_nyash_bridge.so"
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# numpy_box_wrapper.py
|
||||||
|
import numpy as np
|
||||||
|
from nyash_python import Box, register_box
|
||||||
|
|
||||||
|
@register_box("NumpyBox")
|
||||||
|
class NumpyBox(Box):
|
||||||
|
def __init__(self, shape):
|
||||||
|
self.array = np.zeros(shape)
|
||||||
|
|
||||||
|
def multiply(self, scalar):
|
||||||
|
return NumpyBox.from_array(self.array * scalar)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### C製プラグイン(既存ライブラリのラッパー)
|
||||||
|
```c
|
||||||
|
// plugins/sqlite-box/sqlite_box.c
|
||||||
|
#include <sqlite3.h>
|
||||||
|
#include "nyash_box.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
NyBoxBase base;
|
||||||
|
sqlite3 *db;
|
||||||
|
} SqliteBox;
|
||||||
|
|
||||||
|
NyBoxV1* sqlite_box_register(NyHostV1* host) {
|
||||||
|
// SQLiteの全機能をNyashから使えるように
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 統合アーキテクチャ
|
||||||
|
|
||||||
|
```
|
||||||
|
Nyashランタイム
|
||||||
|
├── ビルトインBox(高速・安全)
|
||||||
|
│ ├── StringBox (Rust)
|
||||||
|
│ ├── IntegerBox (Rust)
|
||||||
|
│ └── ConsoleBox (Rust)
|
||||||
|
│
|
||||||
|
└── プラグインBox(柔軟・拡張可能)
|
||||||
|
├── FileBox (Rust plugin)
|
||||||
|
├── NumpyBox (Python via PyO3)
|
||||||
|
├── SqliteBox (C wrapper)
|
||||||
|
├── TensorFlowBox (C++ via bindgen)
|
||||||
|
└── NodeBox (JavaScript via N-API)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 将来的な可能性
|
||||||
|
|
||||||
|
### 1. 言語別プラグインSDK
|
||||||
|
```bash
|
||||||
|
# 各言語用のテンプレート生成
|
||||||
|
nyash-plugin-sdk init --lang python --name my-box
|
||||||
|
nyash-plugin-sdk init --lang rust --name my-box
|
||||||
|
nyash-plugin-sdk init --lang c --name my-box
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. プラグインマーケットプレイス
|
||||||
|
```bash
|
||||||
|
# コミュニティ製Boxの検索・インストール
|
||||||
|
nyash plugin search opencv
|
||||||
|
nyash plugin install opencv-box@2.4.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. ポリグロットBox
|
||||||
|
```toml
|
||||||
|
# 複数言語を組み合わせたBox
|
||||||
|
[box]
|
||||||
|
id = "ml-pipeline"
|
||||||
|
components = [
|
||||||
|
{ lang = "python", module = "preprocessing" },
|
||||||
|
{ lang = "rust", module = "core_algorithm" },
|
||||||
|
{ lang = "c++", module = "gpu_acceleration" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## まとめ
|
## まとめ
|
||||||
|
|
||||||
このシステムにより、「Everything is Box」の理想を保ちながら、
|
このシステムにより、「Everything is Box」の理想を保ちながら、
|
||||||
現実的なパフォーマンスと開発効率を両立できる。
|
現実的なパフォーマンスと開発効率を両立できる。
|
||||||
純粋主義と実用主義の最適なバランスを実現する。
|
さらに、多言語エコシステムとの統合により、
|
||||||
|
Nyashが真の「ユニバーサルグルー言語」となる可能性を秘めている。
|
||||||
|
|
||||||
|
ビルトインBoxは「高速・安全・必須」を担保し、
|
||||||
|
プラグインBoxは「柔軟・拡張・実験」を可能にする。
|
||||||
|
この二層構造こそが、Nyashの持続的な成長を支える基盤となる。
|
||||||
@ -528,3 +528,259 @@ optional = ["file", "math", "time"]
|
|||||||
```
|
```
|
||||||
|
|
||||||
これにより、「Everything is Box」哲学が実装レベルでも完全に実現される!
|
これにより、「Everything is Box」哲学が実装レベルでも完全に実現される!
|
||||||
|
|
||||||
|
## 🔍 統一デバッグインフラストラクチャ(2025-08-26追記)
|
||||||
|
|
||||||
|
### 📍 MIRレベルでの統一デバッグ実現
|
||||||
|
|
||||||
|
今までの議論で、MIRレベルでのデバッグ実装が最も理想的であることが判明しました。
|
||||||
|
Gemini先生とCodex先生の両方が同じ結論に達しました:**設計案2+3のハイブリッド**が最適解です。
|
||||||
|
|
||||||
|
#### 核心設計:メタデータ分離+プロファイリングAPI
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// MIR本体はクリーンに保つ
|
||||||
|
pub struct MIRModule {
|
||||||
|
pub functions: HashMap<String, MIRFunction>,
|
||||||
|
pub constants: Vec<Constant>,
|
||||||
|
pub debug_info: Option<MIRDebugInfo>, // デバッグ時のみ生成
|
||||||
|
}
|
||||||
|
|
||||||
|
// 静的情報(設計案2)
|
||||||
|
pub struct MIRDebugInfo {
|
||||||
|
// ID→名前のマッピング(文字列を避けてIDベース)
|
||||||
|
pub type_table: HashMap<u16, BoxTypeDescriptor>, // TypeId → Box型情報
|
||||||
|
pub method_table: HashMap<u32, MethodInfo>, // MethodId → メソッド情報
|
||||||
|
pub site_table: HashMap<u32, SiteInfo>, // SiteId → 位置情報
|
||||||
|
pub source_map: HashMap<usize, SourceLocation>, // PC → ソース位置
|
||||||
|
}
|
||||||
|
|
||||||
|
// 動的収集(設計案3)
|
||||||
|
pub trait MIRProfiler: Send + Sync {
|
||||||
|
fn on_alloc(&mut self, type_id: u16, site_id: u32, obj_id: u64, size: usize);
|
||||||
|
fn on_free(&mut self, obj_id: u64);
|
||||||
|
fn on_method_enter(&mut self, method_id: u32, site_id: u32, instance_id: u64);
|
||||||
|
fn on_method_exit(&mut self, method_id: u32, site_id: u32, result: &VMValue);
|
||||||
|
fn on_field_access(&mut self, obj_id: u64, field_id: u16, is_write: bool);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### なぜこの設計が美しいのか?
|
||||||
|
|
||||||
|
1. **MIRの純粋性を保つ** - デバッグ命令でIRを汚染しない
|
||||||
|
2. **ゼロオーバーヘッド** - 本番ビルドではdebug_info = None
|
||||||
|
3. **全バックエンド統一** - VM/JIT/AOT/WASMで同じプロファイラAPI
|
||||||
|
4. **業界標準に準拠** - LLVM、JVM、.NETと同じアプローチ
|
||||||
|
|
||||||
|
### 🧠 DeepInspectorBox - 統一デバッグ体験
|
||||||
|
|
||||||
|
#### Everything is Boxの哲学をデバッグでも実現
|
||||||
|
|
||||||
|
```nyash
|
||||||
|
// グローバルシングルトン - すべてを見通す眼
|
||||||
|
static box DeepInspectorBox {
|
||||||
|
init {
|
||||||
|
enabled, // デバッグON/OFF
|
||||||
|
boxCreations, // すべてのBox生成履歴
|
||||||
|
methodCalls, // すべてのメソッド呼び出し
|
||||||
|
fieldAccess, // フィールドアクセス履歴
|
||||||
|
memorySnapshots, // メモリスナップショット
|
||||||
|
referenceGraph, // 参照グラフ(リーク検出用)
|
||||||
|
performanceMetrics // パフォーマンス統計
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Box ライフサイクル完全追跡 ===
|
||||||
|
trackBoxLifecycle(boxType) {
|
||||||
|
// 特定のBox型の生成から破棄まで完全追跡
|
||||||
|
return me.boxCreations.filter(b => b.type == boxType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// === メモリリーク検出(深い実装) ===
|
||||||
|
detectLeaks() {
|
||||||
|
// 参照グラフの構築
|
||||||
|
local graph = me.buildReferenceGraph()
|
||||||
|
|
||||||
|
// 循環参照の検出
|
||||||
|
local cycles = graph.findCycles()
|
||||||
|
|
||||||
|
// 到達不可能なBoxの検出
|
||||||
|
local unreachable = graph.findUnreachableFrom(me.getRootBoxes())
|
||||||
|
|
||||||
|
// weak参照の考慮
|
||||||
|
local suspicious = me.findSuspiciousWeakReferences()
|
||||||
|
|
||||||
|
return LeakReport {
|
||||||
|
cycles: cycles,
|
||||||
|
unreachable: unreachable,
|
||||||
|
suspicious: suspicious,
|
||||||
|
totalLeakedBytes: me.calculateLeakedMemory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === P2P非同期フロー可視化 ===
|
||||||
|
traceAsyncFlow(startEvent) {
|
||||||
|
// P2Pメッセージの送信から受信、ハンドラー実行までの完全追跡
|
||||||
|
local flow = []
|
||||||
|
|
||||||
|
// 送信イベント
|
||||||
|
flow.push(startEvent)
|
||||||
|
|
||||||
|
// Transport経由の転送
|
||||||
|
local transportEvents = me.findTransportEvents(startEvent.messageId)
|
||||||
|
flow.extend(transportEvents)
|
||||||
|
|
||||||
|
// MethodBox.invoke()の実行
|
||||||
|
local handlerExecution = me.findHandlerExecution(startEvent.to, startEvent.intent)
|
||||||
|
flow.push(handlerExecution)
|
||||||
|
|
||||||
|
return AsyncFlowTrace {
|
||||||
|
events: flow,
|
||||||
|
totalTime: flow.last().time - flow.first().time,
|
||||||
|
visualization: me.generateFlowDiagram(flow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔬 メモリリーク検出の深い仕組み
|
||||||
|
|
||||||
|
#### 参照グラフベースの完全検出
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl DeepInspectorBox {
|
||||||
|
/// 参照グラフの構築(UnifiedBox設計と統合)
|
||||||
|
fn build_reference_graph(&self) -> ReferenceGraph {
|
||||||
|
let mut graph = ReferenceGraph::new();
|
||||||
|
|
||||||
|
// すべてのBoxを走査(ビルトイン/プラグイン/ユーザー定義を統一的に)
|
||||||
|
for (obj_id, box_info) in &self.live_boxes {
|
||||||
|
match box_info.content {
|
||||||
|
// InstanceBoxのフィールド参照
|
||||||
|
BoxContent::Instance(fields) => {
|
||||||
|
for (field_name, field_value) in fields {
|
||||||
|
if let Some(target_id) = field_value.get_box_id() {
|
||||||
|
graph.add_edge(*obj_id, target_id, EdgeType::Field(field_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MethodBoxのインスタンス参照
|
||||||
|
BoxContent::Method { instance_id, .. } => {
|
||||||
|
graph.add_edge(*obj_id, instance_id, EdgeType::MethodInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// P2PBoxのハンドラー参照
|
||||||
|
BoxContent::P2P { handlers, .. } => {
|
||||||
|
for (intent, handler_id) in handlers {
|
||||||
|
graph.add_edge(*obj_id, handler_id, EdgeType::Handler(intent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArrayBox/MapBoxの要素参照
|
||||||
|
BoxContent::Container(elements) => {
|
||||||
|
for (index, element_id) in elements.iter().enumerate() {
|
||||||
|
graph.add_edge(*obj_id, element_id, EdgeType::Element(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
graph
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 高度なリーク検出アルゴリズム
|
||||||
|
fn detect_leak_patterns(&self, graph: &ReferenceGraph) -> Vec<LeakPattern> {
|
||||||
|
let mut patterns = vec![];
|
||||||
|
|
||||||
|
// Pattern 1: 単純な循環参照
|
||||||
|
let cycles = graph.tarjan_scc();
|
||||||
|
for cycle in cycles {
|
||||||
|
if cycle.len() > 1 {
|
||||||
|
patterns.push(LeakPattern::CircularReference(cycle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pattern 2: イベントハンドラーリーク(P2P特有)
|
||||||
|
for (node_id, node_info) in &self.p2p_nodes {
|
||||||
|
for (intent, handler) in &node_info.handlers {
|
||||||
|
if let Some(method_box) = handler.as_method_box() {
|
||||||
|
let instance_id = method_box.get_instance_id();
|
||||||
|
if !self.is_box_alive(instance_id) {
|
||||||
|
patterns.push(LeakPattern::DanglingHandler {
|
||||||
|
node: node_id.clone(),
|
||||||
|
intent: intent.clone(),
|
||||||
|
dead_instance: instance_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pattern 3: 巨大オブジェクトグラフ
|
||||||
|
let subgraphs = graph.find_connected_components();
|
||||||
|
for subgraph in subgraphs {
|
||||||
|
let total_size = subgraph.iter()
|
||||||
|
.map(|id| self.get_box_size(*id))
|
||||||
|
.sum::<usize>();
|
||||||
|
|
||||||
|
if total_size > SUSPICIOUS_GRAPH_SIZE {
|
||||||
|
patterns.push(LeakPattern::LargeObjectGraph {
|
||||||
|
root: subgraph[0],
|
||||||
|
size: total_size,
|
||||||
|
object_count: subgraph.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patterns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🚀 実装ロードマップ(2025年後半)
|
||||||
|
|
||||||
|
#### Phase 1: MIRデバッグ基盤(2週間)
|
||||||
|
- [ ] MIRDebugInfo構造の実装
|
||||||
|
- [ ] MIRProfilerトレイトの定義
|
||||||
|
- [ ] MIRビルダーでのデバッグ情報生成
|
||||||
|
|
||||||
|
#### Phase 2: VMプロファイラー統合(1週間)
|
||||||
|
- [ ] VMでのMIRProfiler実装
|
||||||
|
- [ ] DeepInspectorBoxのVM連携
|
||||||
|
- [ ] 基本的なメモリリーク検出
|
||||||
|
|
||||||
|
#### Phase 3: 非同期フロー可視化(1週間)
|
||||||
|
- [ ] P2Pメッセージトレース
|
||||||
|
- [ ] MethodBox実行追跡
|
||||||
|
- [ ] タイミング図の生成
|
||||||
|
|
||||||
|
#### Phase 4: WASM対応(2週間)
|
||||||
|
- [ ] nyash_debugインポートの実装
|
||||||
|
- [ ] カスタムセクションへのデバッグ情報埋め込み
|
||||||
|
- [ ] ブラウザ開発ツール連携
|
||||||
|
|
||||||
|
#### Phase 5: パフォーマンス最適化(1週間)
|
||||||
|
- [ ] ロックフリーリングバッファ
|
||||||
|
- [ ] サンプリングモード
|
||||||
|
- [ ] 増分参照グラフ更新
|
||||||
|
|
||||||
|
### 💎 統一の美しさ
|
||||||
|
|
||||||
|
この設計により、以下が実現されます:
|
||||||
|
|
||||||
|
1. **完全な可視性** - Boxの生成から破棄、メソッド呼び出し、フィールドアクセスまですべて追跡
|
||||||
|
2. **メモリ安全性の保証** - リークパターンの自動検出と可視化
|
||||||
|
3. **非同期フローの理解** - P2Pメッセージングの複雑な流れを完全に把握
|
||||||
|
4. **統一された体験** - VM/JIT/AOT/WASMすべてで同じデバッグ機能
|
||||||
|
5. **Nyashらしさ** - DeepInspectorBox自体もBoxとして実装
|
||||||
|
|
||||||
|
「Everything is Box」の哲学は、デバッグインフラストラクチャにおいても完全に実現されることになります。
|
||||||
|
|
||||||
|
### 🔮 将来の拡張可能性
|
||||||
|
|
||||||
|
- **AI支援デバッグ** - パターン認識によるバグの自動検出
|
||||||
|
- **時間遡行デバッグ** - 実行履歴の巻き戻しと再実行
|
||||||
|
- **分散トレーシング** - 複数ノード間のP2P通信の可視化
|
||||||
|
- **パフォーマンスAI** - ボトルネックの自動最適化提案
|
||||||
|
|
||||||
|
これらすべてが、統一されたMIRデバッグ基盤の上に構築可能です。
|
||||||
68
docs/reference/boxes-system/p2pbox.md
Normal file
68
docs/reference/boxes-system/p2pbox.md
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# P2PBox Reference (Phase 9.79 Minimal)
|
||||||
|
|
||||||
|
Status: Experimental (Loopback-ready)
|
||||||
|
Updated: 2025-08-26
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
P2PBox is a structured P2P node built on IntentBox + MessageBus + Transport.
|
||||||
|
This document covers the minimal API implemented for Phase 9.79.
|
||||||
|
|
||||||
|
## Core API
|
||||||
|
- new(nodeId: String, transport: String)
|
||||||
|
- getNodeId() -> String
|
||||||
|
- getTransportType() -> String
|
||||||
|
- isReachable(nodeId: String) -> Bool
|
||||||
|
- send(to: String, intent: IntentBox) -> ResultBox<Bool, Error>
|
||||||
|
- on(intentName: String, handler: MethodBox) -> ResultBox<Bool, Error>
|
||||||
|
- getLastIntentName() -> String // testing helper
|
||||||
|
- getLastFrom() -> String // testing helper
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- send() returns ResultBox; Ok(true) on success, Err(message) on failure
|
||||||
|
- on() currently wires MethodBox to transport and will call MethodBox.invoke(intent, from)
|
||||||
|
- MethodBox.invoke is a stub until Interpreter hook is added (see Roadmap)
|
||||||
|
|
||||||
|
## Transport
|
||||||
|
- InProcessTransport only (in-proc MessageBus)
|
||||||
|
- Transport trait exposes register_intent_handler(intent, cb) used by on()
|
||||||
|
|
||||||
|
## Lifecycle
|
||||||
|
Typical pattern where handler captures 'me':
|
||||||
|
|
||||||
|
```nyash
|
||||||
|
static box Node {
|
||||||
|
init { p2p, handle }
|
||||||
|
|
||||||
|
birth() {
|
||||||
|
me.p2p = new P2PBox("alice", "inprocess")
|
||||||
|
me.handle = new MethodBox(me, "onPing")
|
||||||
|
me.p2p.on("ping", me.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
onPing(intent, from) {
|
||||||
|
// TODO: requires MethodBox.invoke integration with interpreter
|
||||||
|
// print("got " + intent.getName() + " from " + from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Destruction order (no strong-cycle):
|
||||||
|
- Node (me) drops → fields drop (p2p, handle)
|
||||||
|
- P2PBox drops → InProcessTransport Drop → MessageBus unregister
|
||||||
|
- Registered handlers released → MethodBox released → me released
|
||||||
|
|
||||||
|
## Quick Smoke
|
||||||
|
```bash
|
||||||
|
cargo build -j32
|
||||||
|
./target/debug/nyash local_tests/p2p_self_ping.nyash
|
||||||
|
# Expect:
|
||||||
|
# last intent: ping
|
||||||
|
# last from: alice
|
||||||
|
```
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
- MethodBox.invoke → Interpreter hook (global invoker or dedicated API)
|
||||||
|
- P2PBox.off(intent) and onOnce(intent, handler)
|
||||||
|
- WebSocket/WebRTC transports implementing register_intent_handler
|
||||||
|
- Remove testing helpers (getLast*) once full handler path is live
|
||||||
|
|
||||||
Binary file not shown.
@ -74,6 +74,35 @@ impl VM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// P2PBox methods (minimal)
|
||||||
|
if let Some(p2p) = box_value.as_any().downcast_ref::<crate::boxes::p2p_box::P2PBox>() {
|
||||||
|
match method {
|
||||||
|
"send" => {
|
||||||
|
if _args.len() >= 2 { return Ok(p2p.send(_args[0].clone_or_share(), _args[1].clone_or_share())); }
|
||||||
|
return Ok(Box::new(StringBox::new("Error: send(to, intent) requires 2 args")));
|
||||||
|
}
|
||||||
|
"on" => {
|
||||||
|
if _args.len() >= 2 { return Ok(p2p.on(_args[0].clone_or_share(), _args[1].clone_or_share())); }
|
||||||
|
return Ok(Box::new(StringBox::new("Error: on(intent, handler) requires 2 args")));
|
||||||
|
}
|
||||||
|
"onOnce" | "on_once" => {
|
||||||
|
if _args.len() >= 2 { return Ok(p2p.on_once(_args[0].clone_or_share(), _args[1].clone_or_share())); }
|
||||||
|
return Ok(Box::new(StringBox::new("Error: onOnce(intent, handler) requires 2 args")));
|
||||||
|
}
|
||||||
|
"off" => {
|
||||||
|
if _args.len() >= 1 { return Ok(p2p.off(_args[0].clone_or_share())); }
|
||||||
|
return Ok(Box::new(StringBox::new("Error: off(intent) requires 1 arg")));
|
||||||
|
}
|
||||||
|
"getLastFrom" => { return Ok(p2p.get_last_from()); }
|
||||||
|
"getLastIntentName" => { return Ok(p2p.get_last_intent_name()); }
|
||||||
|
"debug_nodes" | "debugNodes" => { return Ok(p2p.debug_nodes()); }
|
||||||
|
"getNodeId" | "getId" => { return Ok(p2p.get_node_id()); }
|
||||||
|
"getTransportType" | "transport" => { return Ok(p2p.get_transport_type()); }
|
||||||
|
"isReachable" => { if let Some(n) = _args.get(0) { return Ok(p2p.is_reachable(n.clone_or_share())); } return Ok(Box::new(BoolBox::new(false))); }
|
||||||
|
_ => return Ok(Box::new(VoidBox::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SocketBox methods (minimal + timeouts)
|
// SocketBox methods (minimal + timeouts)
|
||||||
if let Some(sock) = box_value.as_any().downcast_ref::<crate::boxes::socket_box::SocketBox>() {
|
if let Some(sock) = box_value.as_any().downcast_ref::<crate::boxes::socket_box::SocketBox>() {
|
||||||
match method {
|
match method {
|
||||||
|
|||||||
@ -480,10 +480,10 @@ impl VM {
|
|||||||
nyash_args.push(arg_value.to_nyash_box());
|
nyash_args.push(arg_value.to_nyash_box());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route through plugin loader v2 (also handles env.* stubs)
|
// Route through unified plugin host (delegates to v2, handles env.* stubs)
|
||||||
let loader = crate::runtime::get_global_loader_v2();
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
let loader = loader.read().map_err(|_| VMError::InvalidInstruction("Plugin loader lock poisoned".into()))?;
|
let host = host.read().map_err(|_| VMError::InvalidInstruction("Plugin host lock poisoned".into()))?;
|
||||||
match loader.extern_call(iface_name, method_name, &nyash_args) {
|
match host.extern_call(iface_name, method_name, &nyash_args) {
|
||||||
Ok(Some(result_box)) => {
|
Ok(Some(result_box)) => {
|
||||||
if let Some(dst_id) = dst {
|
if let Some(dst_id) = dst {
|
||||||
self.set_value(dst_id, VMValue::from_nyash_box(result_box));
|
self.set_value(dst_id, VMValue::from_nyash_box(result_box));
|
||||||
|
|||||||
@ -37,9 +37,12 @@
|
|||||||
|
|
||||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||||
use crate::boxes::IntentBox;
|
use crate::boxes::IntentBox;
|
||||||
|
use crate::method_box::MethodBox;
|
||||||
|
use crate::boxes::result::{ResultBox, NyashResultBox};
|
||||||
use crate::transport::{Transport, InProcessTransport};
|
use crate::transport::{Transport, InProcessTransport};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::sync::RwLock;
|
use std::sync::{RwLock, Arc};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// P2PBox - P2P通信ノード (RwLock pattern)
|
/// P2PBox - P2P通信ノード (RwLock pattern)
|
||||||
@ -47,8 +50,12 @@ use std::collections::HashMap;
|
|||||||
pub struct P2PBox {
|
pub struct P2PBox {
|
||||||
base: BoxBase,
|
base: BoxBase,
|
||||||
node_id: RwLock<String>,
|
node_id: RwLock<String>,
|
||||||
transport: RwLock<Box<dyn Transport>>,
|
transport: Arc<RwLock<Box<dyn Transport>>>,
|
||||||
handlers: RwLock<HashMap<String, Box<dyn NyashBox>>>,
|
handlers: Arc<RwLock<HashMap<String, Box<dyn NyashBox>>>>,
|
||||||
|
handler_flags: Arc<RwLock<HashMap<String, Vec<Arc<AtomicBool>>>>>,
|
||||||
|
// Minimal receive cache for loopback smoke tests
|
||||||
|
last_from: Arc<RwLock<Option<String>>>,
|
||||||
|
last_intent_name: Arc<RwLock<Option<String>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for P2PBox {
|
impl Clone for P2PBox {
|
||||||
@ -62,12 +69,17 @@ impl Clone for P2PBox {
|
|||||||
TransportKind::InProcess => Box::new(InProcessTransport::new(node_id_val.clone())),
|
TransportKind::InProcess => Box::new(InProcessTransport::new(node_id_val.clone())),
|
||||||
};
|
};
|
||||||
let handlers_val = HashMap::new(); // Start fresh for cloned instance
|
let handlers_val = HashMap::new(); // Start fresh for cloned instance
|
||||||
|
let last_from_val = self.last_from.read().unwrap().clone();
|
||||||
|
let last_intent_val = self.last_intent_name.read().unwrap().clone();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
base: BoxBase::new(), // New unique ID for clone
|
base: BoxBase::new(), // New unique ID for clone
|
||||||
node_id: RwLock::new(node_id_val),
|
node_id: RwLock::new(node_id_val),
|
||||||
transport: RwLock::new(new_transport),
|
transport: Arc::new(RwLock::new(new_transport)),
|
||||||
handlers: RwLock::new(handlers_val),
|
handlers: Arc::new(RwLock::new(handlers_val)),
|
||||||
|
handler_flags: Arc::new(RwLock::new(HashMap::new())),
|
||||||
|
last_from: Arc::new(RwLock::new(last_from_val)),
|
||||||
|
last_intent_name: Arc::new(RwLock::new(last_intent_val)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,16 +103,30 @@ impl std::str::FromStr for TransportKind {
|
|||||||
impl P2PBox {
|
impl P2PBox {
|
||||||
/// 新しいP2PBoxを作成
|
/// 新しいP2PBoxを作成
|
||||||
pub fn new(node_id: String, transport_kind: TransportKind) -> Self {
|
pub fn new(node_id: String, transport_kind: TransportKind) -> Self {
|
||||||
let transport: Box<dyn Transport> = match transport_kind {
|
// Create transport and attach receive callback before boxing
|
||||||
TransportKind::InProcess => Box::new(InProcessTransport::new(node_id.clone())),
|
let (transport_boxed, attach_cb): (Box<dyn Transport>, bool) = match transport_kind {
|
||||||
|
TransportKind::InProcess => {
|
||||||
|
let mut t = InProcessTransport::new(node_id.clone());
|
||||||
|
// We'll attach callback below after P2PBox struct is created
|
||||||
|
(Box::new(t), true)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
P2PBox {
|
let p2p = P2PBox {
|
||||||
base: BoxBase::new(),
|
base: BoxBase::new(),
|
||||||
node_id: RwLock::new(node_id),
|
node_id: RwLock::new(node_id),
|
||||||
transport: RwLock::new(transport),
|
transport: Arc::new(RwLock::new(transport_boxed)),
|
||||||
handlers: RwLock::new(HashMap::new()),
|
handlers: Arc::new(RwLock::new(HashMap::new())),
|
||||||
}
|
handler_flags: Arc::new(RwLock::new(HashMap::new())),
|
||||||
|
last_from: Arc::new(RwLock::new(None)),
|
||||||
|
last_intent_name: Arc::new(RwLock::new(None)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note: InProcess callback registration is postponed until a unified
|
||||||
|
// Transport subscription API is provided. For now, loopback tracing is
|
||||||
|
// handled in send() when sending to self.
|
||||||
|
|
||||||
|
p2p
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ノードIDを取得
|
/// ノードIDを取得
|
||||||
@ -117,23 +143,83 @@ impl P2PBox {
|
|||||||
if let Some(intent_box) = intent.as_any().downcast_ref::<IntentBox>() {
|
if let Some(intent_box) = intent.as_any().downcast_ref::<IntentBox>() {
|
||||||
let transport = self.transport.read().unwrap();
|
let transport = self.transport.read().unwrap();
|
||||||
match transport.send(&to_str, intent_box.clone(), Default::default()) {
|
match transport.send(&to_str, intent_box.clone(), Default::default()) {
|
||||||
Ok(()) => Box::new(BoolBox::new(true)),
|
Ok(()) => {
|
||||||
Err(_) => Box::new(BoolBox::new(false)),
|
// Minimal loopback trace without relying on transport callbacks
|
||||||
|
let self_id = self.node_id.read().unwrap().clone();
|
||||||
|
if to_str == self_id {
|
||||||
|
if let Ok(mut lf) = self.last_from.write() { *lf = Some(self_id.clone()); }
|
||||||
|
if let Ok(mut li) = self.last_intent_name.write() { *li = Some(intent_box.get_name().to_string_box().value); }
|
||||||
|
}
|
||||||
|
Box::new(ResultBox::new_ok(Box::new(BoolBox::new(true))))
|
||||||
|
},
|
||||||
|
Err(e) => Box::new(ResultBox::new_err(Box::new(StringBox::new(format!("{:?}", e))))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Box::new(BoolBox::new(false))
|
Box::new(ResultBox::new_err(Box::new(StringBox::new("Second argument must be IntentBox"))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// イベントハンドラーを登録
|
||||||
|
fn register_handler_internal(&self, intent_str: &str, handler: &Box<dyn NyashBox>, once: bool) -> Box<dyn NyashBox> {
|
||||||
|
// 保存
|
||||||
|
{
|
||||||
|
let mut handlers = self.handlers.write().unwrap();
|
||||||
|
handlers.insert(intent_str.to_string(), handler.clone_box());
|
||||||
|
}
|
||||||
|
|
||||||
|
// フラグ登録
|
||||||
|
let flag = Arc::new(AtomicBool::new(true));
|
||||||
|
{
|
||||||
|
let mut flags = self.handler_flags.write().unwrap();
|
||||||
|
flags.entry(intent_str.to_string()).or_default().push(flag.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可能ならTransportにハンドラ登録(InProcessなど)
|
||||||
|
if let Ok(mut t) = self.transport.write() {
|
||||||
|
if let Some(method_box) = handler.as_any().downcast_ref::<MethodBox>() {
|
||||||
|
let method_clone = method_box.clone();
|
||||||
|
let intent_name = intent_str.to_string();
|
||||||
|
t.register_intent_handler(&intent_name, Box::new(move |env| {
|
||||||
|
// flagがtrueのときのみ実行
|
||||||
|
if flag.load(Ordering::SeqCst) {
|
||||||
|
let _ = method_clone.invoke(vec![
|
||||||
|
Box::new(env.intent.clone()),
|
||||||
|
Box::new(StringBox::new(env.from.clone())),
|
||||||
|
]);
|
||||||
|
if once {
|
||||||
|
flag.store(false, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Box::new(ResultBox::new_ok(Box::new(BoolBox::new(true))))
|
||||||
|
}
|
||||||
|
|
||||||
/// イベントハンドラーを登録
|
/// イベントハンドラーを登録
|
||||||
pub fn on(&self, intent_name: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
pub fn on(&self, intent_name: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||||
let intent_str = intent_name.to_string_box().value;
|
let intent_str = intent_name.to_string_box().value;
|
||||||
|
self.register_handler_internal(&intent_str, &handler, false)
|
||||||
|
}
|
||||||
|
|
||||||
// For now, we'll store a simplified handler representation
|
/// 一度だけのハンドラー登録
|
||||||
// In a full implementation, this would need proper IntentHandler integration
|
pub fn on_once(&self, intent_name: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||||
let mut handlers = self.handlers.write().unwrap();
|
let intent_str = intent_name.to_string_box().value;
|
||||||
handlers.insert(intent_str, handler);
|
self.register_handler_internal(&intent_str, &handler, true)
|
||||||
Box::new(BoolBox::new(true))
|
}
|
||||||
|
|
||||||
|
/// ハンドラー解除(intentの全ハンドラー無効化)
|
||||||
|
pub fn off(&self, intent_name: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||||
|
let intent_str = intent_name.to_string_box().value;
|
||||||
|
if let Ok(mut flags) = self.handler_flags.write() {
|
||||||
|
if let Some(v) = flags.get_mut(&intent_str) {
|
||||||
|
for f in v.iter() { f.store(false, Ordering::SeqCst); }
|
||||||
|
v.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 登録ハンドラ保存も削除
|
||||||
|
let _ = self.handlers.write().unwrap().remove(&intent_str);
|
||||||
|
Box::new(ResultBox::new_ok(Box::new(BoolBox::new(true))))
|
||||||
}
|
}
|
||||||
/// ノードが到達可能かチェック
|
/// ノードが到達可能かチェック
|
||||||
pub fn is_reachable(&self, node_id: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
pub fn is_reachable(&self, node_id: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||||
@ -147,6 +233,37 @@ impl P2PBox {
|
|||||||
let transport = self.transport.read().unwrap();
|
let transport = self.transport.read().unwrap();
|
||||||
Box::new(StringBox::new(transport.transport_type().to_string()))
|
Box::new(StringBox::new(transport.transport_type().to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// デバッグ: 既知ノード一覧(InProcessのみ対応)
|
||||||
|
pub fn debug_nodes(&self) -> Box<dyn NyashBox> {
|
||||||
|
let transport = self.transport.read().unwrap();
|
||||||
|
if let Some(list) = transport.debug_list_nodes() {
|
||||||
|
Box::new(StringBox::new(list.join(",")))
|
||||||
|
} else {
|
||||||
|
Box::new(StringBox::new("<unsupported>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn debug_bus_id(&self) -> Box<dyn NyashBox> {
|
||||||
|
let transport = self.transport.read().unwrap();
|
||||||
|
if let Some(id) = transport.debug_bus_id() {
|
||||||
|
Box::new(StringBox::new(id))
|
||||||
|
} else {
|
||||||
|
Box::new(StringBox::new("<unsupported>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 最後に受信したfromを取得(ループバック検証用)
|
||||||
|
pub fn get_last_from(&self) -> Box<dyn NyashBox> {
|
||||||
|
let v = self.last_from.read().unwrap().clone().unwrap_or_default();
|
||||||
|
Box::new(StringBox::new(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 最後に受信したIntent名を取得(ループバック検証用)
|
||||||
|
pub fn get_last_intent_name(&self) -> Box<dyn NyashBox> {
|
||||||
|
let v = self.last_intent_name.read().unwrap().clone().unwrap_or_default();
|
||||||
|
Box::new(StringBox::new(v))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -156,9 +273,18 @@ impl NyashBox for P2PBox {
|
|||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 仮実装: clone_boxと同じ(後で修正)
|
|
||||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||||
self.clone_box()
|
// Share underlying transport and state via Arc clones
|
||||||
|
let node_id_val = self.node_id.read().unwrap().clone();
|
||||||
|
Box::new(P2PBox {
|
||||||
|
base: BoxBase::new(),
|
||||||
|
node_id: RwLock::new(node_id_val),
|
||||||
|
transport: Arc::clone(&self.transport),
|
||||||
|
handlers: Arc::clone(&self.handlers),
|
||||||
|
handler_flags: Arc::clone(&self.handler_flags),
|
||||||
|
last_from: Arc::clone(&self.last_from),
|
||||||
|
last_intent_name: Arc::clone(&self.last_intent_name),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string_box(&self) -> StringBox {
|
fn to_string_box(&self) -> StringBox {
|
||||||
@ -209,3 +335,23 @@ impl std::fmt::Display for P2PBox {
|
|||||||
self.fmt_box(f)
|
self.fmt_box(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn self_ping_sets_last_fields() {
|
||||||
|
let p = P2PBox::new("alice".to_string(), TransportKind::InProcess);
|
||||||
|
let intent = IntentBox::new("ping".to_string(), serde_json::json!({}));
|
||||||
|
let res = p.send(Box::new(StringBox::new("alice".to_string())), Box::new(intent));
|
||||||
|
// Ensure Ok
|
||||||
|
if let Some(r) = res.as_any().downcast_ref::<ResultBox>() {
|
||||||
|
assert!(matches!(r, ResultBox::Ok(_)));
|
||||||
|
} else {
|
||||||
|
panic!("send did not return ResultBox");
|
||||||
|
}
|
||||||
|
assert_eq!(p.get_last_from().to_string_box().value, "alice".to_string());
|
||||||
|
assert_eq!(p.get_last_intent_name().to_string_box().value, "ping".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
63
src/interpreter/calls.rs
Normal file
63
src/interpreter/calls.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
//! Call helpers: centralizes call paths (PluginHost, functions)
|
||||||
|
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
use crate::box_trait::{NyashBox, VoidBox};
|
||||||
|
use super::{NyashInterpreter, RuntimeError};
|
||||||
|
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
|
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
||||||
|
|
||||||
|
impl NyashInterpreter {
|
||||||
|
/// Invoke a method on a PluginBoxV2 via PluginHost facade.
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
|
pub(crate) fn call_plugin_method(
|
||||||
|
&mut self,
|
||||||
|
plugin_box: &PluginBoxV2,
|
||||||
|
method: &str,
|
||||||
|
arg_nodes: &[ASTNode],
|
||||||
|
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||||
|
if plugin_box.is_finalized() {
|
||||||
|
return Err(RuntimeError::RuntimeFailure { message: format!("Use after fini: {}", plugin_box.box_type) });
|
||||||
|
}
|
||||||
|
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
||||||
|
for arg in arg_nodes {
|
||||||
|
arg_values.push(self.execute_expression(arg)?);
|
||||||
|
}
|
||||||
|
let host_guard = crate::runtime::get_global_plugin_host();
|
||||||
|
let host = host_guard.read().map_err(|_| RuntimeError::RuntimeFailure { message: "Plugin host lock poisoned".into() })?;
|
||||||
|
match host.invoke_instance_method(&plugin_box.box_type, method, plugin_box.instance_id(), &arg_values) {
|
||||||
|
Ok(Some(result_box)) => Ok(result_box),
|
||||||
|
Ok(None) => Ok(Box::new(VoidBox::new())),
|
||||||
|
Err(e) => Err(RuntimeError::RuntimeFailure { message: format!("Plugin method {} failed: {:?}", method, e) }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a plugin box by type with arguments evaluated from AST.
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
|
pub(crate) fn create_plugin_box(
|
||||||
|
&mut self,
|
||||||
|
box_type: &str,
|
||||||
|
arg_nodes: &[ASTNode],
|
||||||
|
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||||
|
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
||||||
|
for arg in arg_nodes {
|
||||||
|
arg_values.push(self.execute_expression(arg)?);
|
||||||
|
}
|
||||||
|
let host_guard = crate::runtime::get_global_plugin_host();
|
||||||
|
let host = host_guard.read().map_err(|_| RuntimeError::RuntimeFailure { message: "Plugin host lock poisoned".into() })?;
|
||||||
|
host.create_box(box_type, &arg_values)
|
||||||
|
.map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to construct plugin '{}': {:?}", box_type, e) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a given box type is provided by plugins (per current config).
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
|
pub(crate) fn is_plugin_box_type(&self, box_type: &str) -> bool {
|
||||||
|
let host_guard = crate::runtime::get_global_plugin_host();
|
||||||
|
if let Ok(host) = host_guard.read() {
|
||||||
|
if let Some(cfg) = host.config_ref() {
|
||||||
|
return cfg.find_library_for_box(box_type).is_some();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,7 +5,7 @@
|
|||||||
* Everything is Box哲学に基づくAST実行エンジン
|
* Everything is Box哲学に基づくAST実行エンジン
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::ast::{ASTNode, Span};
|
use crate::ast::ASTNode;
|
||||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox, SharedNyashBox};
|
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox, SharedNyashBox};
|
||||||
use crate::instance_v2::InstanceBox;
|
use crate::instance_v2::InstanceBox;
|
||||||
use crate::parser::ParseError;
|
use crate::parser::ParseError;
|
||||||
@ -14,13 +14,13 @@ use crate::runtime::{NyashRuntime, NyashRuntimeBuilder};
|
|||||||
use crate::box_factory::BoxFactory;
|
use crate::box_factory::BoxFactory;
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use thiserror::Error;
|
|
||||||
use super::{ControlFlow, BoxDeclaration, ConstructorContext, StaticBoxDefinition, StaticBoxState};
|
use super::{ControlFlow, BoxDeclaration, ConstructorContext, StaticBoxDefinition, StaticBoxState};
|
||||||
|
use super::{RuntimeError, SharedState};
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
// ファイルロガー(expressions.rsと同じ)
|
// ファイルロガー(expressions.rsと同じ)
|
||||||
fn debug_log(msg: &str) {
|
pub(crate) fn debug_log(msg: &str) {
|
||||||
if let Ok(mut file) = OpenOptions::new()
|
if let Ok(mut file) = OpenOptions::new()
|
||||||
.create(true)
|
.create(true)
|
||||||
.append(true)
|
.append(true)
|
||||||
@ -31,172 +31,19 @@ fn debug_log(msg: &str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conditional debug macro - only outputs if NYASH_DEBUG=1 environment variable is set
|
// Conditional debug macro - unified with utils::debug_on()
|
||||||
macro_rules! debug_trace {
|
macro_rules! debug_trace {
|
||||||
($($arg:tt)*) => {
|
($($arg:tt)*) => {
|
||||||
if std::env::var("NYASH_DEBUG").unwrap_or_default() == "1" {
|
if crate::interpreter::utils::debug_on() { eprintln!($($arg)*); }
|
||||||
eprintln!($($arg)*);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 実行時エラー
|
macro_rules! idebug {
|
||||||
#[derive(Error, Debug)]
|
($($arg:tt)*) => {
|
||||||
pub enum RuntimeError {
|
if crate::interpreter::utils::debug_on() { eprintln!($($arg)*); }
|
||||||
#[error("Undefined variable '{name}'")]
|
};
|
||||||
UndefinedVariable { name: String },
|
|
||||||
|
|
||||||
#[error("Undefined function '{name}'")]
|
|
||||||
UndefinedFunction { name: String },
|
|
||||||
|
|
||||||
#[error("Undefined class '{name}'")]
|
|
||||||
UndefinedClass { name: String },
|
|
||||||
|
|
||||||
#[error("Type error: {message}")]
|
|
||||||
TypeError { message: String },
|
|
||||||
|
|
||||||
#[error("Invalid operation: {message}")]
|
|
||||||
InvalidOperation { message: String },
|
|
||||||
|
|
||||||
#[error("Break outside of loop")]
|
|
||||||
BreakOutsideLoop,
|
|
||||||
|
|
||||||
#[error("Return outside of function")]
|
|
||||||
ReturnOutsideFunction,
|
|
||||||
|
|
||||||
#[error("Uncaught exception")]
|
|
||||||
UncaughtException,
|
|
||||||
|
|
||||||
#[error("Parse error: {0}")]
|
|
||||||
ParseError(#[from] ParseError),
|
|
||||||
|
|
||||||
#[error("Environment error: {0}")]
|
|
||||||
EnvironmentError(String),
|
|
||||||
|
|
||||||
// === 🔥 Enhanced Errors with Span Information ===
|
|
||||||
|
|
||||||
#[error("Undefined variable '{name}' at {span}")]
|
|
||||||
UndefinedVariableAt { name: String, span: Span },
|
|
||||||
|
|
||||||
#[error("Type error: {message} at {span}")]
|
|
||||||
TypeErrorAt { message: String, span: Span },
|
|
||||||
|
|
||||||
#[error("Invalid operation: {message} at {span}")]
|
|
||||||
InvalidOperationAt { message: String, span: Span },
|
|
||||||
|
|
||||||
#[error("Break outside of loop at {span}")]
|
|
||||||
BreakOutsideLoopAt { span: Span },
|
|
||||||
|
|
||||||
#[error("Return outside of function at {span}")]
|
|
||||||
ReturnOutsideFunctionAt { span: Span },
|
|
||||||
|
|
||||||
#[error("Runtime failure: {message}")]
|
|
||||||
RuntimeFailure { message: String },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RuntimeError {
|
|
||||||
/// エラーの詳細な文脈付きメッセージを生成
|
|
||||||
pub fn detailed_message(&self, source: Option<&str>) -> String {
|
|
||||||
match self {
|
|
||||||
// Enhanced errors with span information
|
|
||||||
RuntimeError::UndefinedVariableAt { name, span } => {
|
|
||||||
let mut msg = format!("⚠️ Undefined variable '{}'", name);
|
|
||||||
if let Some(src) = source {
|
|
||||||
msg.push('\n');
|
|
||||||
msg.push_str(&span.error_context(src));
|
|
||||||
} else {
|
|
||||||
msg.push_str(&format!(" at {}", span));
|
|
||||||
}
|
|
||||||
msg
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeError::TypeErrorAt { message, span } => {
|
|
||||||
let mut msg = format!("⚠️ Type error: {}", message);
|
|
||||||
if let Some(src) = source {
|
|
||||||
msg.push('\n');
|
|
||||||
msg.push_str(&span.error_context(src));
|
|
||||||
} else {
|
|
||||||
msg.push_str(&format!(" at {}", span));
|
|
||||||
}
|
|
||||||
msg
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeError::InvalidOperationAt { message, span } => {
|
|
||||||
let mut msg = format!("⚠️ Invalid operation: {}", message);
|
|
||||||
if let Some(src) = source {
|
|
||||||
msg.push('\n');
|
|
||||||
msg.push_str(&span.error_context(src));
|
|
||||||
} else {
|
|
||||||
msg.push_str(&format!(" at {}", span));
|
|
||||||
}
|
|
||||||
msg
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeError::BreakOutsideLoopAt { span } => {
|
|
||||||
let mut msg = "⚠️ Break statement outside of loop".to_string();
|
|
||||||
if let Some(src) = source {
|
|
||||||
msg.push('\n');
|
|
||||||
msg.push_str(&span.error_context(src));
|
|
||||||
} else {
|
|
||||||
msg.push_str(&format!(" at {}", span));
|
|
||||||
}
|
|
||||||
msg
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeError::ReturnOutsideFunctionAt { span } => {
|
|
||||||
let mut msg = "⚠️ Return statement outside of function".to_string();
|
|
||||||
if let Some(src) = source {
|
|
||||||
msg.push('\n');
|
|
||||||
msg.push_str(&span.error_context(src));
|
|
||||||
} else {
|
|
||||||
msg.push_str(&format!(" at {}", span));
|
|
||||||
}
|
|
||||||
msg
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback for old error variants without span
|
|
||||||
_ => format!("⚠️ {}", self),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// スレッド間で共有される状態
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SharedState {
|
|
||||||
/// 🌍 GlobalBox - すべてのトップレベル関数とグローバル変数を管理
|
|
||||||
pub global_box: Arc<Mutex<InstanceBox>>,
|
|
||||||
|
|
||||||
/// Box宣言のレジストリ(読み込みが多いのでRwLock)
|
|
||||||
pub box_declarations: Arc<RwLock<HashMap<String, BoxDeclaration>>>,
|
|
||||||
|
|
||||||
/// 🔥 静的関数のレジストリ(読み込みが多いのでRwLock)
|
|
||||||
pub static_functions: Arc<RwLock<HashMap<String, HashMap<String, ASTNode>>>>,
|
|
||||||
|
|
||||||
/// 🔥 Static Box定義レジストリ(遅延初期化用)
|
|
||||||
pub static_box_definitions: Arc<RwLock<HashMap<String, StaticBoxDefinition>>>,
|
|
||||||
|
|
||||||
/// 読み込み済みファイル(重複防止)
|
|
||||||
pub included_files: Arc<Mutex<HashSet<String>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SharedState {
|
|
||||||
/// 新しい共有状態を作成
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let global_box = InstanceBox::new(
|
|
||||||
"Global".to_string(),
|
|
||||||
vec![], // フィールド名(空から始める)
|
|
||||||
HashMap::new(), // メソッド(グローバル関数)
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
global_box: Arc::new(Mutex::new(global_box)),
|
|
||||||
box_declarations: Arc::new(RwLock::new(HashMap::new())),
|
|
||||||
static_functions: Arc::new(RwLock::new(HashMap::new())),
|
|
||||||
static_box_definitions: Arc::new(RwLock::new(HashMap::new())),
|
|
||||||
included_files: Arc::new(Mutex::new(HashSet::new())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Nyashインタープリター - AST実行エンジン
|
/// Nyashインタープリター - AST実行エンジン
|
||||||
pub struct NyashInterpreter {
|
pub struct NyashInterpreter {
|
||||||
@ -252,7 +99,7 @@ impl NyashInterpreter {
|
|||||||
reg.register(udf);
|
reg.register(udf);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
let mut this = Self {
|
||||||
shared,
|
shared,
|
||||||
local_vars: HashMap::new(),
|
local_vars: HashMap::new(),
|
||||||
outbox_vars: HashMap::new(),
|
outbox_vars: HashMap::new(),
|
||||||
@ -263,7 +110,12 @@ impl NyashInterpreter {
|
|||||||
stdlib: None, // 遅延初期化
|
stdlib: None, // 遅延初期化
|
||||||
runtime,
|
runtime,
|
||||||
discard_context: false,
|
discard_context: false,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// Register MethodBox invoker once (idempotent)
|
||||||
|
self::register_methodbox_invoker();
|
||||||
|
|
||||||
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// グループ構成を指定して新しいインタープリターを作成
|
/// グループ構成を指定して新しいインタープリターを作成
|
||||||
@ -286,7 +138,7 @@ impl NyashInterpreter {
|
|||||||
reg.register(udf);
|
reg.register(udf);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
let mut this = Self {
|
||||||
shared,
|
shared,
|
||||||
local_vars: HashMap::new(),
|
local_vars: HashMap::new(),
|
||||||
outbox_vars: HashMap::new(),
|
outbox_vars: HashMap::new(),
|
||||||
@ -297,7 +149,9 @@ impl NyashInterpreter {
|
|||||||
stdlib: None,
|
stdlib: None,
|
||||||
runtime,
|
runtime,
|
||||||
discard_context: false,
|
discard_context: false,
|
||||||
}
|
};
|
||||||
|
self::register_methodbox_invoker();
|
||||||
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 共有状態から新しいインタープリターを作成(非同期実行用)
|
/// 共有状態から新しいインタープリターを作成(非同期実行用)
|
||||||
@ -316,7 +170,7 @@ impl NyashInterpreter {
|
|||||||
reg.register(udf);
|
reg.register(udf);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
let mut this = Self {
|
||||||
shared,
|
shared,
|
||||||
local_vars: HashMap::new(),
|
local_vars: HashMap::new(),
|
||||||
outbox_vars: HashMap::new(),
|
outbox_vars: HashMap::new(),
|
||||||
@ -327,7 +181,9 @@ impl NyashInterpreter {
|
|||||||
stdlib: None, // 遅延初期化
|
stdlib: None, // 遅延初期化
|
||||||
runtime,
|
runtime,
|
||||||
discard_context: false,
|
discard_context: false,
|
||||||
}
|
};
|
||||||
|
self::register_methodbox_invoker();
|
||||||
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 共有状態+グループ構成を指定して新しいインタープリターを作成(非同期実行用)
|
/// 共有状態+グループ構成を指定して新しいインタープリターを作成(非同期実行用)
|
||||||
@ -347,7 +203,7 @@ impl NyashInterpreter {
|
|||||||
reg.register(udf);
|
reg.register(udf);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
let mut this = Self {
|
||||||
shared,
|
shared,
|
||||||
local_vars: HashMap::new(),
|
local_vars: HashMap::new(),
|
||||||
outbox_vars: HashMap::new(),
|
outbox_vars: HashMap::new(),
|
||||||
@ -358,19 +214,11 @@ impl NyashInterpreter {
|
|||||||
stdlib: None,
|
stdlib: None,
|
||||||
runtime,
|
runtime,
|
||||||
discard_context: false,
|
discard_context: false,
|
||||||
}
|
};
|
||||||
|
self::register_methodbox_invoker();
|
||||||
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ASTを実行
|
|
||||||
pub fn execute(&mut self, ast: ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
|
||||||
debug_log("=== NYASH EXECUTION START ===");
|
|
||||||
let result = self.execute_node(&ast);
|
|
||||||
if let Err(ref e) = result {
|
|
||||||
eprintln!("❌ Interpreter error: {}", e);
|
|
||||||
}
|
|
||||||
debug_log("=== NYASH EXECUTION END ===");
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register an additional BoxFactory into this interpreter's runtime registry.
|
/// Register an additional BoxFactory into this interpreter's runtime registry.
|
||||||
/// This allows tests or embedders to inject custom factories without globals.
|
/// This allows tests or embedders to inject custom factories without globals.
|
||||||
@ -380,75 +228,6 @@ impl NyashInterpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ノードを実行
|
|
||||||
fn execute_node(&mut self, node: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
|
||||||
match node {
|
|
||||||
ASTNode::Program { statements, .. } => {
|
|
||||||
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
|
|
||||||
|
|
||||||
let last = statements.len().saturating_sub(1);
|
|
||||||
for (i, statement) in statements.iter().enumerate() {
|
|
||||||
let prev = self.discard_context;
|
|
||||||
self.discard_context = i != last; // 最終文以外は値が破棄される
|
|
||||||
result = self.execute_statement(statement)?;
|
|
||||||
self.discard_context = prev;
|
|
||||||
|
|
||||||
// 制御フローチェック
|
|
||||||
match &self.control_flow {
|
|
||||||
ControlFlow::Break => {
|
|
||||||
return Err(RuntimeError::BreakOutsideLoop);
|
|
||||||
}
|
|
||||||
ControlFlow::Return(_) => {
|
|
||||||
return Err(RuntimeError::ReturnOutsideFunction);
|
|
||||||
}
|
|
||||||
ControlFlow::Throw(_) => {
|
|
||||||
return Err(RuntimeError::UncaughtException);
|
|
||||||
}
|
|
||||||
ControlFlow::None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🎯 Static Box Main パターン - main()メソッドの自動実行
|
|
||||||
let has_main_method = {
|
|
||||||
if let Ok(definitions) = self.shared.static_box_definitions.read() {
|
|
||||||
if let Some(main_definition) = definitions.get("Main") {
|
|
||||||
main_definition.methods.contains_key("main")
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if has_main_method {
|
|
||||||
// Main static boxを初期化
|
|
||||||
self.ensure_static_box_initialized("Main")?;
|
|
||||||
|
|
||||||
// Main.main() を呼び出し
|
|
||||||
let main_call_ast = ASTNode::MethodCall {
|
|
||||||
object: Box::new(ASTNode::FieldAccess {
|
|
||||||
object: Box::new(ASTNode::Variable {
|
|
||||||
name: "statics".to_string(),
|
|
||||||
span: crate::ast::Span::unknown(),
|
|
||||||
}),
|
|
||||||
field: "Main".to_string(),
|
|
||||||
span: crate::ast::Span::unknown(),
|
|
||||||
}),
|
|
||||||
method: "main".to_string(),
|
|
||||||
arguments: vec![],
|
|
||||||
span: crate::ast::Span::unknown(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// main()の戻り値を最終結果として使用
|
|
||||||
result = self.execute_statement(&main_call_ast)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
_ => self.execute_statement(node),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== 🌍 GlobalBox変数解決システム ==========
|
// ========== 🌍 GlobalBox変数解決システム ==========
|
||||||
|
|
||||||
@ -515,7 +294,7 @@ impl NyashInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 6. エラー:見つからない
|
// 6. エラー:見つからない
|
||||||
eprintln!("🔍 DEBUG: '{}' not found anywhere!", name);
|
idebug!("🔍 DEBUG: '{}' not found anywhere!", name);
|
||||||
Err(RuntimeError::UndefinedVariable {
|
Err(RuntimeError::UndefinedVariable {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
})
|
})
|
||||||
@ -609,7 +388,7 @@ impl NyashInterpreter {
|
|||||||
// ユーザー定義Box(InstanceBox)の場合
|
// ユーザー定義Box(InstanceBox)の場合
|
||||||
if let Some(instance) = (**value).as_any().downcast_ref::<InstanceBox>() {
|
if let Some(instance) = (**value).as_any().downcast_ref::<InstanceBox>() {
|
||||||
let _ = instance.fini();
|
let _ = instance.fini();
|
||||||
eprintln!("🔄 Scope exit: Called fini() on local variable '{}' (InstanceBox)", name);
|
idebug!("🔄 Scope exit: Called fini() on local variable '{}' (InstanceBox)", name);
|
||||||
}
|
}
|
||||||
// プラグインBoxは共有ハンドルの可能性が高いため自動finiしない(明示呼び出しのみ)
|
// プラグインBoxは共有ハンドルの可能性が高いため自動finiしない(明示呼び出しのみ)
|
||||||
// ビルトインBoxは元々finiメソッドを持たないので呼ばない
|
// ビルトインBoxは元々finiメソッドを持たないので呼ばない
|
||||||
@ -642,7 +421,7 @@ impl NyashInterpreter {
|
|||||||
// ユーザー定義Box(InstanceBox)の場合
|
// ユーザー定義Box(InstanceBox)の場合
|
||||||
if let Some(instance) = (**value).as_any().downcast_ref::<InstanceBox>() {
|
if let Some(instance) = (**value).as_any().downcast_ref::<InstanceBox>() {
|
||||||
let _ = instance.fini();
|
let _ = instance.fini();
|
||||||
eprintln!("🔄 Scope exit: Called fini() on outbox variable '{}' (InstanceBox)", name);
|
idebug!("🔄 Scope exit: Called fini() on outbox variable '{}' (InstanceBox)", name);
|
||||||
}
|
}
|
||||||
// プラグインBoxは共有ハンドルの可能性が高いため自動finiしない
|
// プラグインBoxは共有ハンドルの可能性が高いため自動finiしない
|
||||||
// ビルトインBoxは元々finiメソッドを持たないので呼ばない(要修正)
|
// ビルトインBoxは元々finiメソッドを持たないので呼ばない(要修正)
|
||||||
@ -825,7 +604,7 @@ impl NyashInterpreter {
|
|||||||
initialization_state: StaticBoxState::NotInitialized,
|
initialization_state: StaticBoxState::NotInitialized,
|
||||||
};
|
};
|
||||||
|
|
||||||
eprintln!("🔥 Static Box '{}' definition registered in statics namespace", name);
|
idebug!("🔥 Static Box '{}' definition registered in statics namespace", name);
|
||||||
self.register_static_box(definition)
|
self.register_static_box(definition)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -921,7 +700,7 @@ impl NyashInterpreter {
|
|||||||
|
|
||||||
// 既に存在する場合はスキップ
|
// 既に存在する場合はスキップ
|
||||||
if global_box.get_field("statics").is_some() {
|
if global_box.get_field("statics").is_some() {
|
||||||
eprintln!("🌍 statics namespace already exists - skipping creation");
|
idebug!("🌍 statics namespace already exists - skipping creation");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -939,7 +718,7 @@ impl NyashInterpreter {
|
|||||||
fields_locked.insert("statics".to_string(), Arc::new(statics_box));
|
fields_locked.insert("statics".to_string(), Arc::new(statics_box));
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!("🌍 statics namespace created in GlobalBox successfully");
|
idebug!("🌍 statics namespace created in GlobalBox successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -969,7 +748,7 @@ impl NyashInterpreter {
|
|||||||
fields_locked.insert(name.to_string(), Arc::new(instance));
|
fields_locked.insert(name.to_string(), Arc::new(instance));
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!("🔥 Static box '{}' instance registered in statics namespace", name);
|
idebug!("🔥 Static box '{}' instance registered in statics namespace", name);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -984,7 +763,7 @@ impl NyashInterpreter {
|
|||||||
|
|
||||||
/// 🔗 Trigger weak reference invalidation (expert-validated implementation)
|
/// 🔗 Trigger weak reference invalidation (expert-validated implementation)
|
||||||
pub(super) fn trigger_weak_reference_invalidation(&mut self, target_info: &str) {
|
pub(super) fn trigger_weak_reference_invalidation(&mut self, target_info: &str) {
|
||||||
eprintln!("🔗 DEBUG: Registering invalidation for: {}", target_info);
|
idebug!("🔗 DEBUG: Registering invalidation for: {}", target_info);
|
||||||
|
|
||||||
// Extract actual object ID from target_info string
|
// Extract actual object ID from target_info string
|
||||||
// Format: "<ClassName instance #ID>" -> extract ID
|
// Format: "<ClassName instance #ID>" -> extract ID
|
||||||
@ -996,17 +775,89 @@ impl NyashInterpreter {
|
|||||||
|
|
||||||
if let Ok(id) = clean_id_str.parse::<u64>() {
|
if let Ok(id) = clean_id_str.parse::<u64>() {
|
||||||
self.invalidated_ids.lock().unwrap().insert(id);
|
self.invalidated_ids.lock().unwrap().insert(id);
|
||||||
eprintln!("🔗 DEBUG: Object with ID {} marked as invalidated", id);
|
idebug!("🔗 DEBUG: Object with ID {} marked as invalidated", id);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("🔗 DEBUG: Failed to parse ID from: {}", clean_id_str);
|
idebug!("🔗 DEBUG: Failed to parse ID from: {}", clean_id_str);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback for non-standard target_info format
|
// Fallback for non-standard target_info format
|
||||||
eprintln!("🔗 DEBUG: No ID found in target_info, using fallback");
|
idebug!("🔗 DEBUG: No ID found in target_info, using fallback");
|
||||||
if target_info.contains("Parent") {
|
if target_info.contains("Parent") {
|
||||||
self.invalidated_ids.lock().unwrap().insert(999); // Fallback marker
|
self.invalidated_ids.lock().unwrap().insert(999); // Fallback marker
|
||||||
eprintln!("🔗 DEBUG: Parent objects marked as invalidated (fallback ID 999)");
|
idebug!("🔗 DEBUG: Parent objects marked as invalidated (fallback ID 999)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ==== MethodBox Invoker Bridge ==========================================
|
||||||
|
fn register_methodbox_invoker() {
|
||||||
|
use crate::method_box::{MethodBox, MethodInvoker, FunctionDefinition, set_method_invoker};
|
||||||
|
use crate::box_trait::{VoidBox};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
struct SimpleMethodInvoker;
|
||||||
|
impl MethodInvoker for SimpleMethodInvoker {
|
||||||
|
fn invoke(&self, method: &MethodBox, args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, String> {
|
||||||
|
// 1) 取得: メソッド定義
|
||||||
|
let def: FunctionDefinition = if let Some(def) = &method.method_def {
|
||||||
|
def.clone()
|
||||||
|
} else {
|
||||||
|
let inst_guard = method.get_instance();
|
||||||
|
let inst_locked = inst_guard.lock().map_err(|_| "MethodBox instance lock poisoned".to_string())?;
|
||||||
|
if let Some(inst) = inst_locked.as_any().downcast_ref::<InstanceBox>() {
|
||||||
|
if let Some(ast) = inst.get_method(&method.method_name) {
|
||||||
|
if let ASTNode::FunctionDeclaration { name, params, body, is_static, .. } = ast {
|
||||||
|
FunctionDefinition { name: name.clone(), params: params.clone(), body: body.clone(), is_static: *is_static }
|
||||||
|
} else {
|
||||||
|
return Err("Method AST is not a function declaration".to_string());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(format!("Method '{}' not found on instance", method.method_name));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err("MethodBox instance is not an InstanceBox".to_string());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2) 新しいInterpreterでメソッド本体を実行(簡易)
|
||||||
|
let mut interp = NyashInterpreter::new();
|
||||||
|
// me = instance
|
||||||
|
let me_box = {
|
||||||
|
let inst_guard = method.get_instance();
|
||||||
|
let inst_locked = inst_guard.lock().map_err(|_| "MethodBox instance lock poisoned".to_string())?;
|
||||||
|
inst_locked.clone_or_share()
|
||||||
|
};
|
||||||
|
interp.declare_local_variable("me", me_box);
|
||||||
|
|
||||||
|
// 引数をローカルへ
|
||||||
|
if def.params.len() != args.len() {
|
||||||
|
return Err(format!("Argument mismatch: expected {}, got {}", def.params.len(), args.len()));
|
||||||
|
}
|
||||||
|
for (p, v) in def.params.iter().zip(args.into_iter()) {
|
||||||
|
interp.declare_local_variable(p, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 本体実行
|
||||||
|
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
|
||||||
|
for st in &def.body {
|
||||||
|
match interp.execute_statement(st) {
|
||||||
|
Ok(val) => {
|
||||||
|
result = val;
|
||||||
|
if let super::ControlFlow::Return(ret) = &interp.control_flow {
|
||||||
|
result = ret.clone_box();
|
||||||
|
interp.control_flow = super::ControlFlow::None;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format!("Invoke error: {:?}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登録(複数回new()されてもOnceCellなので一度だけ)
|
||||||
|
let _ = set_method_invoker(Arc::new(SimpleMethodInvoker));
|
||||||
|
}
|
||||||
|
|||||||
123
src/interpreter/errors.rs
Normal file
123
src/interpreter/errors.rs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
use crate::ast::Span;
|
||||||
|
use crate::parser::ParseError;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// 実行時エラー
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum RuntimeError {
|
||||||
|
#[error("Undefined variable '{name}'")]
|
||||||
|
UndefinedVariable { name: String },
|
||||||
|
|
||||||
|
#[error("Undefined function '{name}'")]
|
||||||
|
UndefinedFunction { name: String },
|
||||||
|
|
||||||
|
#[error("Undefined class '{name}'")]
|
||||||
|
UndefinedClass { name: String },
|
||||||
|
|
||||||
|
#[error("Type error: {message}")]
|
||||||
|
TypeError { message: String },
|
||||||
|
|
||||||
|
#[error("Invalid operation: {message}")]
|
||||||
|
InvalidOperation { message: String },
|
||||||
|
|
||||||
|
#[error("Break outside of loop")]
|
||||||
|
BreakOutsideLoop,
|
||||||
|
|
||||||
|
#[error("Return outside of function")]
|
||||||
|
ReturnOutsideFunction,
|
||||||
|
|
||||||
|
#[error("Uncaught exception")]
|
||||||
|
UncaughtException,
|
||||||
|
|
||||||
|
#[error("Parse error: {0}")]
|
||||||
|
ParseError(#[from] ParseError),
|
||||||
|
|
||||||
|
#[error("Environment error: {0}")]
|
||||||
|
EnvironmentError(String),
|
||||||
|
|
||||||
|
// === 🔥 Enhanced Errors with Span Information ===
|
||||||
|
|
||||||
|
#[error("Undefined variable '{name}' at {span}")]
|
||||||
|
UndefinedVariableAt { name: String, span: Span },
|
||||||
|
|
||||||
|
#[error("Type error: {message} at {span}")]
|
||||||
|
TypeErrorAt { message: String, span: Span },
|
||||||
|
|
||||||
|
#[error("Invalid operation: {message} at {span}")]
|
||||||
|
InvalidOperationAt { message: String, span: Span },
|
||||||
|
|
||||||
|
#[error("Break outside of loop at {span}")]
|
||||||
|
BreakOutsideLoopAt { span: Span },
|
||||||
|
|
||||||
|
#[error("Return outside of function at {span}")]
|
||||||
|
ReturnOutsideFunctionAt { span: Span },
|
||||||
|
|
||||||
|
#[error("Runtime failure: {message}")]
|
||||||
|
RuntimeFailure { message: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RuntimeError {
|
||||||
|
/// エラーの詳細な文脈付きメッセージを生成
|
||||||
|
pub fn detailed_message(&self, source: Option<&str>) -> String {
|
||||||
|
match self {
|
||||||
|
// Enhanced errors with span information
|
||||||
|
RuntimeError::UndefinedVariableAt { name, span } => {
|
||||||
|
let mut msg = format!("⚠️ Undefined variable '{}'", name);
|
||||||
|
if let Some(src) = source {
|
||||||
|
msg.push('\n');
|
||||||
|
msg.push_str(&span.error_context(src));
|
||||||
|
} else {
|
||||||
|
msg.push_str(&format!(" at {}", span));
|
||||||
|
}
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeError::TypeErrorAt { message, span } => {
|
||||||
|
let mut msg = format!("⚠️ Type error: {}", message);
|
||||||
|
if let Some(src) = source {
|
||||||
|
msg.push('\n');
|
||||||
|
msg.push_str(&span.error_context(src));
|
||||||
|
} else {
|
||||||
|
msg.push_str(&format!(" at {}", span));
|
||||||
|
}
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeError::InvalidOperationAt { message, span } => {
|
||||||
|
let mut msg = format!("⚠️ Invalid operation: {}", message);
|
||||||
|
if let Some(src) = source {
|
||||||
|
msg.push('\n');
|
||||||
|
msg.push_str(&span.error_context(src));
|
||||||
|
} else {
|
||||||
|
msg.push_str(&format!(" at {}", span));
|
||||||
|
}
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeError::BreakOutsideLoopAt { span } => {
|
||||||
|
let mut msg = "⚠️ Break statement outside of loop".to_string();
|
||||||
|
if let Some(src) = source {
|
||||||
|
msg.push('\n');
|
||||||
|
msg.push_str(&span.error_context(src));
|
||||||
|
} else {
|
||||||
|
msg.push_str(&format!(" at {}", span));
|
||||||
|
}
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeError::ReturnOutsideFunctionAt { span } => {
|
||||||
|
let mut msg = "⚠️ Return statement outside of function".to_string();
|
||||||
|
if let Some(src) = source {
|
||||||
|
msg.push('\n');
|
||||||
|
msg.push_str(&span.error_context(src));
|
||||||
|
} else {
|
||||||
|
msg.push_str(&format!(" at {}", span));
|
||||||
|
}
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for old error variants without span
|
||||||
|
_ => format!("⚠️ {}", self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/interpreter/eval.rs
Normal file
89
src/interpreter/eval.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
//! Evaluation entry points: execute program and nodes
|
||||||
|
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
use crate::box_trait::{NyashBox, VoidBox, StringBox};
|
||||||
|
use super::{NyashInterpreter, RuntimeError, ControlFlow};
|
||||||
|
|
||||||
|
impl NyashInterpreter {
|
||||||
|
/// ASTを実行
|
||||||
|
pub fn execute(&mut self, ast: ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||||
|
super::core::debug_log("=== NYASH EXECUTION START ===");
|
||||||
|
let result = self.execute_node(&ast);
|
||||||
|
if let Err(ref e) = result {
|
||||||
|
eprintln!("❌ Interpreter error: {}", e);
|
||||||
|
}
|
||||||
|
super::core::debug_log("=== NYASH EXECUTION END ===");
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ノードを実行
|
||||||
|
fn execute_node(&mut self, node: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||||
|
match node {
|
||||||
|
ASTNode::Program { statements, .. } => {
|
||||||
|
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
|
||||||
|
|
||||||
|
let last = statements.len().saturating_sub(1);
|
||||||
|
for (i, statement) in statements.iter().enumerate() {
|
||||||
|
let prev = self.discard_context;
|
||||||
|
self.discard_context = i != last; // 最終文以外は値が破棄される
|
||||||
|
result = self.execute_statement(statement)?;
|
||||||
|
self.discard_context = prev;
|
||||||
|
|
||||||
|
// 制御フローチェック
|
||||||
|
match &self.control_flow {
|
||||||
|
ControlFlow::Break => {
|
||||||
|
return Err(RuntimeError::BreakOutsideLoop);
|
||||||
|
}
|
||||||
|
ControlFlow::Return(_) => {
|
||||||
|
return Err(RuntimeError::ReturnOutsideFunction);
|
||||||
|
}
|
||||||
|
ControlFlow::Throw(_) => {
|
||||||
|
return Err(RuntimeError::UncaughtException);
|
||||||
|
}
|
||||||
|
ControlFlow::None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🎯 Static Box Main パターン - main()メソッドの自動実行
|
||||||
|
let has_main_method = {
|
||||||
|
if let Ok(definitions) = self.shared.static_box_definitions.read() {
|
||||||
|
if let Some(main_definition) = definitions.get("Main") {
|
||||||
|
main_definition.methods.contains_key("main")
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if has_main_method {
|
||||||
|
// Main static boxを初期化
|
||||||
|
self.ensure_static_box_initialized("Main")?;
|
||||||
|
|
||||||
|
// Main.main() を呼び出し
|
||||||
|
let main_call_ast = ASTNode::MethodCall {
|
||||||
|
object: Box::new(ASTNode::FieldAccess {
|
||||||
|
object: Box::new(ASTNode::Variable {
|
||||||
|
name: "statics".to_string(),
|
||||||
|
span: crate::ast::Span::unknown(),
|
||||||
|
}),
|
||||||
|
field: "Main".to_string(),
|
||||||
|
span: crate::ast::Span::unknown(),
|
||||||
|
}),
|
||||||
|
method: "main".to_string(),
|
||||||
|
arguments: vec![],
|
||||||
|
span: crate::ast::Span::unknown(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// main()の戻り値を最終結果として使用
|
||||||
|
result = self.execute_statement(&main_call_ast)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
_ => self.execute_statement(node),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ use crate::ast::ASTNode;
|
|||||||
use crate::box_trait::{NyashBox, SharedNyashBox};
|
use crate::box_trait::{NyashBox, SharedNyashBox};
|
||||||
use crate::boxes::FutureBox;
|
use crate::boxes::FutureBox;
|
||||||
use crate::instance_v2::InstanceBox;
|
use crate::instance_v2::InstanceBox;
|
||||||
use crate::interpreter::core::{NyashInterpreter, RuntimeError};
|
use crate::interpreter::{NyashInterpreter, RuntimeError};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
// Conditional debug macro - only outputs if NYASH_DEBUG=1 environment variable is set
|
// Conditional debug macro - only outputs if NYASH_DEBUG=1 environment variable is set
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use crate::ast::ASTNode;
|
|||||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox, VoidBox};
|
use crate::box_trait::{NyashBox, StringBox, IntegerBox, VoidBox};
|
||||||
use crate::boxes::{ArrayBox, MapBox, MathBox, ConsoleBox, TimeBox, RandomBox, DebugBox, SoundBox, SocketBox};
|
use crate::boxes::{ArrayBox, MapBox, MathBox, ConsoleBox, TimeBox, RandomBox, DebugBox, SoundBox, SocketBox};
|
||||||
use crate::boxes::{HTTPServerBox, HTTPRequestBox, HTTPResponseBox};
|
use crate::boxes::{HTTPServerBox, HTTPRequestBox, HTTPResponseBox};
|
||||||
use crate::interpreter::core::{NyashInterpreter, RuntimeError};
|
use crate::interpreter::{NyashInterpreter, RuntimeError};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
impl NyashInterpreter {
|
impl NyashInterpreter {
|
||||||
|
|||||||
@ -11,8 +11,15 @@ use crate::boxes::{HTTPServerBox, HTTPRequestBox, HTTPResponseBox, MathBox, Time
|
|||||||
use crate::boxes::{RandomBox, SoundBox, DebugBox};
|
use crate::boxes::{RandomBox, SoundBox, DebugBox};
|
||||||
use crate::instance_v2::InstanceBox;
|
use crate::instance_v2::InstanceBox;
|
||||||
use crate::channel_box::ChannelBox;
|
use crate::channel_box::ChannelBox;
|
||||||
use crate::interpreter::core::{NyashInterpreter, RuntimeError};
|
use crate::interpreter::{NyashInterpreter, RuntimeError};
|
||||||
use crate::interpreter::finalization;
|
use crate::interpreter::finalization;
|
||||||
|
|
||||||
|
// Debug macro gated by NYASH_DEBUG=1
|
||||||
|
macro_rules! idebug {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
if crate::interpreter::utils::debug_on() { eprintln!($($arg)*); }
|
||||||
|
};
|
||||||
|
}
|
||||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -98,24 +105,24 @@ impl NyashInterpreter {
|
|||||||
if let Some(builtin_method) = static_box.methods.get(method) {
|
if let Some(builtin_method) = static_box.methods.get(method) {
|
||||||
Some(*builtin_method) // Copyトレイトで関数ポインターをコピー
|
Some(*builtin_method) // Copyトレイトで関数ポインターをコピー
|
||||||
} else {
|
} else {
|
||||||
eprintln!("🔍 Method '{}' not found in nyashstd.{}", method, name);
|
idebug!("🔍 Method '{}' not found in nyashstd.{}", method, name);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("🔍 Static box '{}' not found in nyashstd", name);
|
idebug!("🔍 Static box '{}' not found in nyashstd", name);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("🔍 nyashstd namespace not found in stdlib");
|
idebug!("🔍 nyashstd namespace not found in stdlib");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("🔍 stdlib not initialized for method call");
|
idebug!("🔍 stdlib not initialized for method call");
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(builtin_method) = stdlib_method {
|
if let Some(builtin_method) = stdlib_method {
|
||||||
eprintln!("🌟 Calling nyashstd method: {}.{}", name, method);
|
idebug!("🌟 Calling nyashstd method: {}.{}", name, method);
|
||||||
|
|
||||||
// 引数を評価
|
// 引数を評価
|
||||||
let mut arg_values = Vec::new();
|
let mut arg_values = Vec::new();
|
||||||
@ -125,13 +132,13 @@ impl NyashInterpreter {
|
|||||||
|
|
||||||
// 標準ライブラリのメソッドを実行
|
// 標準ライブラリのメソッドを実行
|
||||||
let result = builtin_method(&arg_values)?;
|
let result = builtin_method(&arg_values)?;
|
||||||
eprintln!("✅ nyashstd method completed: {}.{}", name, method);
|
idebug!("✅ nyashstd method completed: {}.{}", name, method);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔥 ユーザー定義のStatic Boxメソッドチェック
|
// 🔥 ユーザー定義のStatic Boxメソッドチェック
|
||||||
if self.is_static_box(name) {
|
if self.is_static_box(name) {
|
||||||
eprintln!("🔍 Checking user-defined static box: {}", name);
|
idebug!("🔍 Checking user-defined static box: {}", name);
|
||||||
|
|
||||||
// Static Boxの初期化を確実に実行
|
// Static Boxの初期化を確実に実行
|
||||||
self.ensure_static_box_initialized(name)?;
|
self.ensure_static_box_initialized(name)?;
|
||||||
@ -175,7 +182,7 @@ impl NyashInterpreter {
|
|||||||
}
|
}
|
||||||
}; // lockはここで解放される
|
}; // lockはここで解放される
|
||||||
|
|
||||||
eprintln!("🌟 Calling static box method: {}.{}", name, method);
|
idebug!("🌟 Calling static box method: {}.{}", name, method);
|
||||||
|
|
||||||
// 引数を評価
|
// 引数を評価
|
||||||
let mut arg_values = Vec::new();
|
let mut arg_values = Vec::new();
|
||||||
@ -213,7 +220,7 @@ impl NyashInterpreter {
|
|||||||
// local変数スタックを復元
|
// local変数スタックを復元
|
||||||
self.restore_local_vars(saved_locals);
|
self.restore_local_vars(saved_locals);
|
||||||
|
|
||||||
eprintln!("✅ Static box method completed: {}.{}", name, method);
|
idebug!("✅ Static box method completed: {}.{}", name, method);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,102 +228,11 @@ impl NyashInterpreter {
|
|||||||
|
|
||||||
// オブジェクトを評価(通常のメソッド呼び出し)
|
// オブジェクトを評価(通常のメソッド呼び出し)
|
||||||
let obj_value = self.execute_expression(object)?;
|
let obj_value = self.execute_expression(object)?;
|
||||||
eprintln!("🔍 DEBUG: execute_method_call - object type: {}, method: {}", obj_value.type_name(), method);
|
idebug!("🔍 DEBUG: execute_method_call - object type: {}, method: {}", obj_value.type_name(), method);
|
||||||
|
|
||||||
// StringBox method calls
|
// Builtin dispatch (centralized)
|
||||||
eprintln!("🔍 DEBUG: Checking StringBox downcast for type: {}", obj_value.type_name());
|
if let Some(res) = self.dispatch_builtin_method(&obj_value, method, arguments) {
|
||||||
if let Some(string_box) = obj_value.as_any().downcast_ref::<StringBox>() {
|
return res;
|
||||||
eprintln!("🔍 DEBUG: StringBox detected, calling execute_string_method");
|
|
||||||
return self.execute_string_method(string_box, method, arguments);
|
|
||||||
} else {
|
|
||||||
eprintln!("🔍 DEBUG: StringBox downcast failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntegerBox method calls
|
|
||||||
if let Some(integer_box) = obj_value.as_any().downcast_ref::<IntegerBox>() {
|
|
||||||
return self.execute_integer_method(integer_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FloatBox method calls
|
|
||||||
if let Some(float_box) = obj_value.as_any().downcast_ref::<FloatBox>() {
|
|
||||||
return self.execute_float_method(float_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolBox method calls
|
|
||||||
if let Some(bool_box) = obj_value.as_any().downcast_ref::<BoolBox>() {
|
|
||||||
return self.execute_bool_method(bool_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArrayBox method calls
|
|
||||||
if let Some(array_box) = obj_value.as_any().downcast_ref::<ArrayBox>() {
|
|
||||||
return self.execute_array_method(array_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// BufferBox method calls
|
|
||||||
if let Some(buffer_box) = obj_value.as_any().downcast_ref::<BufferBox>() {
|
|
||||||
return self.execute_buffer_method(buffer_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileBox method calls
|
|
||||||
if let Some(file_box) = obj_value.as_any().downcast_ref::<crate::boxes::file::FileBox>() {
|
|
||||||
return self.execute_file_method(file_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* legacy - PluginFileBox専用
|
|
||||||
// PluginFileBox method calls (BID-FFI system)
|
|
||||||
if let Some(plugin_file_box) = obj_value.as_any().downcast_ref::<crate::bid::plugin_box::PluginFileBox>() {
|
|
||||||
return self.execute_plugin_file_method(plugin_file_box, method, arguments);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ResultBox method calls
|
|
||||||
if let Some(result_box) = obj_value.as_any().downcast_ref::<crate::boxes::ResultBox>() {
|
|
||||||
return self.execute_result_method(result_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FutureBox method calls
|
|
||||||
if let Some(future_box) = obj_value.as_any().downcast_ref::<FutureBox>() {
|
|
||||||
return self.execute_future_method(future_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChannelBox method calls
|
|
||||||
if let Some(channel_box) = obj_value.as_any().downcast_ref::<ChannelBox>() {
|
|
||||||
return self.execute_channel_method(channel_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONBox method calls
|
|
||||||
if let Some(json_box) = obj_value.as_any().downcast_ref::<JSONBox>() {
|
|
||||||
return self.execute_json_method(json_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HttpClientBox method calls
|
|
||||||
if let Some(http_box) = obj_value.as_any().downcast_ref::<HttpClientBox>() {
|
|
||||||
return self.execute_http_method(http_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// StreamBox method calls
|
|
||||||
if let Some(stream_box) = obj_value.as_any().downcast_ref::<StreamBox>() {
|
|
||||||
return self.execute_stream_method(stream_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegexBox method calls
|
|
||||||
if let Some(regex_box) = obj_value.as_any().downcast_ref::<RegexBox>() {
|
|
||||||
return self.execute_regex_method(regex_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// MathBox method calls
|
|
||||||
if let Some(math_box) = obj_value.as_any().downcast_ref::<MathBox>() {
|
|
||||||
return self.execute_math_method(math_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NullBox method calls
|
|
||||||
if let Some(null_box) = obj_value.as_any().downcast_ref::<crate::boxes::null_box::NullBox>() {
|
|
||||||
return self.execute_null_method(null_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TimeBox method calls
|
|
||||||
if let Some(time_box) = obj_value.as_any().downcast_ref::<TimeBox>() {
|
|
||||||
return self.execute_time_method(time_box, method, arguments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DateTimeBox method calls
|
// DateTimeBox method calls
|
||||||
@ -367,68 +283,68 @@ impl NyashInterpreter {
|
|||||||
// These methods modify the SocketBox internal state, so we need to update
|
// These methods modify the SocketBox internal state, so we need to update
|
||||||
// the stored variable/field to ensure subsequent accesses get the updated state
|
// the stored variable/field to ensure subsequent accesses get the updated state
|
||||||
if matches!(method, "bind" | "connect" | "close") {
|
if matches!(method, "bind" | "connect" | "close") {
|
||||||
eprintln!("🔧 DEBUG: Stateful method '{}' called, updating stored instance", method);
|
idebug!("🔧 DEBUG: Stateful method '{}' called, updating stored instance", method);
|
||||||
let updated_instance = socket_box.clone();
|
let updated_instance = socket_box.clone();
|
||||||
eprintln!("🔧 DEBUG: Updated instance created with ID={}", updated_instance.box_id());
|
idebug!("🔧 DEBUG: Updated instance created with ID={}", updated_instance.box_id());
|
||||||
|
|
||||||
match object {
|
match object {
|
||||||
ASTNode::Variable { name, .. } => {
|
ASTNode::Variable { name, .. } => {
|
||||||
eprintln!("🔧 DEBUG: Updating local variable '{}'", name);
|
idebug!("🔧 DEBUG: Updating local variable '{}'", name);
|
||||||
// Handle local variables
|
// Handle local variables
|
||||||
if let Some(stored_var) = self.local_vars.get_mut(name) {
|
if let Some(stored_var) = self.local_vars.get_mut(name) {
|
||||||
eprintln!("🔧 DEBUG: Found local variable '{}', updating from id={} to id={}",
|
idebug!("🔧 DEBUG: Found local variable '{}', updating from id={} to id={}",
|
||||||
name, stored_var.box_id(), updated_instance.box_id());
|
name, stored_var.box_id(), updated_instance.box_id());
|
||||||
*stored_var = Arc::new(updated_instance);
|
*stored_var = Arc::new(updated_instance);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("🔧 DEBUG: Local variable '{}' not found", name);
|
idebug!("🔧 DEBUG: Local variable '{}' not found", name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ASTNode::FieldAccess { object: field_obj, field, .. } => {
|
ASTNode::FieldAccess { object: field_obj, field, .. } => {
|
||||||
eprintln!("🔧 DEBUG: Updating field access '{}'", field);
|
idebug!("🔧 DEBUG: Updating field access '{}'", field);
|
||||||
// Handle StaticBox fields like me.server
|
// Handle StaticBox fields like me.server
|
||||||
match field_obj.as_ref() {
|
match field_obj.as_ref() {
|
||||||
ASTNode::Variable { name, .. } => {
|
ASTNode::Variable { name, .. } => {
|
||||||
eprintln!("🔧 DEBUG: Field object is variable '{}'", name);
|
idebug!("🔧 DEBUG: Field object is variable '{}'", name);
|
||||||
if name == "me" {
|
if name == "me" {
|
||||||
eprintln!("🔧 DEBUG: Updating me.{} (via variable)", field);
|
idebug!("🔧 DEBUG: Updating me.{} (via variable)", field);
|
||||||
if let Ok(me_instance) = self.resolve_variable("me") {
|
if let Ok(me_instance) = self.resolve_variable("me") {
|
||||||
eprintln!("🔧 DEBUG: Resolved 'me' instance id={}", me_instance.box_id());
|
idebug!("🔧 DEBUG: Resolved 'me' instance id={}", me_instance.box_id());
|
||||||
if let Some(instance) = (*me_instance).as_any().downcast_ref::<InstanceBox>() {
|
if let Some(instance) = (*me_instance).as_any().downcast_ref::<InstanceBox>() {
|
||||||
eprintln!("🔧 DEBUG: me is InstanceBox, setting field '{}' to updated instance id={}", field, updated_instance.box_id());
|
idebug!("🔧 DEBUG: me is InstanceBox, setting field '{}' to updated instance id={}", field, updated_instance.box_id());
|
||||||
let result = instance.set_field(field, Arc::new(updated_instance));
|
let result = instance.set_field(field, Arc::new(updated_instance));
|
||||||
eprintln!("🔧 DEBUG: set_field result: {:?}", result);
|
idebug!("🔧 DEBUG: set_field result: {:?}", result);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("🔧 DEBUG: me is not an InstanceBox, type: {}", me_instance.type_name());
|
idebug!("🔧 DEBUG: me is not an InstanceBox, type: {}", me_instance.type_name());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("🔧 DEBUG: Failed to resolve 'me'");
|
idebug!("🔧 DEBUG: Failed to resolve 'me'");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("🔧 DEBUG: Field object is not 'me', it's '{}'", name);
|
idebug!("🔧 DEBUG: Field object is not 'me', it's '{}'", name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ASTNode::Me { .. } => {
|
ASTNode::Me { .. } => {
|
||||||
eprintln!("🔧 DEBUG: Field object is Me node, updating me.{}", field);
|
idebug!("🔧 DEBUG: Field object is Me node, updating me.{}", field);
|
||||||
if let Ok(me_instance) = self.resolve_variable("me") {
|
if let Ok(me_instance) = self.resolve_variable("me") {
|
||||||
eprintln!("🔧 DEBUG: Resolved 'me' instance id={}", me_instance.box_id());
|
idebug!("🔧 DEBUG: Resolved 'me' instance id={}", me_instance.box_id());
|
||||||
if let Some(instance) = (*me_instance).as_any().downcast_ref::<InstanceBox>() {
|
if let Some(instance) = (*me_instance).as_any().downcast_ref::<InstanceBox>() {
|
||||||
eprintln!("🔧 DEBUG: me is InstanceBox, setting field '{}' to updated instance id={}", field, updated_instance.box_id());
|
idebug!("🔧 DEBUG: me is InstanceBox, setting field '{}' to updated instance id={}", field, updated_instance.box_id());
|
||||||
let result = instance.set_field(field, Arc::new(updated_instance));
|
let result = instance.set_field(field, Arc::new(updated_instance));
|
||||||
eprintln!("🔧 DEBUG: set_field result: {:?}", result);
|
idebug!("🔧 DEBUG: set_field result: {:?}", result);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("🔧 DEBUG: me is not an InstanceBox, type: {}", me_instance.type_name());
|
idebug!("🔧 DEBUG: me is not an InstanceBox, type: {}", me_instance.type_name());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("🔧 DEBUG: Failed to resolve 'me'");
|
idebug!("🔧 DEBUG: Failed to resolve 'me'");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("🔧 DEBUG: Field object is not a variable or me, type: {:?}", field_obj);
|
idebug!("🔧 DEBUG: Field object is not a variable or me, type: {:?}", field_obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("🔧 DEBUG: Object type not handled: {:?}", object);
|
idebug!("🔧 DEBUG: Object type not handled: {:?}", object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -451,10 +367,10 @@ impl NyashInterpreter {
|
|||||||
return self.execute_http_response_method(http_response_box, method, arguments);
|
return self.execute_http_response_method(http_response_box, method, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2PBox method calls - Temporarily disabled
|
// P2PBox method calls
|
||||||
// if let Some(p2p_box) = obj_value.as_any().downcast_ref::<P2PBox>() {
|
if let Some(p2p_box) = obj_value.as_any().downcast_ref::<crate::boxes::P2PBox>() {
|
||||||
// return self.execute_p2p_box_method(p2p_box, method, arguments);
|
return self.execute_p2p_box_method(p2p_box, method, arguments);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// EguiBox method calls (非WASM環境のみ)
|
// EguiBox method calls (非WASM環境のみ)
|
||||||
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
|
||||||
@ -500,218 +416,10 @@ impl NyashInterpreter {
|
|||||||
return self.execute_plugin_box_v2_method(plugin_box, method, arguments);
|
return self.execute_plugin_box_v2_method(plugin_box, method, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⚠️ InstanceBox method calls (最後にチェック、ビルトインBoxの後)
|
// InstanceBox dispatch
|
||||||
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
|
if let Some(res) = self.dispatch_instance_method(object, &obj_value, method, arguments) { return res; }
|
||||||
// 🔥 finiは何回呼ばれてもエラーにしない(ユーザー要求)
|
idebug!("🔍 DEBUG: Reached non-instance type error for type: {}, method: {}", obj_value.type_name(), method);
|
||||||
// is_finalized()チェックを削除
|
Err(RuntimeError::TypeError { message: format!("Cannot call method '{}' on non-instance type", method) })
|
||||||
|
|
||||||
// fini()は特別処理
|
|
||||||
if method == "fini" {
|
|
||||||
// 🔥 weak-fini prohibition check - prevent fini() on weak fields
|
|
||||||
if let ASTNode::FieldAccess { object: field_object, field, .. } = object {
|
|
||||||
// Check if this is me.<field>.fini() pattern
|
|
||||||
if let ASTNode::Variable { name, .. } = field_object.as_ref() {
|
|
||||||
if name == "me" {
|
|
||||||
// Get current instance to check if field is weak
|
|
||||||
if let Ok(current_me) = self.resolve_variable("me") {
|
|
||||||
if let Some(current_instance) = (*current_me).as_any().downcast_ref::<InstanceBox>() {
|
|
||||||
if current_instance.is_weak_field(field) {
|
|
||||||
return Err(RuntimeError::InvalidOperation {
|
|
||||||
message: format!(
|
|
||||||
"Cannot finalize weak field '{}' (non-owning reference)",
|
|
||||||
field
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 既に解放済みの場合は何もしない(二重fini()対策)
|
|
||||||
if instance.is_finalized() {
|
|
||||||
return Ok(Box::new(VoidBox::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// まず、Box内で定義されたfini()メソッドがあれば実行
|
|
||||||
if let Some(fini_method) = instance.get_method("fini") {
|
|
||||||
if let ASTNode::FunctionDeclaration { body, .. } = fini_method.clone() {
|
|
||||||
// 🌍 革命的メソッド実行:local変数スタックを使用
|
|
||||||
let saved_locals = self.save_local_vars();
|
|
||||||
self.local_vars.clear();
|
|
||||||
|
|
||||||
// thisをlocal変数として設定
|
|
||||||
self.declare_local_variable("me", obj_value.clone_or_share());
|
|
||||||
|
|
||||||
// fini()メソッドの本体を実行
|
|
||||||
let mut _result = Box::new(VoidBox::new()) as Box<dyn NyashBox>;
|
|
||||||
for statement in &body {
|
|
||||||
_result = self.execute_statement(statement)?;
|
|
||||||
|
|
||||||
// return文チェック
|
|
||||||
if let super::ControlFlow::Return(_) = &self.control_flow {
|
|
||||||
self.control_flow = super::ControlFlow::None;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// local変数スタックを復元
|
|
||||||
self.restore_local_vars(saved_locals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🔗 Phase 8.9: Weak reference invalidation after user fini
|
|
||||||
let target_info = obj_value.to_string_box().value;
|
|
||||||
eprintln!("🔗 DEBUG: Triggering weak reference invalidation for fini: {}", target_info);
|
|
||||||
self.trigger_weak_reference_invalidation(&target_info);
|
|
||||||
|
|
||||||
// インスタンスの内部的な解放処理
|
|
||||||
instance.fini().map_err(|e| RuntimeError::InvalidOperation {
|
|
||||||
message: e,
|
|
||||||
})?;
|
|
||||||
finalization::mark_as_finalized(instance.box_id());
|
|
||||||
return Ok(Box::new(VoidBox::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// メソッドを取得(まずローカルメソッドを確認)
|
|
||||||
if let Some(method_ast) = instance.get_method(method) {
|
|
||||||
let method_ast = method_ast.clone();
|
|
||||||
|
|
||||||
// メソッドが関数宣言の形式であることを確認
|
|
||||||
if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast {
|
|
||||||
// 🚨 FIX: 引数評価を完全に現在のコンテキストで完了させる
|
|
||||||
let mut arg_values = Vec::new();
|
|
||||||
for (_i, arg) in arguments.iter().enumerate() {
|
|
||||||
let arg_value = self.execute_expression(arg)?;
|
|
||||||
arg_values.push(arg_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// パラメータ数チェック
|
|
||||||
if arg_values.len() != params.len() {
|
|
||||||
return Err(RuntimeError::InvalidOperation {
|
|
||||||
message: format!("Method {} expects {} arguments, got {}",
|
|
||||||
method, params.len(), arg_values.len()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🌍 NOW SAFE: すべての引数評価完了後にコンテキスト切り替え
|
|
||||||
let saved_locals = self.save_local_vars();
|
|
||||||
self.local_vars.clear();
|
|
||||||
|
|
||||||
// thisをlocal変数として設定
|
|
||||||
self.declare_local_variable("me", obj_value.clone_or_share());
|
|
||||||
|
|
||||||
// パラメータをlocal変数として設定
|
|
||||||
for (param, value) in params.iter().zip(arg_values.iter()) {
|
|
||||||
self.declare_local_variable(param, value.clone_or_share());
|
|
||||||
}
|
|
||||||
|
|
||||||
// メソッド本体を実行
|
|
||||||
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
|
|
||||||
for statement in &body {
|
|
||||||
result = self.execute_statement(statement)?;
|
|
||||||
|
|
||||||
// return文チェック
|
|
||||||
if let super::ControlFlow::Return(return_val) = &self.control_flow {
|
|
||||||
result = return_val.clone_box();
|
|
||||||
self.control_flow = super::ControlFlow::None;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// local変数スタックを復元
|
|
||||||
self.restore_local_vars(saved_locals);
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::InvalidOperation {
|
|
||||||
message: format!("Method '{}' is not a valid function declaration", method),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// ローカルメソッドが見つからない場合、親のビルトインBoxメソッドを確認
|
|
||||||
let box_declarations = self.shared.box_declarations.read().unwrap();
|
|
||||||
let parent_names = if let Some(box_decl) = box_declarations.get(&instance.class_name) {
|
|
||||||
box_decl.extends.clone()
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
};
|
|
||||||
drop(box_declarations);
|
|
||||||
|
|
||||||
// 親がビルトインBoxか確認
|
|
||||||
for parent_name in &parent_names {
|
|
||||||
if crate::box_trait::is_builtin_box(parent_name) {
|
|
||||||
// ビルトインBoxメソッドを実行
|
|
||||||
if parent_name == "StringBox" {
|
|
||||||
// ユーザー定義BoxがStringBoxを継承している場合
|
|
||||||
// __builtin_contentフィールドからStringBoxを取得
|
|
||||||
if let Some(builtin_value) = instance.get_field_ng("__builtin_content") {
|
|
||||||
if let crate::value::NyashValue::Box(boxed) = builtin_value {
|
|
||||||
let boxed_guard = boxed.lock().unwrap();
|
|
||||||
if let Some(string_box) = boxed_guard.as_any().downcast_ref::<StringBox>() {
|
|
||||||
return self.execute_string_method(string_box, method, arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
// フィールドが見つからない場合は空のStringBoxを使用(互換性のため)
|
|
||||||
let string_box = StringBox::new("");
|
|
||||||
return self.execute_string_method(&string_box, method, arguments);
|
|
||||||
} else if parent_name == "IntegerBox" {
|
|
||||||
// __builtin_contentフィールドからIntegerBoxを取得
|
|
||||||
if let Some(builtin_value) = instance.get_field_ng("__builtin_content") {
|
|
||||||
if let crate::value::NyashValue::Box(boxed) = builtin_value {
|
|
||||||
let boxed_guard = boxed.lock().unwrap();
|
|
||||||
if let Some(integer_box) = boxed_guard.as_any().downcast_ref::<IntegerBox>() {
|
|
||||||
return self.execute_integer_method(integer_box, method, arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// フィールドが見つからない場合は0のIntegerBoxを使用
|
|
||||||
let integer_box = IntegerBox::new(0);
|
|
||||||
return self.execute_integer_method(&integer_box, method, arguments);
|
|
||||||
} else if parent_name == "MathBox" {
|
|
||||||
// MathBoxはステートレスなので、新しいインスタンスを作成
|
|
||||||
let math_box = MathBox::new();
|
|
||||||
return self.execute_math_method(&math_box, method, arguments);
|
|
||||||
}
|
|
||||||
// 他のビルトインBoxも必要に応じて追加
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// プラグイン親のメソッド呼び出し(__plugin_content)
|
|
||||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
|
||||||
{
|
|
||||||
if let Some(plugin_shared) = instance.get_field_legacy("__plugin_content") {
|
|
||||||
let plugin_ref = &*plugin_shared;
|
|
||||||
if let Some(plugin) = plugin_ref.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
|
||||||
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
|
||||||
for arg in arguments {
|
|
||||||
arg_values.push(self.execute_expression(arg)?);
|
|
||||||
}
|
|
||||||
let loader = crate::runtime::get_global_loader_v2();
|
|
||||||
let loader = loader.read().unwrap();
|
|
||||||
match loader.invoke_instance_method(&plugin.box_type, method, plugin.instance_id(), &arg_values) {
|
|
||||||
Ok(Some(result_box)) => return Ok(result_box),
|
|
||||||
Ok(None) => return Ok(Box::new(VoidBox::new())),
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// メソッドが見つからない
|
|
||||||
Err(RuntimeError::InvalidOperation {
|
|
||||||
message: format!("Method '{}' not found in {}", method, instance.class_name),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("🔍 DEBUG: Reached non-instance type error for type: {}, method: {}", obj_value.type_name(), method);
|
|
||||||
Err(RuntimeError::TypeError {
|
|
||||||
message: format!("Cannot call method '{}' on non-instance type", method),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 🔥 FromCall実行処理 - from Parent.method(arguments) or from Parent.constructor(arguments)
|
/// 🔥 FromCall実行処理 - from Parent.method(arguments) or from Parent.constructor(arguments)
|
||||||
@ -752,18 +460,12 @@ impl NyashInterpreter {
|
|||||||
// 先にプラグイン親のコンストラクタ/メソッドを優先的に処理(v2プラグイン対応)
|
// 先にプラグイン親のコンストラクタ/メソッドを優先的に処理(v2プラグイン対応)
|
||||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
{
|
{
|
||||||
let loader_guard = crate::runtime::get_global_loader_v2();
|
|
||||||
let loader = loader_guard.read().unwrap();
|
|
||||||
// 親がプラグインで提供されているかを確認
|
// 親がプラグインで提供されているかを確認
|
||||||
if loader.config.as_ref().and_then(|c| c.find_library_for_box(parent)).is_some() {
|
if self.is_plugin_box_type(parent) {
|
||||||
// コンストラクタ相当(birth もしくは 親名と同名)の場合は、
|
// コンストラクタ相当(birth もしくは 親名と同名)の場合は、
|
||||||
// プラグインBoxを生成して __plugin_content に格納
|
// プラグインBoxを生成して __plugin_content に格納
|
||||||
if method == "birth" || method == parent {
|
if method == "birth" || method == parent {
|
||||||
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
match self.create_plugin_box(parent, arguments) {
|
||||||
for arg in arguments {
|
|
||||||
arg_values.push(self.execute_expression(arg)?);
|
|
||||||
}
|
|
||||||
match loader.create_box(parent, &arg_values) {
|
|
||||||
Ok(pbox) => {
|
Ok(pbox) => {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
let _ = current_instance.set_field_legacy("__plugin_content", Arc::from(pbox));
|
let _ = current_instance.set_field_legacy("__plugin_content", Arc::from(pbox));
|
||||||
@ -780,17 +482,7 @@ impl NyashInterpreter {
|
|||||||
if let Some(plugin_shared) = current_instance.get_field_legacy("__plugin_content") {
|
if let Some(plugin_shared) = current_instance.get_field_legacy("__plugin_content") {
|
||||||
let plugin_ref = &*plugin_shared;
|
let plugin_ref = &*plugin_shared;
|
||||||
if let Some(plugin) = plugin_ref.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
if let Some(plugin) = plugin_ref.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||||
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
return self.execute_plugin_box_v2_method(plugin, method, arguments);
|
||||||
for arg in arguments { arg_values.push(self.execute_expression(arg)?); }
|
|
||||||
match loader.invoke_instance_method(&plugin.box_type, method, plugin.instance_id(), &arg_values) {
|
|
||||||
Ok(Some(result_box)) => return Ok(result_box),
|
|
||||||
Ok(None) => return Ok(Box::new(crate::box_trait::VoidBox::new())),
|
|
||||||
Err(e) => {
|
|
||||||
return Err(RuntimeError::InvalidOperation {
|
|
||||||
message: format!("Plugin call {}.{} failed: {:?}", parent, method, e),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -816,25 +508,12 @@ impl NyashInterpreter {
|
|||||||
// プラグイン親(__plugin_content)
|
// プラグイン親(__plugin_content)
|
||||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
{
|
{
|
||||||
// 親がユーザー定義に見つからない場合は、プラグインとして試行
|
if let Some(plugin_shared) = current_instance.get_field_legacy("__plugin_content") {
|
||||||
// 現在のインスタンスから __plugin_content を参照
|
let plugin_ref = &*plugin_shared;
|
||||||
if let Some(plugin_shared) = current_instance.get_field_legacy("__plugin_content") {
|
if let Some(plugin) = plugin_ref.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||||
// 引数を評価(ロックは既に解放済みの設計)
|
return self.execute_plugin_box_v2_method(plugin, method, arguments);
|
||||||
let plugin_ref = &*plugin_shared;
|
}
|
||||||
if let Some(plugin) = plugin_ref.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
}
|
||||||
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
|
||||||
for arg in arguments {
|
|
||||||
arg_values.push(self.execute_expression(arg)?);
|
|
||||||
}
|
|
||||||
let loader = crate::runtime::get_global_loader_v2();
|
|
||||||
let loader = loader.read().unwrap();
|
|
||||||
match loader.invoke_instance_method(&plugin.box_type, method, plugin.instance_id(), &arg_values) {
|
|
||||||
Ok(Some(result_box)) => return Ok(result_box),
|
|
||||||
Ok(None) => return Ok(Box::new(VoidBox::new())),
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 親クラスのBox宣言を取得(ユーザー定義Boxの場合)
|
// 3. 親クラスのBox宣言を取得(ユーザー定義Boxの場合)
|
||||||
@ -901,7 +580,7 @@ impl NyashInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 🔍 DEBUG: FromCall実行結果をログ出力
|
// 🔍 DEBUG: FromCall実行結果をログ出力
|
||||||
eprintln!("🔍 DEBUG: FromCall {}.{} result: {}", parent, method, result.to_string_box().value);
|
idebug!("🔍 DEBUG: FromCall {}.{} result: {}", parent, method, result.to_string_box().value);
|
||||||
|
|
||||||
// local変数スタックを復元
|
// local変数スタックを復元
|
||||||
self.restore_local_vars(saved_locals);
|
self.restore_local_vars(saved_locals);
|
||||||
@ -996,21 +675,6 @@ impl NyashInterpreter {
|
|||||||
method: &str,
|
method: &str,
|
||||||
arguments: &[ASTNode],
|
arguments: &[ASTNode],
|
||||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||||
// Guard: use-after-fini is a runtime error (明示ライフサイクル)
|
self.call_plugin_method(plugin_box, method, arguments)
|
||||||
if plugin_box.is_finalized() {
|
|
||||||
return Err(RuntimeError::RuntimeFailure { message: format!("Use after fini: {}", plugin_box.box_type) });
|
|
||||||
}
|
|
||||||
eprintln!("🔍 execute_plugin_box_v2_method called: {}.{}", plugin_box.box_type, method);
|
|
||||||
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
|
||||||
for arg in arguments {
|
|
||||||
arg_values.push(self.execute_expression(arg)?);
|
|
||||||
}
|
|
||||||
let loader_guard = crate::runtime::plugin_loader_v2::get_global_loader_v2();
|
|
||||||
let loader = loader_guard.read().map_err(|_| RuntimeError::RuntimeFailure { message: "Plugin loader lock poisoned".into() })?;
|
|
||||||
match loader.invoke_instance_method(&plugin_box.box_type, method, plugin_box.instance_id(), &arg_values) {
|
|
||||||
Ok(Some(result_box)) => Ok(result_box),
|
|
||||||
Ok(None) => Ok(Box::new(VoidBox::new())),
|
|
||||||
Err(e) => Err(RuntimeError::RuntimeFailure { message: format!("Plugin method {} failed: {:?}", method, e) }),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use crate::ast::{ASTNode, BinaryOperator, UnaryOperator};
|
|||||||
use crate::box_trait::{NyashBox, BoolBox, CompareBox};
|
use crate::box_trait::{NyashBox, BoolBox, CompareBox};
|
||||||
use crate::box_trait::{IntegerBox, StringBox}; // 🔧 修正: box_trait::*に統一
|
use crate::box_trait::{IntegerBox, StringBox}; // 🔧 修正: box_trait::*に統一
|
||||||
use crate::boxes::FloatBox; // FloatBoxはboxesのみに存在
|
use crate::boxes::FloatBox; // FloatBoxはboxesのみに存在
|
||||||
use crate::interpreter::core::{NyashInterpreter, RuntimeError};
|
use crate::interpreter::{NyashInterpreter, RuntimeError};
|
||||||
use crate::instance_v2::InstanceBox;
|
use crate::instance_v2::InstanceBox;
|
||||||
|
|
||||||
// Local helper functions to bypass import issues
|
// Local helper functions to bypass import issues
|
||||||
|
|||||||
@ -3,11 +3,12 @@
|
|||||||
* Arc<Mutex>パターン対応版
|
* Arc<Mutex>パターン対応版
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::interpreter::core::NyashInterpreter;
|
use crate::interpreter::NyashInterpreter;
|
||||||
use crate::interpreter::core::RuntimeError;
|
use crate::interpreter::RuntimeError;
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::box_trait::{NyashBox, StringBox};
|
use crate::box_trait::{NyashBox, StringBox};
|
||||||
use crate::boxes::{IntentBox};
|
use crate::boxes::{IntentBox, P2PBox};
|
||||||
|
use crate::box_trait::BoolBox;
|
||||||
|
|
||||||
impl NyashInterpreter {
|
impl NyashInterpreter {
|
||||||
/// IntentBoxのメソッド実行 (RwLock版)
|
/// IntentBoxのメソッド実行 (RwLock版)
|
||||||
@ -39,75 +40,73 @@ impl NyashInterpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// P2PBoxのメソッド実行 (Arc<Mutex>版) - Temporarily disabled
|
// P2PBoxのメソッド実装(RwLockベース)
|
||||||
/*
|
|
||||||
pub(in crate::interpreter) fn execute_p2p_box_method(
|
pub(in crate::interpreter) fn execute_p2p_box_method(
|
||||||
&mut self,
|
&mut self,
|
||||||
p2p_box: &P2PBox,
|
p2p_box: &P2PBox,
|
||||||
method: &str,
|
method: &str,
|
||||||
arguments: &[ASTNode],
|
arguments: &[ASTNode],
|
||||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||||
let data = p2p_box.lock().map_err(|_| RuntimeError::UndefinedVariable {
|
|
||||||
name: "Failed to lock P2PBox".to_string(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
match method {
|
match method {
|
||||||
// ノードID取得
|
// ノードID取得
|
||||||
"getNodeId" | "getId" => {
|
"getNodeId" | "getId" => Ok(p2p_box.get_node_id()),
|
||||||
Ok(Box::new(StringBox::new(data.get_node_id().to_string())))
|
|
||||||
}
|
|
||||||
|
|
||||||
// トランスポート種類取得
|
// トランスポート種類取得
|
||||||
"getTransportType" | "transport" => {
|
"getTransportType" | "transport" => Ok(p2p_box.get_transport_type()),
|
||||||
Ok(Box::new(StringBox::new(data.get_transport_type())))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ノード到達可能性確認
|
// ノード到達可能性確認
|
||||||
"isReachable" => {
|
"isReachable" => {
|
||||||
if arguments.is_empty() {
|
if arguments.is_empty() {
|
||||||
return Err(RuntimeError::UndefinedVariable {
|
return Err(RuntimeError::InvalidOperation { message: "isReachable requires node_id argument".to_string() });
|
||||||
name: "isReachable requires node_id argument".to_string(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let node_id_result = self.execute_expression(&arguments[0])?;
|
let node_id_result = self.execute_expression(&arguments[0])?;
|
||||||
let node_id = node_id_result.to_string_box().value;
|
Ok(p2p_box.is_reachable(node_id_result))
|
||||||
let reachable = data.is_reachable(&node_id);
|
|
||||||
Ok(Box::new(BoolBox::new(reachable)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// send メソッド実装
|
// send メソッド実装(ResultBox返却)
|
||||||
"send" => {
|
"send" => {
|
||||||
if arguments.len() < 2 {
|
if arguments.len() < 2 {
|
||||||
return Err(RuntimeError::UndefinedVariable {
|
return Err(RuntimeError::InvalidOperation { message: "send requires (to, intent) arguments".to_string() });
|
||||||
name: "send requires (to, intent) arguments".to_string(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let to_result = self.execute_expression(&arguments[0])?;
|
let to_result = self.execute_expression(&arguments[0])?;
|
||||||
let to = to_result.to_string_box().value;
|
|
||||||
|
|
||||||
let intent_result = self.execute_expression(&arguments[1])?;
|
let intent_result = self.execute_expression(&arguments[1])?;
|
||||||
|
Ok(p2p_box.send(to_result, intent_result))
|
||||||
// IntentBoxかチェック
|
|
||||||
if let Some(intent_box) = intent_result.as_any().downcast_ref::<IntentBox>() {
|
|
||||||
match data.send(&to, intent_box.clone()) {
|
|
||||||
Ok(_) => Ok(Box::new(StringBox::new("sent"))),
|
|
||||||
Err(e) => Err(RuntimeError::UndefinedVariable {
|
|
||||||
name: format!("Send failed: {:?}", e),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::UndefinedVariable {
|
|
||||||
name: "Second argument must be an IntentBox".to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Err(RuntimeError::UndefinedVariable {
|
// on メソッド実装(ResultBox返却)
|
||||||
name: format!("P2PBox method '{}' not found", method),
|
, "on" => {
|
||||||
})
|
if arguments.len() < 2 {
|
||||||
|
return Err(RuntimeError::InvalidOperation { message: "on requires (intentName, handler) arguments".to_string() });
|
||||||
|
}
|
||||||
|
let name_val = self.execute_expression(&arguments[0])?;
|
||||||
|
let handler_val = self.execute_expression(&arguments[1])?;
|
||||||
|
Ok(p2p_box.on(name_val, handler_val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最後の受信情報(ループバック検証用)
|
||||||
|
"getLastFrom" => Ok(p2p_box.get_last_from()),
|
||||||
|
"getLastIntentName" => Ok(p2p_box.get_last_intent_name()),
|
||||||
|
"debug_nodes" | "debugNodes" => Ok(p2p_box.debug_nodes()),
|
||||||
|
"debug_bus_id" | "debugBusId" => Ok(p2p_box.debug_bus_id()),
|
||||||
|
|
||||||
|
// onOnce / off
|
||||||
|
"onOnce" | "on_once" => {
|
||||||
|
if arguments.len() < 2 {
|
||||||
|
return Err(RuntimeError::InvalidOperation { message: "onOnce requires (intentName, handler) arguments".to_string() });
|
||||||
|
}
|
||||||
|
let name_val = self.execute_expression(&arguments[0])?;
|
||||||
|
let handler_val = self.execute_expression(&arguments[1])?;
|
||||||
|
Ok(p2p_box.on_once(name_val, handler_val))
|
||||||
|
}
|
||||||
|
"off" => {
|
||||||
|
if arguments.len() < 1 {
|
||||||
|
return Err(RuntimeError::InvalidOperation { message: "off requires (intentName) argument".to_string() });
|
||||||
|
}
|
||||||
|
let name_val = self.execute_expression(&arguments[0])?;
|
||||||
|
Ok(p2p_box.off(name_val))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => Err(RuntimeError::UndefinedVariable { name: format!("P2PBox method '{}' not found", method) }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
276
src/interpreter/methods_dispatch.rs
Normal file
276
src/interpreter/methods_dispatch.rs
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
//! Central builtin method dispatcher (thin wrapper)
|
||||||
|
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore};
|
||||||
|
use crate::boxes::{ArrayBox, FloatBox, BufferBox, ResultBox, FutureBox, JSONBox, HttpClientBox, StreamBox, RegexBox, MathBox};
|
||||||
|
use crate::boxes::{null_box, time_box, map_box, random_box, sound_box, debug_box, console_box};
|
||||||
|
use crate::boxes::file;
|
||||||
|
use crate::channel_box::ChannelBox;
|
||||||
|
use super::{NyashInterpreter, RuntimeError};
|
||||||
|
|
||||||
|
impl NyashInterpreter {
|
||||||
|
/// Try dispatching a builtin method based on dynamic type.
|
||||||
|
/// Returns Some(Result) if handled, or None to let caller continue other paths.
|
||||||
|
pub(crate) fn dispatch_builtin_method(
|
||||||
|
&mut self,
|
||||||
|
obj: &Box<dyn NyashBox>,
|
||||||
|
method: &str,
|
||||||
|
arguments: &[ASTNode],
|
||||||
|
) -> Option<Result<Box<dyn NyashBox>, RuntimeError>> {
|
||||||
|
// StringBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<StringBox>() {
|
||||||
|
return Some(self.execute_string_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// IntegerBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<IntegerBox>() {
|
||||||
|
return Some(self.execute_integer_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// FloatBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<FloatBox>() {
|
||||||
|
return Some(self.execute_float_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// BoolBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<BoolBox>() {
|
||||||
|
return Some(self.execute_bool_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// ArrayBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<ArrayBox>() {
|
||||||
|
return Some(self.execute_array_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// BufferBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<BufferBox>() {
|
||||||
|
return Some(self.execute_buffer_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// FileBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<file::FileBox>() {
|
||||||
|
return Some(self.execute_file_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// ResultBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<ResultBox>() {
|
||||||
|
return Some(self.execute_result_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// FutureBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<FutureBox>() {
|
||||||
|
return Some(self.execute_future_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// ChannelBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<ChannelBox>() {
|
||||||
|
return Some(self.execute_channel_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// JSONBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<JSONBox>() {
|
||||||
|
return Some(self.execute_json_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// HttpClientBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<HttpClientBox>() {
|
||||||
|
return Some(self.execute_http_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// StreamBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<StreamBox>() {
|
||||||
|
return Some(self.execute_stream_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// RegexBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<RegexBox>() {
|
||||||
|
return Some(self.execute_regex_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// MathBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<MathBox>() {
|
||||||
|
return Some(self.execute_math_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// NullBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<null_box::NullBox>() {
|
||||||
|
return Some(self.execute_null_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// TimeBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<time_box::TimeBox>() {
|
||||||
|
return Some(self.execute_time_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// TimerBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<time_box::TimerBox>() {
|
||||||
|
return Some(self.execute_timer_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// MapBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<map_box::MapBox>() {
|
||||||
|
return Some(self.execute_map_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// RandomBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<random_box::RandomBox>() {
|
||||||
|
return Some(self.execute_random_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// SoundBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<sound_box::SoundBox>() {
|
||||||
|
return Some(self.execute_sound_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// DebugBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<debug_box::DebugBox>() {
|
||||||
|
return Some(self.execute_debug_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
// ConsoleBox
|
||||||
|
if let Some(b) = obj.as_any().downcast_ref::<console_box::ConsoleBox>() {
|
||||||
|
return Some(self.execute_console_method(b, method, arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dispatch user-defined instance methods (InstanceBox path).
|
||||||
|
/// Returns Some(Result) if handled, or None if obj is not an InstanceBox.
|
||||||
|
pub(crate) fn dispatch_instance_method(
|
||||||
|
&mut self,
|
||||||
|
object_ast: &ASTNode,
|
||||||
|
obj_value: &Box<dyn NyashBox>,
|
||||||
|
method: &str,
|
||||||
|
arguments: &[ASTNode],
|
||||||
|
) -> Option<Result<Box<dyn NyashBox>, RuntimeError>> {
|
||||||
|
use crate::box_trait::{StringBox, IntegerBox};
|
||||||
|
use crate::boxes::MathBox;
|
||||||
|
use crate::instance_v2::InstanceBox;
|
||||||
|
use crate::finalization;
|
||||||
|
|
||||||
|
let instance = match obj_value.as_any().downcast_ref::<InstanceBox>() {
|
||||||
|
Some(i) => i,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// fini() special handling (idempotent, weak prohibition)
|
||||||
|
if method == "fini" {
|
||||||
|
// weak-fini prohibition check: me.<weak_field>.fini()
|
||||||
|
if let ASTNode::FieldAccess { object: field_object, field, .. } = object_ast {
|
||||||
|
if let ASTNode::Variable { name, .. } = field_object.as_ref() {
|
||||||
|
if name == "me" {
|
||||||
|
if let Ok(current_me) = self.resolve_variable("me") {
|
||||||
|
if let Some(current_instance) = (*current_me).as_any().downcast_ref::<InstanceBox>() {
|
||||||
|
if current_instance.is_weak_field(field) {
|
||||||
|
return Some(Err(RuntimeError::InvalidOperation {
|
||||||
|
message: format!(
|
||||||
|
"Cannot finalize weak field '{}' (non-owning reference)",
|
||||||
|
field
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if instance.is_finalized() {
|
||||||
|
return Some(Ok(Box::new(crate::box_trait::VoidBox::new())));
|
||||||
|
}
|
||||||
|
if let Some(fini_method) = instance.get_method("fini") {
|
||||||
|
if let ASTNode::FunctionDeclaration { body, .. } = fini_method.clone() {
|
||||||
|
let saved = self.save_local_vars();
|
||||||
|
self.local_vars.clear();
|
||||||
|
self.declare_local_variable("me", obj_value.clone_or_share());
|
||||||
|
let mut _result = Box::new(crate::box_trait::VoidBox::new()) as Box<dyn NyashBox>;
|
||||||
|
for statement in &body {
|
||||||
|
match self.execute_statement(statement) {
|
||||||
|
Ok(v) => { _result = v; },
|
||||||
|
Err(e) => { self.restore_local_vars(saved); return Some(Err(e)); }
|
||||||
|
}
|
||||||
|
if let super::ControlFlow::Return(_) = &self.control_flow {
|
||||||
|
self.control_flow = super::ControlFlow::None;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.restore_local_vars(saved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let target_info = obj_value.to_string_box().value;
|
||||||
|
self.trigger_weak_reference_invalidation(&target_info);
|
||||||
|
if let Err(e) = instance.fini() { return Some(Err(RuntimeError::InvalidOperation { message: e })); }
|
||||||
|
finalization::mark_as_finalized(instance.box_id());
|
||||||
|
return Some(Ok(Box::new(crate::box_trait::VoidBox::new())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local method on instance
|
||||||
|
if let Some(method_ast) = instance.get_method(method) {
|
||||||
|
if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast.clone() {
|
||||||
|
// Evaluate args in current context
|
||||||
|
let mut arg_values = Vec::new();
|
||||||
|
for a in arguments {
|
||||||
|
match self.execute_expression(a) {
|
||||||
|
Ok(v) => arg_values.push(v),
|
||||||
|
Err(e) => return Some(Err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if arg_values.len() != params.len() {
|
||||||
|
return Some(Err(RuntimeError::InvalidOperation {
|
||||||
|
message: format!("Method {} expects {} arguments, got {}", method, params.len(), arg_values.len()),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
let saved = self.save_local_vars();
|
||||||
|
self.local_vars.clear();
|
||||||
|
self.declare_local_variable("me", obj_value.clone_or_share());
|
||||||
|
for (p, v) in params.iter().zip(arg_values.iter()) {
|
||||||
|
self.declare_local_variable(p, v.clone_or_share());
|
||||||
|
}
|
||||||
|
let mut result: Box<dyn NyashBox> = Box::new(crate::box_trait::VoidBox::new());
|
||||||
|
for stmt in &body {
|
||||||
|
match self.execute_statement(stmt) {
|
||||||
|
Ok(v) => { result = v; },
|
||||||
|
Err(e) => return Some(Err(e)),
|
||||||
|
}
|
||||||
|
if let super::ControlFlow::Return(ret) = &self.control_flow {
|
||||||
|
result = ret.clone_box();
|
||||||
|
self.control_flow = super::ControlFlow::None;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.restore_local_vars(saved);
|
||||||
|
return Some(Ok(result));
|
||||||
|
} else {
|
||||||
|
return Some(Err(RuntimeError::InvalidOperation { message: format!("Method '{}' is not a valid function declaration", method) }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builtin parent method promotion (StringBox/IntegerBox/MathBox)
|
||||||
|
let parent_names = {
|
||||||
|
let decls = self.shared.box_declarations.read().unwrap();
|
||||||
|
decls.get(&instance.class_name).map(|d| d.extends.clone()).unwrap_or_default()
|
||||||
|
};
|
||||||
|
for parent_name in &parent_names {
|
||||||
|
if crate::box_trait::is_builtin_box(parent_name) {
|
||||||
|
if parent_name == "StringBox" {
|
||||||
|
if let Some(builtin_value) = instance.get_field_ng("__builtin_content") {
|
||||||
|
if let crate::value::NyashValue::Box(boxed) = builtin_value {
|
||||||
|
let g = boxed.lock().unwrap();
|
||||||
|
if let Some(sb) = g.as_any().downcast_ref::<StringBox>() {
|
||||||
|
return Some(self.execute_string_method(sb, method, arguments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let sb = StringBox::new("");
|
||||||
|
return Some(self.execute_string_method(&sb, method, arguments));
|
||||||
|
} else if parent_name == "IntegerBox" {
|
||||||
|
if let Some(builtin_value) = instance.get_field_ng("__builtin_content") {
|
||||||
|
if let crate::value::NyashValue::Box(boxed) = builtin_value {
|
||||||
|
let g = boxed.lock().unwrap();
|
||||||
|
if let Some(ib) = g.as_any().downcast_ref::<IntegerBox>() {
|
||||||
|
return Some(self.execute_integer_method(ib, method, arguments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ib = IntegerBox::new(0);
|
||||||
|
return Some(self.execute_integer_method(&ib, method, arguments));
|
||||||
|
} else if parent_name == "MathBox" {
|
||||||
|
let math_box = MathBox::new();
|
||||||
|
return Some(self.execute_math_method(&math_box, method, arguments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugin parent via __plugin_content
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
|
{
|
||||||
|
if let Some(plugin_shared) = instance.get_field_legacy("__plugin_content") {
|
||||||
|
let plugin_ref = &*plugin_shared;
|
||||||
|
if let Some(plugin) = plugin_ref.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||||
|
return Some(self.call_plugin_method(plugin, method, arguments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not handled here
|
||||||
|
Some(Err(RuntimeError::InvalidOperation { message: format!("Method '{}' not found in {}", method, instance.class_name) }))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,6 +30,12 @@ use std::collections::HashMap;
|
|||||||
mod async_methods;
|
mod async_methods;
|
||||||
mod box_methods;
|
mod box_methods;
|
||||||
mod core;
|
mod core;
|
||||||
|
mod eval;
|
||||||
|
mod calls;
|
||||||
|
mod methods_dispatch;
|
||||||
|
pub mod utils;
|
||||||
|
pub mod state;
|
||||||
|
pub mod errors;
|
||||||
mod expressions;
|
mod expressions;
|
||||||
mod statements;
|
mod statements;
|
||||||
mod functions;
|
mod functions;
|
||||||
@ -44,6 +50,8 @@ mod special_methods;
|
|||||||
|
|
||||||
// Main interpreter implementation - will be moved from interpreter.rs
|
// Main interpreter implementation - will be moved from interpreter.rs
|
||||||
pub use core::NyashInterpreter;
|
pub use core::NyashInterpreter;
|
||||||
|
pub use state::SharedState;
|
||||||
|
pub use errors::RuntimeError;
|
||||||
|
|
||||||
|
|
||||||
/// 実行制御フロー
|
/// 実行制御フロー
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::box_trait::*;
|
use crate::box_trait::*;
|
||||||
use crate::interpreter::core::{NyashInterpreter as Interpreter, RuntimeError};
|
use crate::interpreter::{NyashInterpreter as Interpreter, RuntimeError};
|
||||||
use crate::boxes::FloatBox;
|
use crate::boxes::FloatBox;
|
||||||
use crate::boxes::null_box::NullBox;
|
use crate::boxes::null_box::NullBox;
|
||||||
use crate::boxes::map_box::MapBox;
|
use crate::boxes::map_box::MapBox;
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::box_trait::*;
|
use crate::box_trait::*;
|
||||||
use crate::interpreter::core::{NyashInterpreter as Interpreter, RuntimeError};
|
use crate::interpreter::{NyashInterpreter as Interpreter, RuntimeError};
|
||||||
use crate::boxes::math_box::MathBox;
|
use crate::boxes::math_box::MathBox;
|
||||||
use crate::boxes::random_box::RandomBox;
|
use crate::boxes::random_box::RandomBox;
|
||||||
use crate::boxes::sound_box::SoundBox;
|
use crate::boxes::sound_box::SoundBox;
|
||||||
|
|||||||
42
src/interpreter/state.rs
Normal file
42
src/interpreter/state.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use crate::instance_v2::InstanceBox;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
use super::{BoxDeclaration, StaticBoxDefinition};
|
||||||
|
|
||||||
|
/// スレッド間で共有される状態
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SharedState {
|
||||||
|
/// 🌍 GlobalBox - すべてのトップレベル関数とグローバル変数を管理
|
||||||
|
pub global_box: Arc<Mutex<InstanceBox>>,
|
||||||
|
|
||||||
|
/// Box宣言のレジストリ(読み込みが多いのでRwLock)
|
||||||
|
pub box_declarations: Arc<RwLock<HashMap<String, BoxDeclaration>>>,
|
||||||
|
|
||||||
|
/// 🔥 静的関数のレジストリ(読み込みが多いのでRwLock)
|
||||||
|
pub static_functions: Arc<RwLock<HashMap<String, HashMap<String, crate::ast::ASTNode>>>>,
|
||||||
|
|
||||||
|
/// 🔥 Static Box定義レジストリ(遅延初期化用)
|
||||||
|
pub static_box_definitions: Arc<RwLock<HashMap<String, StaticBoxDefinition>>>,
|
||||||
|
|
||||||
|
/// 読み込み済みファイル(重複防止)
|
||||||
|
pub included_files: Arc<Mutex<HashSet<String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SharedState {
|
||||||
|
/// 新しい共有状態を作成
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let global_box = InstanceBox::new(
|
||||||
|
"Global".to_string(),
|
||||||
|
vec![], // フィールド名(空から始める)
|
||||||
|
HashMap::new(), // メソッド(グローバル関数)
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
global_box: Arc::new(Mutex::new(global_box)),
|
||||||
|
box_declarations: Arc::new(RwLock::new(HashMap::new())),
|
||||||
|
static_functions: Arc::new(RwLock::new(HashMap::new())),
|
||||||
|
static_box_definitions: Arc::new(RwLock::new(HashMap::new())),
|
||||||
|
included_files: Arc::new(Mutex::new(HashSet::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,12 +10,17 @@ use super::*;
|
|||||||
use super::BuiltinStdlib;
|
use super::BuiltinStdlib;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
// Conditional debug macro - only outputs if NYASH_DEBUG=1 environment variable is set
|
// Conditional debug macro - unified with utils::debug_on()
|
||||||
macro_rules! debug_trace {
|
macro_rules! debug_trace {
|
||||||
($($arg:tt)*) => {
|
($($arg:tt)*) => {
|
||||||
if std::env::var("NYASH_DEBUG").unwrap_or_default() == "1" {
|
if crate::interpreter::utils::debug_on() { eprintln!($($arg)*); }
|
||||||
eprintln!($($arg)*);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Local debug helper
|
||||||
|
macro_rules! idebug {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
if crate::interpreter::utils::debug_on() { eprintln!($($arg)*); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,10 +148,10 @@ impl NyashInterpreter {
|
|||||||
.insert(func_name.clone(), func_ast);
|
.insert(func_name.clone(), func_ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!("🔥 Static function '{}.{}' registered", box_name, func_name);
|
idebug!("🔥 Static function '{}.{}' registered", box_name, func_name);
|
||||||
} else {
|
} else {
|
||||||
// box名なしのstatic関数(将来的にはエラーにする)
|
// box名なしのstatic関数(将来的にはエラーにする)
|
||||||
eprintln!("⚠️ Static function '{}' needs box prefix (e.g., Math.min)", name);
|
idebug!("⚠️ Static function '{}' needs box prefix (e.g., Math.min)", name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 通常の関数:従来通りGlobalBoxメソッドとして登録
|
// 通常の関数:従来通りGlobalBoxメソッドとして登録
|
||||||
@ -566,7 +571,7 @@ impl NyashInterpreter {
|
|||||||
|
|
||||||
/// using文を実行 - Import namespace
|
/// using文を実行 - Import namespace
|
||||||
pub(super) fn execute_using_statement(&mut self, namespace_name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
pub(super) fn execute_using_statement(&mut self, namespace_name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||||
eprintln!("🌟 DEBUG: execute_using_statement called with namespace: {}", namespace_name);
|
idebug!("🌟 DEBUG: execute_using_statement called with namespace: {}", namespace_name);
|
||||||
|
|
||||||
// Phase 0: nyashstdのみサポート
|
// Phase 0: nyashstdのみサポート
|
||||||
if namespace_name != "nyashstd" {
|
if namespace_name != "nyashstd" {
|
||||||
@ -576,9 +581,9 @@ impl NyashInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 標準ライブラリを初期化(存在しない場合)
|
// 標準ライブラリを初期化(存在しない場合)
|
||||||
eprintln!("🌟 DEBUG: About to call ensure_stdlib_initialized");
|
idebug!("🌟 DEBUG: About to call ensure_stdlib_initialized");
|
||||||
self.ensure_stdlib_initialized()?;
|
self.ensure_stdlib_initialized()?;
|
||||||
eprintln!("🌟 DEBUG: ensure_stdlib_initialized completed");
|
idebug!("🌟 DEBUG: ensure_stdlib_initialized completed");
|
||||||
|
|
||||||
// using nyashstdの場合は特に何もしない(既に標準ライブラリが初期化されている)
|
// using nyashstdの場合は特に何もしない(既に標準ライブラリが初期化されている)
|
||||||
Ok(Box::new(VoidBox::new()))
|
Ok(Box::new(VoidBox::new()))
|
||||||
@ -587,9 +592,9 @@ impl NyashInterpreter {
|
|||||||
/// 標準ライブラリの初期化を確保
|
/// 標準ライブラリの初期化を確保
|
||||||
fn ensure_stdlib_initialized(&mut self) -> Result<(), RuntimeError> {
|
fn ensure_stdlib_initialized(&mut self) -> Result<(), RuntimeError> {
|
||||||
if self.stdlib.is_none() {
|
if self.stdlib.is_none() {
|
||||||
eprintln!("🌟 Initializing BuiltinStdlib...");
|
idebug!("🌟 Initializing BuiltinStdlib...");
|
||||||
self.stdlib = Some(BuiltinStdlib::new());
|
self.stdlib = Some(BuiltinStdlib::new());
|
||||||
eprintln!("✅ BuiltinStdlib initialized successfully");
|
idebug!("✅ BuiltinStdlib initialized successfully");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,49 +1,7 @@
|
|||||||
/*!
|
//! Interpreter utilities: debug toggles
|
||||||
* Utility Functions Module
|
|
||||||
*
|
|
||||||
* Extracted from expressions.rs lines 1033-1085 (~52 lines)
|
|
||||||
* Handles utility functions for object identification and hash calculations
|
|
||||||
* Core philosophy: "Everything is Box" with helper utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
use super::*;
|
/// Global debug toggle for interpreter layer (NYASH_DEBUG=1)
|
||||||
|
pub fn debug_on() -> bool {
|
||||||
impl NyashInterpreter {
|
std::env::var("NYASH_DEBUG").unwrap_or_default() == "1"
|
||||||
/// 🔄 循環参照検出: オブジェクトの一意IDを取得
|
|
||||||
pub(super) fn get_object_id(&self, node: &ASTNode) -> Option<usize> {
|
|
||||||
match node {
|
|
||||||
ASTNode::Variable { name, .. } => {
|
|
||||||
// 変数名のハッシュをIDとして使用
|
|
||||||
Some(self.hash_string(name))
|
|
||||||
}
|
|
||||||
ASTNode::Me { .. } => {
|
|
||||||
// 'me'参照の特別なID
|
|
||||||
Some(usize::MAX)
|
|
||||||
}
|
|
||||||
ASTNode::This { .. } => {
|
|
||||||
// 'this'参照の特別なID
|
|
||||||
Some(usize::MAX - 1)
|
|
||||||
}
|
|
||||||
_ => None, // 他のノードタイプはID追跡しない
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 🔄 文字列のシンプルなハッシュ関数
|
|
||||||
pub(super) fn hash_string(&self, s: &str) -> usize {
|
|
||||||
let mut hash = 0usize;
|
|
||||||
for byte in s.bytes() {
|
|
||||||
hash = hash.wrapping_mul(31).wrapping_add(byte as usize);
|
|
||||||
}
|
|
||||||
hash
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 🔗 Convert NyashBox to NyashValue for weak reference operations
|
|
||||||
/// Note: Currently commented out due to complexity, to be implemented in future phases
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn box_to_nyash_value(&self, _box_val: &Box<dyn NyashBox>) -> Option<crate::value::NyashValue> {
|
|
||||||
// This is a placeholder for future weak reference implementation
|
|
||||||
// When implemented, this will convert Box types back to NyashValue
|
|
||||||
// for proper weak reference storage and management
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -125,6 +125,18 @@ impl MessageBusData {
|
|||||||
pub fn get_nodes(&self) -> Vec<String> {
|
pub fn get_nodes(&self) -> Vec<String> {
|
||||||
self.nodes.keys().cloned().collect()
|
self.nodes.keys().cloned().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 条件付きでノードを解除(同一エンドポイントの場合のみ)
|
||||||
|
pub fn unregister_if_same(&mut self, id: &str, endpoint: &BusEndpoint) -> bool {
|
||||||
|
if let Some(current) = self.nodes.get(id) {
|
||||||
|
let a = std::sync::Arc::as_ptr(¤t.handlers);
|
||||||
|
let b = std::sync::Arc::as_ptr(&endpoint.handlers);
|
||||||
|
if std::ptr::eq(a, b) {
|
||||||
|
return self.unregister_node(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// グローバルMessageBusシングルトン
|
/// グローバルMessageBusシングルトン
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use crate::instance_v2::InstanceBox;
|
|||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
/// BoxType enum - ChatGPT先生の提案に従い、Box型を分類
|
/// BoxType enum - ChatGPT先生の提案に従い、Box型を分類
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -81,9 +82,10 @@ impl MethodBox {
|
|||||||
|
|
||||||
/// メソッドを呼び出す
|
/// メソッドを呼び出す
|
||||||
pub fn invoke(&self, _args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, String> {
|
pub fn invoke(&self, _args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, String> {
|
||||||
// TODO: インタープリタとの統合が必要
|
if let Some(invoker) = METHOD_INVOKER.get() {
|
||||||
// 現在は仮実装
|
return invoker.invoke(self, _args);
|
||||||
Err(format!("MethodBox.invoke not yet implemented for method '{}'", self.method_name))
|
}
|
||||||
|
Err(format!("MethodBox.invoke not configured (no invoker) for method '{}'", self.method_name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// インスタンスを取得(内部使用)
|
/// インスタンスを取得(内部使用)
|
||||||
@ -92,6 +94,17 @@ impl MethodBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Global invoker hook to connect MethodBox to the interpreter
|
||||||
|
pub trait MethodInvoker: Send + Sync {
|
||||||
|
fn invoke(&self, method: &MethodBox, args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
static METHOD_INVOKER: OnceCell<Arc<dyn MethodInvoker + Send + Sync>> = OnceCell::new();
|
||||||
|
|
||||||
|
pub fn set_method_invoker(invoker: Arc<dyn MethodInvoker + Send + Sync>) {
|
||||||
|
let _ = METHOD_INVOKER.set(invoker);
|
||||||
|
}
|
||||||
|
|
||||||
impl NyashBox for MethodBox {
|
impl NyashBox for MethodBox {
|
||||||
fn to_string_box(&self) -> StringBox {
|
fn to_string_box(&self) -> StringBox {
|
||||||
StringBox::new(format!("<MethodBox: {}>", self.method_name))
|
StringBox::new(format!("<MethodBox: {}>", self.method_name))
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::mir::TypeOpKind;
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@ -75,77 +76,46 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn emit_type_check(&mut self, value: ValueId, expected_type: String) -> Result<ValueId, String> {
|
pub(super) fn emit_type_check(&mut self, value: ValueId, expected_type: String) -> Result<ValueId, String> {
|
||||||
let target_value = self.value_gen.next_value_id();
|
let dst = self.value_gen.next();
|
||||||
|
|
||||||
let instruction = MirInstruction::TypeOp {
|
let instruction = MirInstruction::TypeOp {
|
||||||
dst: target_value,
|
dst,
|
||||||
operation: super::TypeOpKind::Check,
|
op: TypeOpKind::Check,
|
||||||
operand: value,
|
value,
|
||||||
type_info: expected_type,
|
ty: MirType::Box(expected_type),
|
||||||
effects: EffectMask::new(Effect::ReadOnly),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.emit_instruction(instruction)?;
|
self.emit_instruction(instruction)?;
|
||||||
Ok(target_value)
|
Ok(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn emit_cast(&mut self, value: ValueId, target_type: super::MirType) -> Result<ValueId, String> {
|
pub(super) fn emit_cast(&mut self, value: ValueId, target_type: super::MirType) -> Result<ValueId, String> {
|
||||||
let target_value = self.value_gen.next_value_id();
|
let dst = self.value_gen.next();
|
||||||
|
let instruction = MirInstruction::TypeOp { dst, op: TypeOpKind::Cast, value, ty: target_type };
|
||||||
let instruction = MirInstruction::TypeOp {
|
|
||||||
dst: target_value,
|
|
||||||
operation: super::TypeOpKind::Cast,
|
|
||||||
operand: value,
|
|
||||||
type_info: format!("{:?}", target_type),
|
|
||||||
effects: EffectMask::new(Effect::ReadOnly),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.emit_instruction(instruction)?;
|
self.emit_instruction(instruction)?;
|
||||||
Ok(target_value)
|
Ok(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn emit_weak_new(&mut self, box_val: ValueId) -> Result<ValueId, String> {
|
pub(super) fn emit_weak_new(&mut self, box_val: ValueId) -> Result<ValueId, String> {
|
||||||
let weak_ref = self.value_gen.next_value_id();
|
let dst = self.value_gen.next();
|
||||||
|
let instruction = MirInstruction::WeakNew { dst, box_val };
|
||||||
let instruction = MirInstruction::WeakNew {
|
|
||||||
dst: weak_ref,
|
|
||||||
source: box_val,
|
|
||||||
effects: EffectMask::new(Effect::Pure),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.emit_instruction(instruction)?;
|
self.emit_instruction(instruction)?;
|
||||||
Ok(weak_ref)
|
Ok(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn emit_weak_load(&mut self, weak_ref: ValueId) -> Result<ValueId, String> {
|
pub(super) fn emit_weak_load(&mut self, weak_ref: ValueId) -> Result<ValueId, String> {
|
||||||
let loaded_value = self.value_gen.next_value_id();
|
let dst = self.value_gen.next();
|
||||||
|
let instruction = MirInstruction::WeakLoad { dst, weak_ref };
|
||||||
let instruction = MirInstruction::WeakLoad {
|
|
||||||
dst: loaded_value,
|
|
||||||
weak_ref,
|
|
||||||
effects: EffectMask::new(Effect::ReadOnly),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.emit_instruction(instruction)?;
|
self.emit_instruction(instruction)?;
|
||||||
Ok(loaded_value)
|
Ok(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn emit_barrier_read(&mut self, ptr: ValueId) -> Result<(), String> {
|
pub(super) fn emit_barrier_read(&mut self, ptr: ValueId) -> Result<(), String> {
|
||||||
let instruction = MirInstruction::BarrierRead {
|
let instruction = MirInstruction::BarrierRead { ptr };
|
||||||
ptr,
|
|
||||||
effects: EffectMask::new(Effect::SideEffect),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.emit_instruction(instruction)?;
|
self.emit_instruction(instruction)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn emit_barrier_write(&mut self, ptr: ValueId) -> Result<(), String> {
|
pub(super) fn emit_barrier_write(&mut self, ptr: ValueId) -> Result<(), String> {
|
||||||
let instruction = MirInstruction::BarrierWrite {
|
let instruction = MirInstruction::BarrierWrite { ptr };
|
||||||
ptr,
|
|
||||||
effects: EffectMask::new(Effect::SideEffect),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.emit_instruction(instruction)?;
|
self.emit_instruction(instruction)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -162,18 +132,16 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let current_block_id = self.current_block.unwrap();
|
let current_block_id = self.current_block.unwrap();
|
||||||
|
// Ensure the block exists first (uses &mut self)
|
||||||
// Get a mutable reference to the current function
|
|
||||||
let current_function = self.current_function.as_mut().unwrap();
|
|
||||||
|
|
||||||
// Ensure the block exists
|
|
||||||
self.ensure_block_exists(current_block_id)?;
|
self.ensure_block_exists(current_block_id)?;
|
||||||
|
// Then borrow current_function mutably to add instruction
|
||||||
// Add instruction to current block
|
{
|
||||||
if let Some(block) = current_function.basic_blocks.get_mut(¤t_block_id) {
|
let f = self.current_function.as_mut().unwrap();
|
||||||
block.instructions.push(instruction);
|
if let Some(bb) = f.get_block_mut(current_block_id) {
|
||||||
} else {
|
bb.add_instruction(instruction);
|
||||||
return Err(format!("Block {:?} not found in current function", current_block_id));
|
} else {
|
||||||
|
return Err(format!("Block {:?} not found in current function", current_block_id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -183,11 +151,8 @@ impl MirBuilder {
|
|||||||
let current_function = self.current_function.as_mut()
|
let current_function = self.current_function.as_mut()
|
||||||
.ok_or("No current function")?;
|
.ok_or("No current function")?;
|
||||||
|
|
||||||
if !current_function.basic_blocks.contains_key(&block_id) {
|
if current_function.get_block(block_id).is_none() {
|
||||||
current_function.basic_blocks.insert(block_id, BasicBlock {
|
current_function.add_block(BasicBlock::new(block_id));
|
||||||
id: block_id,
|
|
||||||
instructions: Vec::new(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -204,6 +169,51 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MirBuilder {
|
||||||
|
/// Build a MIR module from AST (thin outer shell aligning with legacy builder)
|
||||||
|
pub fn build_module(&mut self, ast: ASTNode) -> Result<MirModule, String> {
|
||||||
|
// Create empty module and a main function
|
||||||
|
let module = MirModule::new("main".to_string());
|
||||||
|
let main_signature = FunctionSignature {
|
||||||
|
name: "main".to_string(),
|
||||||
|
params: vec![],
|
||||||
|
return_type: MirType::Void,
|
||||||
|
effects: EffectMask::PURE,
|
||||||
|
};
|
||||||
|
let entry_block = self.block_gen.next();
|
||||||
|
let mut main_function = MirFunction::new(main_signature, entry_block);
|
||||||
|
main_function.metadata.is_entry_point = true;
|
||||||
|
|
||||||
|
// Set context
|
||||||
|
self.current_module = Some(module);
|
||||||
|
self.current_function = Some(main_function);
|
||||||
|
self.current_block = Some(entry_block);
|
||||||
|
|
||||||
|
// Entry safepoint
|
||||||
|
self.emit_instruction(MirInstruction::Safepoint)?;
|
||||||
|
|
||||||
|
// Lower AST to MIR
|
||||||
|
let result_value = self.build_expression(ast)?;
|
||||||
|
|
||||||
|
// Ensure a return at the end
|
||||||
|
if let Some(block_id) = self.current_block {
|
||||||
|
if let Some(ref mut f) = self.current_function {
|
||||||
|
if let Some(bb) = f.get_block_mut(block_id) {
|
||||||
|
if !bb.is_terminated() {
|
||||||
|
bb.add_instruction(MirInstruction::Return { value: Some(result_value) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize
|
||||||
|
let mut module = self.current_module.take().unwrap();
|
||||||
|
let function = self.current_function.take().unwrap();
|
||||||
|
module.add_function(function);
|
||||||
|
Ok(module)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Adapter: Implement LoopBuilderApi for modularized MirBuilder to enable shared helpers
|
// Adapter: Implement LoopBuilderApi for modularized MirBuilder to enable shared helpers
|
||||||
impl crate::mir::loop_api::LoopBuilderApi for MirBuilder {
|
impl crate::mir::loop_api::LoopBuilderApi for MirBuilder {
|
||||||
fn new_block(&mut self) -> super::BasicBlockId { self.block_gen.next() }
|
fn new_block(&mut self) -> super::BasicBlockId { self.block_gen.next() }
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::mir::TypeOpKind;
|
||||||
use crate::ast::{ASTNode, LiteralValue, BinaryOperator};
|
use crate::ast::{ASTNode, LiteralValue, BinaryOperator};
|
||||||
|
|
||||||
/// Binary operation type classification
|
/// Binary operation type classification
|
||||||
@ -179,7 +180,7 @@ impl MirBuilder {
|
|||||||
let val = self.build_expression(args[0].clone())?;
|
let val = self.build_expression(args[0].clone())?;
|
||||||
let ty = Self::parse_type_name_to_mir(&type_name);
|
let ty = Self::parse_type_name_to_mir(&type_name);
|
||||||
let dst = self.value_gen.next();
|
let dst = self.value_gen.next();
|
||||||
let op = if name == "isType" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
|
let op = if name == "isType" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
|
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
|
||||||
return Ok(dst);
|
return Ok(dst);
|
||||||
}
|
}
|
||||||
@ -211,7 +212,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse type name string to MIR type
|
/// Parse type name string to MIR type
|
||||||
fn parse_type_name_to_mir(name: &str) -> super::MirType {
|
pub(super) fn parse_type_name_to_mir(name: &str) -> super::MirType {
|
||||||
match name {
|
match name {
|
||||||
"Integer" | "Int" | "I64" => super::MirType::Integer,
|
"Integer" | "Int" | "I64" => super::MirType::Integer,
|
||||||
"Float" | "F64" => super::MirType::Float,
|
"Float" | "F64" => super::MirType::Float,
|
||||||
@ -224,7 +225,7 @@ impl MirBuilder {
|
|||||||
|
|
||||||
/// Extract string literal from AST node if possible
|
/// Extract string literal from AST node if possible
|
||||||
/// Supports: Literal("Type") and new StringBox("Type")
|
/// Supports: Literal("Type") and new StringBox("Type")
|
||||||
fn extract_string_literal(node: &ASTNode) -> Option<String> {
|
pub(super) fn extract_string_literal(node: &ASTNode) -> Option<String> {
|
||||||
let mut cur = node;
|
let mut cur = node;
|
||||||
loop {
|
loop {
|
||||||
match cur {
|
match cur {
|
||||||
@ -266,7 +267,7 @@ impl MirBuilder {
|
|||||||
// Map string to MIR type
|
// Map string to MIR type
|
||||||
let mir_ty = Self::parse_type_name_to_mir(&type_name);
|
let mir_ty = Self::parse_type_name_to_mir(&type_name);
|
||||||
let dst = self.value_gen.next();
|
let dst = self.value_gen.next();
|
||||||
let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
|
let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
|
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
|
||||||
return Ok(dst);
|
return Ok(dst);
|
||||||
}
|
}
|
||||||
@ -315,7 +316,7 @@ impl MirBuilder {
|
|||||||
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
|
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
|
||||||
let mir_ty = Self::parse_type_name_to_mir(&type_name);
|
let mir_ty = Self::parse_type_name_to_mir(&type_name);
|
||||||
let dst = self.value_gen.next();
|
let dst = self.value_gen.next();
|
||||||
let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
|
let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
|
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
|
||||||
return Ok(dst);
|
return Ok(dst);
|
||||||
}
|
}
|
||||||
@ -397,12 +398,13 @@ impl MirBuilder {
|
|||||||
if let Some(&parent_me) = self.variable_map.get(&format!("{}.__me__", parent)) {
|
if let Some(&parent_me) = self.variable_map.get(&format!("{}.__me__", parent)) {
|
||||||
// Emit from call with parent's me
|
// Emit from call with parent's me
|
||||||
let result = self.value_gen.next();
|
let result = self.value_gen.next();
|
||||||
self.emit_instruction(MirInstruction::Call {
|
let func_id = self.value_gen.next(); // Placeholder for from resolution
|
||||||
dst: Some(result),
|
self.emit_instruction(MirInstruction::Call {
|
||||||
func: self.value_gen.next(), // Placeholder for from resolution
|
dst: Some(result),
|
||||||
args: vec![parent_me], // Include parent's me plus other args
|
func: func_id,
|
||||||
effects: EffectMask::READ.add(Effect::ReadHeap),
|
args: vec![parent_me], // Include parent's me plus other args
|
||||||
})?;
|
effects: EffectMask::READ.add(Effect::ReadHeap),
|
||||||
|
})?;
|
||||||
Ok(result)
|
Ok(result)
|
||||||
} else {
|
} else {
|
||||||
// Fallback behavior without proper parent context
|
// Fallback behavior without proper parent context
|
||||||
@ -445,7 +447,7 @@ impl MirBuilder {
|
|||||||
let obj_val = self.build_expression(*object.clone())?;
|
let obj_val = self.build_expression(*object.clone())?;
|
||||||
let ty = Self::parse_type_name_to_mir(&type_name);
|
let ty = Self::parse_type_name_to_mir(&type_name);
|
||||||
let dst = self.value_gen.next();
|
let dst = self.value_gen.next();
|
||||||
let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
|
let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
|
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
|
||||||
return Ok(dst);
|
return Ok(dst);
|
||||||
}
|
}
|
||||||
@ -587,11 +589,7 @@ impl MirBuilder {
|
|||||||
let result_id = self.value_gen.next();
|
let result_id = self.value_gen.next();
|
||||||
|
|
||||||
// Emit await instruction
|
// Emit await instruction
|
||||||
self.emit_instruction(MirInstruction::Await {
|
self.emit_instruction(MirInstruction::Await { dst: result_id, future: future_value })?;
|
||||||
dst: result_id,
|
|
||||||
future: future_value,
|
|
||||||
effects: EffectMask::READ.add(Effect::Async),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(result_id)
|
Ok(result_id)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,5 +23,5 @@ pub use self::core::MirBuilder;
|
|||||||
pub use super::{
|
pub use super::{
|
||||||
MirInstruction, BasicBlock, BasicBlockId, MirFunction, MirModule,
|
MirInstruction, BasicBlock, BasicBlockId, MirFunction, MirModule,
|
||||||
FunctionSignature, ValueId, ConstValue, BinaryOp, UnaryOp, CompareOp,
|
FunctionSignature, ValueId, ConstValue, BinaryOp, UnaryOp, CompareOp,
|
||||||
MirType, EffectMask, Effect, BasicBlockIdGenerator, ValueIdGenerator
|
MirType, EffectMask, Effect, BasicBlockIdGenerator, ValueIdGenerator, TypeOpKind
|
||||||
};
|
};
|
||||||
@ -5,6 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::mir::builder_modularized::core::builder_debug_log;
|
||||||
|
use crate::mir::TypeOpKind;
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
|
|
||||||
impl MirBuilder {
|
impl MirBuilder {
|
||||||
@ -20,7 +22,7 @@ impl MirBuilder {
|
|||||||
let val = self.build_expression(arguments[0].clone())?;
|
let val = self.build_expression(arguments[0].clone())?;
|
||||||
let ty = Self::parse_type_name_to_mir(&type_name);
|
let ty = Self::parse_type_name_to_mir(&type_name);
|
||||||
let dst = self.value_gen.next();
|
let dst = self.value_gen.next();
|
||||||
let op = if name == "isType" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
|
let op = if name == "isType" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||||
builder_debug_log(&format!("emit TypeOp {:?} value={} dst= {}", op, val, dst));
|
builder_debug_log(&format!("emit TypeOp {:?} value={} dst= {}", op, val, dst));
|
||||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
|
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
|
||||||
self.emit_instruction(MirInstruction::Print { value: dst, effects: EffectMask::PURE.add(Effect::Io) })?;
|
self.emit_instruction(MirInstruction::Print { value: dst, effects: EffectMask::PURE.add(Effect::Io) })?;
|
||||||
@ -36,7 +38,7 @@ impl MirBuilder {
|
|||||||
let obj_val = self.build_expression(*object.clone())?;
|
let obj_val = self.build_expression(*object.clone())?;
|
||||||
let ty = Self::parse_type_name_to_mir(&type_name);
|
let ty = Self::parse_type_name_to_mir(&type_name);
|
||||||
let dst = self.value_gen.next();
|
let dst = self.value_gen.next();
|
||||||
let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
|
let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||||
builder_debug_log(&format!("emit TypeOp {:?} obj={} dst= {}", op, obj_val, dst));
|
builder_debug_log(&format!("emit TypeOp {:?} obj={} dst= {}", op, obj_val, dst));
|
||||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
|
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
|
||||||
self.emit_instruction(MirInstruction::Print { value: dst, effects: EffectMask::PURE.add(Effect::Io) })?;
|
self.emit_instruction(MirInstruction::Print { value: dst, effects: EffectMask::PURE.add(Effect::Io) })?;
|
||||||
|
|||||||
@ -11,6 +11,8 @@ pub mod instruction_introspection; // Introspection helpers for tests (core inst
|
|||||||
pub mod basic_block;
|
pub mod basic_block;
|
||||||
pub mod function;
|
pub mod function;
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
|
#[cfg(feature = "mir_modular_builder")]
|
||||||
|
pub mod builder_modularized; // Modular split of MIR builder (optional)
|
||||||
pub mod loop_builder; // SSA loop construction with phi nodes
|
pub mod loop_builder; // SSA loop construction with phi nodes
|
||||||
pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready)
|
pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready)
|
||||||
pub mod verification;
|
pub mod verification;
|
||||||
@ -25,7 +27,10 @@ pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue,
|
|||||||
pub use instruction_v2::{MirInstructionV2, AtomicOrdering}; // New 25-instruction set
|
pub use instruction_v2::{MirInstructionV2, AtomicOrdering}; // New 25-instruction set
|
||||||
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
||||||
pub use function::{MirFunction, MirModule, FunctionSignature};
|
pub use function::{MirFunction, MirModule, FunctionSignature};
|
||||||
|
#[cfg(not(feature = "mir_modular_builder"))]
|
||||||
pub use builder::MirBuilder;
|
pub use builder::MirBuilder;
|
||||||
|
#[cfg(feature = "mir_modular_builder")]
|
||||||
|
pub use builder_modularized::MirBuilder;
|
||||||
pub use verification::{MirVerifier, VerificationError};
|
pub use verification::{MirVerifier, VerificationError};
|
||||||
pub use ownership_verifier_simple::{OwnershipVerifier, OwnershipError, OwnershipStats}; // Simple ownership forest verification
|
pub use ownership_verifier_simple::{OwnershipVerifier, OwnershipError, OwnershipStats}; // Simple ownership forest verification
|
||||||
pub use printer::MirPrinter;
|
pub use printer::MirPrinter;
|
||||||
|
|||||||
@ -5,20 +5,20 @@
|
|||||||
* Behavior: Quiet by default; use NYASH_CLI_VERBOSE=1 or NYASH_DEBUG_PLUGIN=1 for logs
|
* Behavior: Quiet by default; use NYASH_CLI_VERBOSE=1 or NYASH_DEBUG_PLUGIN=1 for logs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::runtime::{init_global_loader_v2, get_global_registry, get_global_loader_v2, PluginConfig};
|
use crate::runtime::{init_global_plugin_host, get_global_registry, get_global_plugin_host, PluginConfig};
|
||||||
|
|
||||||
pub fn init_bid_plugins() {
|
pub fn init_bid_plugins() {
|
||||||
let cli_verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1");
|
let cli_verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1");
|
||||||
let plugin_debug = std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1");
|
let plugin_debug = std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1");
|
||||||
if plugin_debug { eprintln!("🔍 DEBUG: Initializing v2 plugin system"); }
|
if plugin_debug { eprintln!("🔍 DEBUG: Initializing v2 plugin system"); }
|
||||||
|
|
||||||
if let Ok(()) = init_global_loader_v2("nyash.toml") {
|
if let Ok(()) = init_global_plugin_host("nyash.toml") {
|
||||||
if plugin_debug || cli_verbose {
|
if plugin_debug || cli_verbose {
|
||||||
println!("🔌 v2 plugin system initialized from nyash.toml");
|
println!("🔌 plugin host initialized from nyash.toml");
|
||||||
}
|
}
|
||||||
let loader = get_global_loader_v2();
|
let host = get_global_plugin_host();
|
||||||
let loader = loader.read().unwrap();
|
let host = host.read().unwrap();
|
||||||
if let Some(config) = &loader.config {
|
if let Some(config) = host.config_ref() {
|
||||||
let registry = get_global_registry();
|
let registry = get_global_registry();
|
||||||
for (lib_name, lib_def) in &config.libraries {
|
for (lib_name, lib_def) in &config.libraries {
|
||||||
for box_name in &lib_def.boxes {
|
for box_name in &lib_def.boxes {
|
||||||
@ -27,7 +27,7 @@ pub fn init_bid_plugins() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if plugin_debug || cli_verbose {
|
if plugin_debug || cli_verbose {
|
||||||
println!("✅ v2 plugin system fully configured");
|
println!("✅ plugin host fully configured");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if plugin_debug || cli_verbose {
|
} else if plugin_debug || cli_verbose {
|
||||||
|
|||||||
@ -75,16 +75,12 @@ impl BoxFactoryRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// プラグインBoxを生成(v2実装)
|
/// プラグインBoxを生成(unified facade→v2)
|
||||||
fn create_plugin_box(&self, plugin_name: &str, box_name: &str, args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, String> {
|
fn create_plugin_box(&self, plugin_name: &str, box_name: &str, args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, String> {
|
||||||
use crate::runtime::get_global_loader_v2;
|
use crate::runtime::get_global_plugin_host;
|
||||||
|
let host = get_global_plugin_host();
|
||||||
// v2ローダーを取得
|
let host = host.read().unwrap();
|
||||||
let loader = get_global_loader_v2();
|
host.create_box(box_name, args)
|
||||||
let loader = loader.read().unwrap();
|
|
||||||
|
|
||||||
// プラグインからBoxを生成
|
|
||||||
loader.create_box(box_name, args)
|
|
||||||
.map_err(|e| format!("Failed to create {} from plugin {}: {:?}", box_name, plugin_name, e))
|
.map_err(|e| format!("Failed to create {} from plugin {}: {:?}", box_name, plugin_name, e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
pub mod plugin_config;
|
pub mod plugin_config;
|
||||||
pub mod box_registry;
|
pub mod box_registry;
|
||||||
pub mod plugin_loader_v2;
|
pub mod plugin_loader_v2;
|
||||||
|
pub mod plugin_loader_unified;
|
||||||
|
pub mod plugin_ffi_common;
|
||||||
pub mod leak_tracker;
|
pub mod leak_tracker;
|
||||||
pub mod unified_registry;
|
pub mod unified_registry;
|
||||||
pub mod nyash_runtime;
|
pub mod nyash_runtime;
|
||||||
@ -17,6 +19,7 @@ mod tests;
|
|||||||
pub use plugin_config::PluginConfig;
|
pub use plugin_config::PluginConfig;
|
||||||
pub use box_registry::{BoxFactoryRegistry, BoxProvider, get_global_registry};
|
pub use box_registry::{BoxFactoryRegistry, BoxProvider, get_global_registry};
|
||||||
pub use plugin_loader_v2::{PluginLoaderV2, get_global_loader_v2, init_global_loader_v2};
|
pub use plugin_loader_v2::{PluginLoaderV2, get_global_loader_v2, init_global_loader_v2};
|
||||||
|
pub use plugin_loader_unified::{PluginHost, get_global_plugin_host, init_global_plugin_host, PluginLibraryHandle, PluginBoxType, MethodHandle};
|
||||||
pub use unified_registry::{get_global_unified_registry, init_global_unified_registry, register_user_defined_factory};
|
pub use unified_registry::{get_global_unified_registry, init_global_unified_registry, register_user_defined_factory};
|
||||||
pub use nyash_runtime::{NyashRuntime, NyashRuntimeBuilder};
|
pub use nyash_runtime::{NyashRuntime, NyashRuntimeBuilder};
|
||||||
// pub use plugin_box::PluginBox; // legacy
|
// pub use plugin_box::PluginBox; // legacy
|
||||||
|
|||||||
78
src/runtime/plugin_ffi_common.rs
Normal file
78
src/runtime/plugin_ffi_common.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
//! Common FFI helpers for Plugin system
|
||||||
|
//! Minimal TLV utilities extracted for unified facade usage.
|
||||||
|
|
||||||
|
/// Encode empty TLV arguments: version=1, argc=0
|
||||||
|
pub fn encode_empty_args() -> Vec<u8> { vec![1u8, 0, 0, 0] }
|
||||||
|
|
||||||
|
/// Encode TLV header with argc (no payload entries encoded here)
|
||||||
|
pub fn encode_tlv_header(argc: u16) -> Vec<u8> {
|
||||||
|
let mut buf = Vec::with_capacity(4);
|
||||||
|
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||||
|
buf.extend_from_slice(&argc.to_le_bytes());
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple helpers for common primitive returns
|
||||||
|
pub mod decode {
|
||||||
|
/// Try to parse a u32 instance id from an output buffer (little-endian).
|
||||||
|
pub fn instance_id(buf: &[u8]) -> Option<u32> {
|
||||||
|
if buf.len() < 4 { return None; }
|
||||||
|
Some(u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse TLV header from buffer; returns (tag, size, payload_slice)
|
||||||
|
pub fn tlv_first(buf: &[u8]) -> Option<(u8, usize, &[u8])> {
|
||||||
|
if buf.len() < 8 { return None; }
|
||||||
|
let tag = buf[4];
|
||||||
|
let size = u16::from_le_bytes([buf[6], buf[7]]) as usize;
|
||||||
|
if buf.len() < 8 + size { return None; }
|
||||||
|
Some((tag, size, &buf[8..8 + size]))
|
||||||
|
}
|
||||||
|
/// Decode i32 payload (size must be 4)
|
||||||
|
pub fn i32(payload: &[u8]) -> Option<i32> {
|
||||||
|
if payload.len() != 4 { return None; }
|
||||||
|
let mut b = [0u8;4]; b.copy_from_slice(payload);
|
||||||
|
Some(i32::from_le_bytes(b))
|
||||||
|
}
|
||||||
|
/// Decode UTF-8 string/bytes
|
||||||
|
pub fn string(payload: &[u8]) -> String {
|
||||||
|
String::from_utf8_lossy(payload).to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TLV encode helpers for primitive Nyash values
|
||||||
|
pub mod encode {
|
||||||
|
/// tag for I32
|
||||||
|
const TAG_I32: u8 = 2;
|
||||||
|
/// tag for UTF-8 string
|
||||||
|
const TAG_STRING: u8 = 6;
|
||||||
|
/// tag for Plugin Handle (type_id + instance_id)
|
||||||
|
const TAG_HANDLE: u8 = 8;
|
||||||
|
|
||||||
|
/// Append an i32 TLV entry (tag=2, size=4, little-endian)
|
||||||
|
pub fn i32(buf: &mut Vec<u8>, v: i32) {
|
||||||
|
buf.push(TAG_I32);
|
||||||
|
buf.push(0u8); // reserved
|
||||||
|
buf.extend_from_slice(&(4u16).to_le_bytes());
|
||||||
|
buf.extend_from_slice(&v.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append a string TLV entry (tag=6, size=u16 trunc, UTF-8)
|
||||||
|
pub fn string(buf: &mut Vec<u8>, s: &str) {
|
||||||
|
let bytes = s.as_bytes();
|
||||||
|
let len = core::cmp::min(bytes.len(), u16::MAX as usize);
|
||||||
|
buf.push(TAG_STRING);
|
||||||
|
buf.push(0u8); // reserved
|
||||||
|
buf.extend_from_slice(&((len as u16).to_le_bytes()));
|
||||||
|
buf.extend_from_slice(&bytes[..len]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append a plugin handle TLV entry (tag=8, size=8, type_id:u32 + instance_id:u32)
|
||||||
|
pub fn plugin_handle(buf: &mut Vec<u8>, type_id: u32, instance_id: u32) {
|
||||||
|
buf.push(TAG_HANDLE);
|
||||||
|
buf.push(0u8); // reserved
|
||||||
|
buf.extend_from_slice(&(8u16).to_le_bytes());
|
||||||
|
buf.extend_from_slice(&type_id.to_le_bytes());
|
||||||
|
buf.extend_from_slice(&instance_id.to_le_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
133
src/runtime/plugin_loader_unified.rs
Normal file
133
src/runtime/plugin_loader_unified.rs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
//! Unified Plugin Host facade
|
||||||
|
//!
|
||||||
|
//! Thin wrapper over v2 loader to provide a stable facade
|
||||||
|
//! with minimal, friendly API for runtime/runner and future transports.
|
||||||
|
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
use crate::bid::{BidError, BidResult};
|
||||||
|
use crate::config::nyash_toml_v2::NyashConfigV2;
|
||||||
|
use crate::runtime::plugin_loader_v2::PluginLoaderV2;
|
||||||
|
|
||||||
|
/// Opaque library handle (by name for now)
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PluginLibraryHandle {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Box type descriptor
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PluginBoxType {
|
||||||
|
pub lib: String,
|
||||||
|
pub name: String,
|
||||||
|
pub type_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolved method handle
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MethodHandle {
|
||||||
|
pub lib: String,
|
||||||
|
pub box_type: String,
|
||||||
|
pub type_id: u32,
|
||||||
|
pub method_id: u32,
|
||||||
|
pub returns_result: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unified facade
|
||||||
|
pub struct PluginHost {
|
||||||
|
loader: Arc<RwLock<PluginLoaderV2>>, // delegate
|
||||||
|
config: Option<NyashConfigV2>, // cached config for resolution
|
||||||
|
config_path: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginHost {
|
||||||
|
pub fn new(loader: Arc<RwLock<PluginLoaderV2>>) -> Self {
|
||||||
|
Self { loader, config: None, config_path: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load config and dynamic libraries, keeping a local config cache.
|
||||||
|
pub fn load_libraries(&mut self, config_path: &str) -> BidResult<()> {
|
||||||
|
{
|
||||||
|
let mut l = self.loader.write().unwrap();
|
||||||
|
l.load_config(config_path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep our own copy for quick lookups
|
||||||
|
let canonical = std::fs::canonicalize(config_path)
|
||||||
|
.map(|p| p.to_string_lossy().to_string())
|
||||||
|
.unwrap_or_else(|_| config_path.to_string());
|
||||||
|
self.config = Some(NyashConfigV2::from_file(&canonical).map_err(|_| BidError::PluginError)?);
|
||||||
|
self.config_path = Some(canonical);
|
||||||
|
|
||||||
|
// Delegate actual library loads + pre-birth singletons to v2
|
||||||
|
let l = self.loader.read().unwrap();
|
||||||
|
l.load_all_plugins()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register built-ins or user-defined boxes if needed (no-op for now).
|
||||||
|
pub fn register_boxes(&self) -> BidResult<()> { Ok(()) }
|
||||||
|
|
||||||
|
/// Expose read-only view of loaded config for callers migrating from v2 paths.
|
||||||
|
pub fn config_ref(&self) -> Option<&NyashConfigV2> { self.config.as_ref() }
|
||||||
|
|
||||||
|
/// Resolve a method handle for a given plugin box type and method name.
|
||||||
|
pub fn resolve_method(&self, box_type: &str, method_name: &str) -> BidResult<MethodHandle> {
|
||||||
|
let cfg = self.config.as_ref().ok_or(BidError::PluginError)?;
|
||||||
|
let (lib_name, _lib_def) = cfg.find_library_for_box(box_type).ok_or(BidError::InvalidType)?;
|
||||||
|
let cfg_path = self.config_path.as_deref().unwrap_or("nyash.toml");
|
||||||
|
let toml_content = std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?;
|
||||||
|
let toml_value: toml::Value = toml::from_str(&toml_content).map_err(|_| BidError::PluginError)?;
|
||||||
|
let box_conf = cfg.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?;
|
||||||
|
let m = box_conf.methods.get(method_name).ok_or(BidError::InvalidMethod)?;
|
||||||
|
Ok(MethodHandle {
|
||||||
|
lib: lib_name.to_string(),
|
||||||
|
box_type: box_type.to_string(),
|
||||||
|
type_id: box_conf.type_id,
|
||||||
|
method_id: m.method_id,
|
||||||
|
returns_result: m.returns_result,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- v2 adapter layer: allow gradual migration of callers ---
|
||||||
|
pub fn create_box(&self, box_type: &str, args: &[Box<dyn crate::box_trait::NyashBox>]) -> BidResult<Box<dyn crate::box_trait::NyashBox>> {
|
||||||
|
let l = self.loader.read().unwrap();
|
||||||
|
l.create_box(box_type, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invoke_instance_method(
|
||||||
|
&self,
|
||||||
|
box_type: &str,
|
||||||
|
method_name: &str,
|
||||||
|
instance_id: u32,
|
||||||
|
args: &[Box<dyn crate::box_trait::NyashBox>],
|
||||||
|
) -> BidResult<Option<Box<dyn crate::box_trait::NyashBox>>> {
|
||||||
|
let l = self.loader.read().unwrap();
|
||||||
|
l.invoke_instance_method(box_type, method_name, instance_id, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extern_call(
|
||||||
|
&self,
|
||||||
|
iface_name: &str,
|
||||||
|
method_name: &str,
|
||||||
|
args: &[Box<dyn crate::box_trait::NyashBox>],
|
||||||
|
) -> BidResult<Option<Box<dyn crate::box_trait::NyashBox>>> {
|
||||||
|
let l = self.loader.read().unwrap();
|
||||||
|
l.extern_call(iface_name, method_name, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global singleton
|
||||||
|
static GLOBAL_HOST: Lazy<Arc<RwLock<PluginHost>>> = Lazy::new(|| {
|
||||||
|
let loader = crate::runtime::plugin_loader_v2::get_global_loader_v2();
|
||||||
|
Arc::new(RwLock::new(PluginHost::new(loader)))
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn get_global_plugin_host() -> Arc<RwLock<PluginHost>> { GLOBAL_HOST.clone() }
|
||||||
|
|
||||||
|
pub fn init_global_plugin_host(config_path: &str) -> BidResult<()> {
|
||||||
|
let host = get_global_plugin_host();
|
||||||
|
host.write().unwrap().load_libraries(config_path)?;
|
||||||
|
host.read().unwrap().register_boxes()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -294,7 +294,7 @@ impl PluginBoxV2 {
|
|||||||
// Call birth
|
// Call birth
|
||||||
let mut output_buffer = vec![0u8; 1024];
|
let mut output_buffer = vec![0u8; 1024];
|
||||||
let mut output_len = output_buffer.len();
|
let mut output_len = output_buffer.len();
|
||||||
let tlv_args = vec![1u8, 0, 0, 0];
|
let tlv_args = crate::runtime::plugin_ffi_common::encode_empty_args();
|
||||||
let birth_result = unsafe {
|
let birth_result = unsafe {
|
||||||
(plugin.invoke_fn)(type_id, 0, 0, tlv_args.as_ptr(), tlv_args.len(), output_buffer.as_mut_ptr(), &mut output_len)
|
(plugin.invoke_fn)(type_id, 0, 0, tlv_args.as_ptr(), tlv_args.len(), output_buffer.as_mut_ptr(), &mut output_len)
|
||||||
};
|
};
|
||||||
@ -378,10 +378,7 @@ impl PluginBoxV2 {
|
|||||||
eprintln!("[PluginLoaderV2] Invoke {}.{}: resolving and encoding args (argc={})", box_type, method_name, args.len());
|
eprintln!("[PluginLoaderV2] Invoke {}.{}: resolving and encoding args (argc={})", box_type, method_name, args.len());
|
||||||
// TLV args: encode using BID-1 style (u16 ver, u16 argc, then entries)
|
// TLV args: encode using BID-1 style (u16 ver, u16 argc, then entries)
|
||||||
let tlv_args = {
|
let tlv_args = {
|
||||||
let mut buf = Vec::with_capacity(4 + args.len() * 16);
|
let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header(args.len() as u16);
|
||||||
// Header: ver=1, argc=args.len()
|
|
||||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
|
||||||
buf.extend_from_slice(&(args.len() as u16).to_le_bytes());
|
|
||||||
// Validate against nyash.toml method args schema if present
|
// Validate against nyash.toml method args schema if present
|
||||||
let expected_args = box_conf.methods.get(method_name).and_then(|m| m.args.clone());
|
let expected_args = box_conf.methods.get(method_name).and_then(|m| m.args.clone());
|
||||||
if let Some(exp) = expected_args.as_ref() {
|
if let Some(exp) = expected_args.as_ref() {
|
||||||
@ -447,44 +444,27 @@ impl PluginBoxV2 {
|
|||||||
// Plugin Handle (BoxRef): tag=8, size=8
|
// Plugin Handle (BoxRef): tag=8, size=8
|
||||||
if let Some(p) = a.as_any().downcast_ref::<PluginBoxV2>() {
|
if let Some(p) = a.as_any().downcast_ref::<PluginBoxV2>() {
|
||||||
eprintln!("[PluginLoaderV2] arg[{}]: PluginBoxV2({}, id={}) -> Handle(tag=8)", idx, p.box_type, p.inner.instance_id);
|
eprintln!("[PluginLoaderV2] arg[{}]: PluginBoxV2({}, id={}) -> Handle(tag=8)", idx, p.box_type, p.inner.instance_id);
|
||||||
buf.push(8u8); // tag
|
crate::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.inner.instance_id);
|
||||||
buf.push(0u8); // reserved
|
|
||||||
buf.extend_from_slice(&(8u16).to_le_bytes());
|
|
||||||
buf.extend_from_slice(&p.inner.type_id.to_le_bytes());
|
|
||||||
buf.extend_from_slice(&p.inner.instance_id.to_le_bytes());
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Integer: prefer i32
|
// Integer: prefer i32
|
||||||
if let Some(i) = a.as_any().downcast_ref::<IntegerBox>() {
|
if let Some(i) = a.as_any().downcast_ref::<IntegerBox>() {
|
||||||
eprintln!("[PluginLoaderV2] arg[{}]: Integer({}) -> I32(tag=2)", idx, i.value);
|
eprintln!("[PluginLoaderV2] arg[{}]: Integer({}) -> I32(tag=2)", idx, i.value);
|
||||||
buf.push(2u8); // tag=I32
|
|
||||||
buf.push(0u8);
|
|
||||||
buf.extend_from_slice(&(4u16).to_le_bytes());
|
|
||||||
let v = i.value as i32;
|
let v = i.value as i32;
|
||||||
buf.extend_from_slice(&v.to_le_bytes());
|
crate::runtime::plugin_ffi_common::encode::i32(&mut buf, v);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// String: tag=6
|
// String: tag=6
|
||||||
if let Some(s) = a.as_any().downcast_ref::<StringBox>() {
|
if let Some(s) = a.as_any().downcast_ref::<StringBox>() {
|
||||||
eprintln!("[PluginLoaderV2] arg[{}]: String(len={}) -> String(tag=6)", idx, s.value.len());
|
eprintln!("[PluginLoaderV2] arg[{}]: String(len={}) -> String(tag=6)", idx, s.value.len());
|
||||||
let bytes = s.value.as_bytes();
|
crate::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value);
|
||||||
let len = std::cmp::min(bytes.len(), u16::MAX as usize);
|
|
||||||
buf.push(6u8);
|
|
||||||
buf.push(0u8);
|
|
||||||
buf.extend_from_slice(&((len as u16).to_le_bytes()));
|
|
||||||
buf.extend_from_slice(&bytes[..len]);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// No schema or unsupported type: only allow fallback when schema is None
|
// No schema or unsupported type: only allow fallback when schema is None
|
||||||
if expected_args.is_none() {
|
if expected_args.is_none() {
|
||||||
eprintln!("[PluginLoaderV2] arg[{}]: fallback stringify", idx);
|
eprintln!("[PluginLoaderV2] arg[{}]: fallback stringify", idx);
|
||||||
let sv = a.to_string_box().value;
|
let sv = a.to_string_box().value;
|
||||||
let bytes = sv.as_bytes();
|
crate::runtime::plugin_ffi_common::encode::string(&mut buf, &sv);
|
||||||
let len = std::cmp::min(bytes.len(), u16::MAX as usize);
|
|
||||||
buf.push(6u8);
|
|
||||||
buf.push(0u8);
|
|
||||||
buf.extend_from_slice(&((len as u16).to_le_bytes()));
|
|
||||||
buf.extend_from_slice(&bytes[..len]);
|
|
||||||
} else {
|
} else {
|
||||||
return Err(BidError::InvalidArgs);
|
return Err(BidError::InvalidArgs);
|
||||||
}
|
}
|
||||||
@ -540,12 +520,7 @@ impl PluginBoxV2 {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if data.len() < 8 { return Ok(None); }
|
if let Some((tag, size, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(data) {
|
||||||
let tag = data[4];
|
|
||||||
let _rsv = data[5];
|
|
||||||
let size = u16::from_le_bytes([data[6], data[7]]) as usize;
|
|
||||||
if data.len() < 8 + size { return Ok(None); }
|
|
||||||
let payload = &data[8..8+size];
|
|
||||||
match tag {
|
match tag {
|
||||||
8 if size == 8 => { // Handle -> PluginBoxV2
|
8 if size == 8 => { // Handle -> PluginBoxV2
|
||||||
let mut t = [0u8;4]; t.copy_from_slice(&payload[0..4]);
|
let mut t = [0u8;4]; t.copy_from_slice(&payload[0..4]);
|
||||||
@ -583,13 +558,13 @@ impl PluginBoxV2 {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
2 if size == 4 => { // I32
|
2 if size == 4 => { // I32
|
||||||
let mut b = [0u8;4]; b.copy_from_slice(payload);
|
let n = crate::runtime::plugin_ffi_common::decode::i32(payload).unwrap();
|
||||||
let val: Box<dyn NyashBox> = Box::new(IntegerBox::new(i32::from_le_bytes(b) as i64));
|
let val: Box<dyn NyashBox> = Box::new(IntegerBox::new(n as i64));
|
||||||
if dbg_on() { eprintln!("[Plugin→VM] return i32 value={} (returns_result={})", i32::from_le_bytes(b), returns_result); }
|
if dbg_on() { eprintln!("[Plugin→VM] return i32 value={} (returns_result={})", n, returns_result); }
|
||||||
if returns_result { Some(Box::new(crate::boxes::result::NyashResultBox::new_ok(val)) as Box<dyn NyashBox>) } else { Some(val) }
|
if returns_result { Some(Box::new(crate::boxes::result::NyashResultBox::new_ok(val)) as Box<dyn NyashBox>) } else { Some(val) }
|
||||||
}
|
}
|
||||||
6 | 7 => { // String/Bytes
|
6 | 7 => { // String/Bytes
|
||||||
let s = String::from_utf8_lossy(payload).to_string();
|
let s = crate::runtime::plugin_ffi_common::decode::string(payload);
|
||||||
if dbg_on() { eprintln!("[Plugin→VM] return str/bytes len={} (returns_result={})", size, returns_result); }
|
if dbg_on() { eprintln!("[Plugin→VM] return str/bytes len={} (returns_result={})", size, returns_result); }
|
||||||
if returns_result {
|
if returns_result {
|
||||||
// Heuristic: for Result-returning methods, string payload represents an error message
|
// Heuristic: for Result-returning methods, string payload represents an error message
|
||||||
@ -604,7 +579,7 @@ impl PluginBoxV2 {
|
|||||||
if returns_result { Some(Box::new(crate::boxes::result::NyashResultBox::new_ok(Box::new(crate::box_trait::VoidBox::new()))) as Box<dyn NyashBox>) } else { None }
|
if returns_result { Some(Box::new(crate::boxes::result::NyashResultBox::new_ok(Box::new(crate::box_trait::VoidBox::new()))) as Box<dyn NyashBox>) } else { None }
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}} else { None }
|
||||||
};
|
};
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
@ -746,7 +721,7 @@ impl PluginBoxV2 {
|
|||||||
let mut output_len = output_buffer.len();
|
let mut output_len = output_buffer.len();
|
||||||
|
|
||||||
// Create TLV-encoded empty arguments (version=1, argc=0)
|
// Create TLV-encoded empty arguments (version=1, argc=0)
|
||||||
let tlv_args = vec![1u8, 0, 0, 0]; // version=1, argc=0
|
let tlv_args = crate::runtime::plugin_ffi_common::encode_empty_args();
|
||||||
eprintln!("🔍 Output buffer allocated, about to call plugin invoke_fn...");
|
eprintln!("🔍 Output buffer allocated, about to call plugin invoke_fn...");
|
||||||
|
|
||||||
let birth_result = unsafe {
|
let birth_result = unsafe {
|
||||||
|
|||||||
@ -103,12 +103,37 @@ impl Transport for InProcessTransport {
|
|||||||
fn transport_type(&self) -> &'static str {
|
fn transport_type(&self) -> &'static str {
|
||||||
"inprocess"
|
"inprocess"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register_intent_handler(&mut self, intent: &str, cb: Box<dyn Fn(IntentEnvelope) + Send + Sync>) {
|
||||||
|
let intent_name = intent.to_string();
|
||||||
|
let cb = std::sync::Arc::new(cb);
|
||||||
|
let cb_clone = cb.clone();
|
||||||
|
// Adapt to MessageBus handler signature
|
||||||
|
self.add_handler(&intent_name, Box::new(move |intent_box: IntentBox, from: &str| {
|
||||||
|
let env = IntentEnvelope {
|
||||||
|
from: from.to_string(),
|
||||||
|
to: String::new(), // not tracked at this layer for handler
|
||||||
|
intent: intent_box,
|
||||||
|
timestamp: std::time::Instant::now(),
|
||||||
|
};
|
||||||
|
(cb_clone)(env);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug_list_nodes(&self) -> Option<Vec<String>> {
|
||||||
|
let bus = self.bus.lock().ok()?;
|
||||||
|
Some(bus.get_nodes())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug_bus_id(&self) -> Option<String> {
|
||||||
|
let ptr = std::sync::Arc::as_ptr(&self.bus);
|
||||||
|
Some(format!("{:p}", ptr))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for InProcessTransport {
|
impl Drop for InProcessTransport {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// ノードをバスから解除
|
// NOTE: Temporarily disabled unregister to avoid interfering with shared-node lifetimes.
|
||||||
let mut bus = self.bus.lock().unwrap();
|
// Proper refcounted unregister will be implemented later.
|
||||||
bus.unregister_node(&self.node_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,6 +49,17 @@ pub trait Transport: Send + Sync + std::fmt::Debug {
|
|||||||
|
|
||||||
/// Get transport type identifier
|
/// Get transport type identifier
|
||||||
fn transport_type(&self) -> &'static str;
|
fn transport_type(&self) -> &'static str;
|
||||||
|
|
||||||
|
/// Downcast support for dynamic transports
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any where Self: 'static + Sized { self }
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any where Self: 'static + Sized { self }
|
||||||
|
|
||||||
|
/// Register an intent handler (default: no-op)
|
||||||
|
fn register_intent_handler(&mut self, _intent: &str, _cb: Box<dyn Fn(IntentEnvelope) + Send + Sync>) { }
|
||||||
|
|
||||||
|
/// Debug helper: enumerate known nodes (if supported)
|
||||||
|
fn debug_list_nodes(&self) -> Option<Vec<String>> { None }
|
||||||
|
fn debug_bus_id(&self) -> Option<String> { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use inprocess::InProcessTransport;
|
pub use inprocess::InProcessTransport;
|
||||||
Reference in New Issue
Block a user