🎉 FileBox v2 plugin system fully working with TLV encoding fix
Major achievements: - Fixed TLV encoding format to match plugin expectations - Header: version(2 bytes) + argc(2 bytes) - Entry: tag(1) + reserved(1) + size(2) + data - Removed duplicate implementation in method_dispatch.rs - All FileBox methods working: open/read/write/close - Successfully tested file I/O operations This completes the v2 plugin system integration for FileBox. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -1,28 +1,80 @@
|
|||||||
# 🎯 現在のタスク (2025-08-19 更新)
|
# 🎯 現在のタスク (2025-08-19 更新)
|
||||||
|
|
||||||
## 🔥 最優先タスク:nyash.toml v2対応
|
## 🎉 FileBox v2プラグインシステム完全動作達成!
|
||||||
|
|
||||||
### 📍 問題の本質
|
### 📍 本日の成果
|
||||||
**nyash.toml v2(マルチBox型)に誰も対応していない!**
|
**FileBoxプラグインの全機能が正常動作!**
|
||||||
|
|
||||||
1. **プラグインテスター** - 古い単一Box型前提
|
1. **✅ 重複実装の解消**
|
||||||
2. **Nyash本体のレジストリ** - 古い単一Box型前提
|
- `method_dispatch.rs` を削除(使われていないコード)
|
||||||
3. **結果** - プラグインが正しく読み込まれない
|
- `calls.rs` が実際の実装であることを確認
|
||||||
|
|
||||||
### 🎯 正しい実装順序
|
2. **✅ TLVエンコーディング修正**
|
||||||
1. **プラグインテスターをnyash.toml v2対応にする**
|
```rust
|
||||||
- マルチBox型プラグイン対応
|
// Header: version(2 bytes) + argc(2 bytes)
|
||||||
- nyash.tomlから型情報読み取り
|
tlv_data.extend_from_slice(&1u16.to_le_bytes());
|
||||||
|
tlv_data.extend_from_slice(&(arg_values.len() as u16).to_le_bytes());
|
||||||
|
|
||||||
2. **プラグインテスターで動作確認**
|
// TLV entry: tag(1) + reserved(1) + size(2) + data
|
||||||
- FileBoxプラグインが正しく認識されるか
|
tlv_data.push(6); // tag = 6 (String)
|
||||||
- メソッド情報が正しく取得できるか
|
tlv_data.push(0); // reserved
|
||||||
|
tlv_data.extend_from_slice(&(arg_bytes.len() as u16).to_le_bytes());
|
||||||
|
```
|
||||||
|
|
||||||
3. **Nyash本体のレジストリに移植**
|
3. **✅ FileBox全機能テスト成功**
|
||||||
- プラグインテスターの実装をコピー
|
- open("file.txt", "w") - 書き込みモードで開く
|
||||||
- 汎用プラグインBox生成が動作
|
- write("data") - データ書き込み(バイト数返却)
|
||||||
|
- read() - ファイル内容読み込み
|
||||||
|
- close() - ファイルクローズ
|
||||||
|
- 実際のファイル作成・読み書き確認済み
|
||||||
|
|
||||||
### 📝 nyash.toml v2形式(確認)
|
### 🔥 今日の重要な発見
|
||||||
|
**コードフローの正確な追跡の重要性**
|
||||||
|
|
||||||
|
「深く考える」ことで、以下を発見:
|
||||||
|
- execute_method_callの実行パスを追跡
|
||||||
|
- interpreter/expressions/mod.rs → calls.rs が実際の実行パス
|
||||||
|
- method_dispatch.rsは未使用のレガシーコード
|
||||||
|
|
||||||
|
**教訓**: 推測せず、実際のコードフローを追跡することが重要!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 次のステップ
|
||||||
|
|
||||||
|
### Phase 9.8 - プラグイン設定のnyash.toml拡張
|
||||||
|
- ✅ v2形式のnyash.toml対応完了
|
||||||
|
- ✅ FileBoxプラグイン完全動作
|
||||||
|
- 次: 他のプラグイン(MathBox、StringManipulatorBox等)の移行
|
||||||
|
|
||||||
|
### Phase 8.4 - AST→MIR Lowering
|
||||||
|
- copilot_issues.txtに従って実装継続
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 完了したタスク(要約)
|
||||||
|
|
||||||
|
### FileBox v2プラグインシステム ✅
|
||||||
|
- プラグインローダーv2実装
|
||||||
|
- TLVエンコーディング修正
|
||||||
|
- 全機能動作確認
|
||||||
|
|
||||||
|
### 汎用プラグインBox生成システム ✅
|
||||||
|
- `src/bid/generic_plugin_box.rs` 実装完了
|
||||||
|
- FileBox決め打ちコードを削除
|
||||||
|
|
||||||
|
### Phase 9.75g-0 BID-FFI Plugin System ✅
|
||||||
|
- プラグインシステム基盤完成
|
||||||
|
- plugin-tester診断ツール実装
|
||||||
|
|
||||||
|
### Phase 8.6 VM性能改善 ✅
|
||||||
|
- VM 50.94倍高速化達成!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 技術詳細・参考資料
|
||||||
|
|
||||||
|
### nyash.toml v2仕様
|
||||||
```toml
|
```toml
|
||||||
[libraries]
|
[libraries]
|
||||||
"libnyash_filebox_plugin.so" = {
|
"libnyash_filebox_plugin.so" = {
|
||||||
@ -42,54 +94,10 @@ close = { method_id = 4 }
|
|||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
```
|
```
|
||||||
|
|
||||||
### 🚨 現在の間違った形式
|
|
||||||
```toml
|
|
||||||
[plugins]
|
|
||||||
FileBox = "./target/release/libnyash_filebox_plugin.so" # ← 古い形式!
|
|
||||||
|
|
||||||
[plugins.FileBox] # ← パーサーエラーの原因
|
|
||||||
type_id = 6
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 Phase 9.75h-0: プラグインシステム完全統一(進行中)
|
|
||||||
|
|
||||||
### 進捗状況
|
|
||||||
- ✅ 設計方針決定(nyash.toml中心設計)
|
|
||||||
- ✅ FileBox決め打ちコード削除完了
|
|
||||||
- ✅ 汎用プラグインBox(GenericPluginBox)実装完了
|
|
||||||
- 🔄 **nyash.toml v2対応が必要!**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 完了したタスク(要約)
|
|
||||||
|
|
||||||
### 汎用プラグインBox生成システム ✅
|
|
||||||
- `src/bid/generic_plugin_box.rs` 実装完了
|
|
||||||
- FileBox決め打ちコードを削除
|
|
||||||
- `new FileBox()`が汎用システムで動作する仕組み完成
|
|
||||||
|
|
||||||
### Phase 9.75g-0 BID-FFI Plugin System ✅
|
|
||||||
- プラグインシステム基盤完成
|
|
||||||
- plugin-tester診断ツール実装
|
|
||||||
|
|
||||||
### Phase 8.6 VM性能改善 ✅
|
|
||||||
- VM 50.94倍高速化達成!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 技術詳細・参考資料
|
|
||||||
|
|
||||||
### nyash.toml v2仕様
|
|
||||||
- [config/nyash_toml_v2.rs](../src/config/nyash_toml_v2.rs)
|
|
||||||
- マルチBox型プラグイン対応
|
|
||||||
- ライブラリベースの設定形式
|
|
||||||
|
|
||||||
### 開発計画
|
### 開発計画
|
||||||
- [copilot_issues.txt](../予定/native-plan/copilot_issues.txt)
|
- [copilot_issues.txt](../docs/予定/native-plan/copilot_issues.txt)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**最終更新**: 2025年8月19日
|
**最終更新**: 2025年8月19日
|
||||||
**次回マイルストーン**: プラグインテスターのnyash.toml v2対応
|
**次回マイルストーン**: 他のプラグインのv2移行
|
||||||
9
local_tests/test_filebox_debug.nyash
Normal file
9
local_tests/test_filebox_debug.nyash
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Debug test for FileBox type checking
|
||||||
|
local file
|
||||||
|
file = new FileBox()
|
||||||
|
print("Created FileBox")
|
||||||
|
|
||||||
|
// Try method call
|
||||||
|
local result
|
||||||
|
result = file.open("test.txt", "w")
|
||||||
|
print("Open result: " + result)
|
||||||
36
local_tests/test_filebox_full.nyash
Normal file
36
local_tests/test_filebox_full.nyash
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Complete FileBox test
|
||||||
|
local file
|
||||||
|
file = new FileBox()
|
||||||
|
print("Created FileBox")
|
||||||
|
|
||||||
|
// Open file for writing
|
||||||
|
local openResult
|
||||||
|
openResult = file.open("test_output.txt", "w")
|
||||||
|
print("Open result: " + openResult)
|
||||||
|
|
||||||
|
// Write some data
|
||||||
|
local writeResult
|
||||||
|
writeResult = file.write("Hello from Nyash!\n")
|
||||||
|
print("Write result: " + writeResult)
|
||||||
|
|
||||||
|
// Write more data
|
||||||
|
writeResult = file.write("FileBox is working! 🎉\n")
|
||||||
|
print("Write result 2: " + writeResult)
|
||||||
|
|
||||||
|
// Close the file
|
||||||
|
local closeResult
|
||||||
|
closeResult = file.close()
|
||||||
|
print("Close result: " + closeResult)
|
||||||
|
|
||||||
|
// Open for reading
|
||||||
|
openResult = file.open("test_output.txt", "r")
|
||||||
|
print("Open for read result: " + openResult)
|
||||||
|
|
||||||
|
// Read the content
|
||||||
|
local content
|
||||||
|
content = file.read()
|
||||||
|
print("Read content: " + content)
|
||||||
|
|
||||||
|
// Close again
|
||||||
|
closeResult = file.close()
|
||||||
|
print("Final close result: " + closeResult)
|
||||||
@ -13,6 +13,7 @@ use crate::instance::InstanceBox;
|
|||||||
use crate::channel_box::ChannelBox;
|
use crate::channel_box::ChannelBox;
|
||||||
use crate::interpreter::core::{NyashInterpreter, RuntimeError};
|
use crate::interpreter::core::{NyashInterpreter, RuntimeError};
|
||||||
use crate::interpreter::finalization;
|
use crate::interpreter::finalization;
|
||||||
|
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
impl NyashInterpreter {
|
impl NyashInterpreter {
|
||||||
@ -487,6 +488,11 @@ impl NyashInterpreter {
|
|||||||
|
|
||||||
// RangeBox method calls (将来的に追加予定)
|
// RangeBox method calls (将来的に追加予定)
|
||||||
|
|
||||||
|
// PluginBoxV2 method calls
|
||||||
|
if let Some(plugin_box) = obj_value.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||||
|
return self.execute_plugin_box_v2_method(plugin_box, method, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
// InstanceBox method calls
|
// InstanceBox method calls
|
||||||
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
|
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
|
||||||
// 🔥 Usage prohibition guard - check if instance is finalized
|
// 🔥 Usage prohibition guard - check if instance is finalized
|
||||||
@ -836,4 +842,171 @@ impl NyashInterpreter {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute method call on PluginBoxV2
|
||||||
|
fn execute_plugin_box_v2_method(
|
||||||
|
&mut self,
|
||||||
|
plugin_box: &PluginBoxV2,
|
||||||
|
method: &str,
|
||||||
|
arguments: &[ASTNode],
|
||||||
|
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||||
|
eprintln!("🔍 execute_plugin_box_v2_method called: {}.{}", plugin_box.box_type, method);
|
||||||
|
|
||||||
|
// Get global loader to access configuration
|
||||||
|
let loader = crate::runtime::plugin_loader_v2::get_global_loader_v2();
|
||||||
|
let loader = loader.read().unwrap();
|
||||||
|
|
||||||
|
// Get method_id from configuration
|
||||||
|
let method_id = if let Some(config) = &loader.config {
|
||||||
|
// Find library that provides this box type
|
||||||
|
let (lib_name, _) = config.find_library_for_box(&plugin_box.box_type)
|
||||||
|
.ok_or_else(|| RuntimeError::InvalidOperation {
|
||||||
|
message: format!("No plugin provides box type: {}", plugin_box.box_type)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Get method_id from toml
|
||||||
|
if let Ok(toml_content) = std::fs::read_to_string("nyash.toml") {
|
||||||
|
if let Ok(toml_value) = toml::from_str::<toml::Value>(&toml_content) {
|
||||||
|
if let Some(box_config) = config.get_box_config(lib_name, &plugin_box.box_type, &toml_value) {
|
||||||
|
if let Some(method_config) = box_config.methods.get(method) {
|
||||||
|
eprintln!("🔍 Found method {} with id: {}", method, method_config.method_id);
|
||||||
|
method_config.method_id
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError::InvalidOperation {
|
||||||
|
message: format!("Unknown method '{}' for {}", method, plugin_box.box_type)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError::InvalidOperation {
|
||||||
|
message: format!("No configuration for box type: {}", plugin_box.box_type)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError::InvalidOperation {
|
||||||
|
message: "Failed to parse nyash.toml".into()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError::InvalidOperation {
|
||||||
|
message: "Failed to read nyash.toml".into()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError::InvalidOperation {
|
||||||
|
message: "No configuration loaded".into()
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Evaluate arguments
|
||||||
|
let mut arg_values = Vec::new();
|
||||||
|
for arg in arguments {
|
||||||
|
arg_values.push(self.execute_expression(arg)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode arguments using TLV (plugin's expected format)
|
||||||
|
let mut tlv_data = Vec::new();
|
||||||
|
|
||||||
|
// Header: version(2 bytes) + argc(2 bytes)
|
||||||
|
tlv_data.extend_from_slice(&1u16.to_le_bytes()); // version = 1
|
||||||
|
tlv_data.extend_from_slice(&(arg_values.len() as u16).to_le_bytes()); // argc
|
||||||
|
|
||||||
|
// Encode each argument
|
||||||
|
for arg in arg_values.iter() {
|
||||||
|
// For now, convert all arguments to strings
|
||||||
|
let arg_str = arg.to_string_box().value;
|
||||||
|
let arg_bytes = arg_str.as_bytes();
|
||||||
|
|
||||||
|
// TLV entry: tag(1) + reserved(1) + size(2) + data
|
||||||
|
tlv_data.push(6); // tag = 6 (String)
|
||||||
|
tlv_data.push(0); // reserved
|
||||||
|
tlv_data.extend_from_slice(&(arg_bytes.len() as u16).to_le_bytes()); // size
|
||||||
|
tlv_data.extend_from_slice(arg_bytes); // data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare output buffer
|
||||||
|
let mut output_buffer = vec![0u8; 4096]; // 4KB buffer
|
||||||
|
let mut output_len = output_buffer.len();
|
||||||
|
|
||||||
|
eprintln!("🔍 Calling plugin invoke_fn: type_id={}, method_id={}, instance_id={}",
|
||||||
|
plugin_box.type_id, method_id, plugin_box.instance_id);
|
||||||
|
|
||||||
|
// Call plugin method
|
||||||
|
let result = unsafe {
|
||||||
|
(plugin_box.invoke_fn)(
|
||||||
|
plugin_box.type_id, // type_id from PluginBoxV2
|
||||||
|
method_id, // method_id
|
||||||
|
plugin_box.instance_id, // instance_id
|
||||||
|
tlv_data.as_ptr(), // arguments
|
||||||
|
tlv_data.len(), // arguments length
|
||||||
|
output_buffer.as_mut_ptr(), // output buffer
|
||||||
|
&mut output_len, // output length
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
eprintln!("🔍 Plugin method returned: {}", result);
|
||||||
|
|
||||||
|
if result != 0 {
|
||||||
|
return Err(RuntimeError::RuntimeFailure {
|
||||||
|
message: format!("Plugin method {} failed with code: {}", method, result)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse TLV output dynamically
|
||||||
|
if output_len >= 4 {
|
||||||
|
// Parse TLV header
|
||||||
|
let version = u16::from_le_bytes([output_buffer[0], output_buffer[1]]);
|
||||||
|
let argc = u16::from_le_bytes([output_buffer[2], output_buffer[3]]);
|
||||||
|
|
||||||
|
eprintln!("🔍 TLV response: version={}, argc={}", version, argc);
|
||||||
|
|
||||||
|
if version == 1 && argc > 0 && output_len >= 8 {
|
||||||
|
// Parse first TLV entry
|
||||||
|
let tag = output_buffer[4];
|
||||||
|
let _reserved = output_buffer[5];
|
||||||
|
let size = u16::from_le_bytes([output_buffer[6], output_buffer[7]]) as usize;
|
||||||
|
|
||||||
|
eprintln!("🔍 TLV entry: tag={}, size={}", tag, size);
|
||||||
|
|
||||||
|
if output_len >= 8 + size {
|
||||||
|
match tag {
|
||||||
|
2 => {
|
||||||
|
// I32 type
|
||||||
|
if size == 4 {
|
||||||
|
let value = i32::from_le_bytes([
|
||||||
|
output_buffer[8], output_buffer[9],
|
||||||
|
output_buffer[10], output_buffer[11]
|
||||||
|
]);
|
||||||
|
Ok(Box::new(IntegerBox::new(value as i64)))
|
||||||
|
} else {
|
||||||
|
Ok(Box::new(StringBox::new("ok")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
6 | 7 => {
|
||||||
|
// String or Bytes type
|
||||||
|
let data = &output_buffer[8..8+size];
|
||||||
|
let string = String::from_utf8_lossy(data).to_string();
|
||||||
|
Ok(Box::new(StringBox::new(string)))
|
||||||
|
}
|
||||||
|
9 => {
|
||||||
|
// Void type
|
||||||
|
Ok(Box::new(StringBox::new("ok")))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Unknown type, treat as string
|
||||||
|
eprintln!("🔍 Unknown TLV tag: {}", tag);
|
||||||
|
Ok(Box::new(StringBox::new("ok")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Box::new(StringBox::new("ok")))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No output, return void
|
||||||
|
Ok(Box::new(VoidBox::new()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No output, return void
|
||||||
|
Ok(Box::new(VoidBox::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,494 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Method Dispatch Module
|
|
||||||
*
|
|
||||||
* Extracted from expressions.rs lines 383-900 (~517 lines)
|
|
||||||
* Handles method call dispatch for all Box types and static function calls
|
|
||||||
* Core philosophy: "Everything is Box" with unified method dispatch
|
|
||||||
*/
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::boxes::{buffer::BufferBox, JSONBox, HttpClientBox, StreamBox, RegexBox, IntentBox, SocketBox, HTTPServerBox, HTTPRequestBox, HTTPResponseBox};
|
|
||||||
use crate::boxes::{FloatBox, MathBox, ConsoleBox, TimeBox, DateTimeBox, RandomBox, SoundBox, DebugBox, file::FileBox, MapBox};
|
|
||||||
use crate::bid::plugin_box::PluginFileBox;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
impl NyashInterpreter {
|
|
||||||
/// メソッド呼び出しを実行 - 全Box型の統一ディスパッチ
|
|
||||||
pub(super) fn execute_method_call(&mut self, object: &ASTNode, method: &str, arguments: &[ASTNode])
|
|
||||||
-> Result<Box<dyn NyashBox>, RuntimeError> {
|
|
||||||
|
|
||||||
// 🔥 static関数のチェック
|
|
||||||
if let ASTNode::Variable { name, .. } = object {
|
|
||||||
// static関数が存在するかチェック
|
|
||||||
let static_func = {
|
|
||||||
let static_funcs = self.shared.static_functions.read().unwrap();
|
|
||||||
if let Some(box_statics) = static_funcs.get(name) {
|
|
||||||
if let Some(func) = box_statics.get(method) {
|
|
||||||
Some(func.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(static_func) = static_func {
|
|
||||||
return self.execute_static_function(static_func, name, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 📚 nyashstd標準ライブラリのメソッドチェック
|
|
||||||
if let Some(stdlib_result) = self.try_execute_stdlib_method(name, method, arguments)? {
|
|
||||||
return Ok(stdlib_result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// オブジェクトを評価(通常のメソッド呼び出し)
|
|
||||||
let obj_value = self.execute_expression(object)?;
|
|
||||||
|
|
||||||
// 各Box型に対するメソッドディスパッチ
|
|
||||||
self.dispatch_builtin_method(&obj_value, method, arguments, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// static関数を実行
|
|
||||||
fn execute_static_function(
|
|
||||||
&mut self,
|
|
||||||
static_func: ASTNode,
|
|
||||||
box_name: &str,
|
|
||||||
method: &str,
|
|
||||||
arguments: &[ASTNode]
|
|
||||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
|
||||||
if let ASTNode::FunctionDeclaration { params, body, .. } = static_func {
|
|
||||||
// 引数を評価
|
|
||||||
let mut arg_values = Vec::new();
|
|
||||||
for arg in arguments {
|
|
||||||
arg_values.push(self.execute_expression(arg)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
// パラメータ数チェック
|
|
||||||
if arg_values.len() != params.len() {
|
|
||||||
return Err(RuntimeError::InvalidOperation {
|
|
||||||
message: format!("Static method {}.{} expects {} arguments, got {}",
|
|
||||||
box_name, method, params.len(), arg_values.len()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🌍 local変数スタックを保存・クリア(static関数呼び出し開始)
|
|
||||||
let saved_locals = self.save_local_vars();
|
|
||||||
self.local_vars.clear();
|
|
||||||
|
|
||||||
// 📤 outbox変数スタックも保存・クリア(static関数専用)
|
|
||||||
let saved_outbox = self.save_outbox_vars();
|
|
||||||
self.outbox_vars.clear();
|
|
||||||
|
|
||||||
// 引数をlocal変数として設定
|
|
||||||
for (param, value) in params.iter().zip(arg_values.iter()) {
|
|
||||||
self.declare_local_variable(param, value.clone_box());
|
|
||||||
}
|
|
||||||
|
|
||||||
// static関数の本体を実行
|
|
||||||
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(return_val) = &self.control_flow {
|
|
||||||
result = return_val.clone_box();
|
|
||||||
self.control_flow = super::ControlFlow::None;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// local変数スタックを復元
|
|
||||||
self.restore_local_vars(saved_locals);
|
|
||||||
|
|
||||||
// outbox変数スタックを復元
|
|
||||||
self.restore_outbox_vars(saved_outbox);
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::InvalidOperation {
|
|
||||||
message: format!("Invalid static function: {}.{}", box_name, method),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// nyashstd標準ライブラリメソッド実行を試行
|
|
||||||
fn try_execute_stdlib_method(
|
|
||||||
&mut self,
|
|
||||||
box_name: &str,
|
|
||||||
method: &str,
|
|
||||||
arguments: &[ASTNode]
|
|
||||||
) -> Result<Option<Box<dyn NyashBox>>, RuntimeError> {
|
|
||||||
let stdlib_method = if let Some(ref stdlib) = self.stdlib {
|
|
||||||
if let Some(nyashstd_namespace) = stdlib.namespaces.get("nyashstd") {
|
|
||||||
if let Some(static_box) = nyashstd_namespace.static_boxes.get(box_name) {
|
|
||||||
if let Some(builtin_method) = static_box.methods.get(method) {
|
|
||||||
Some(*builtin_method) // Copyトレイトで関数ポインターをコピー
|
|
||||||
} else {
|
|
||||||
eprintln!("🔍 Method '{}' not found in nyashstd.{}", method, box_name);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("🔍 Static box '{}' not found in nyashstd", box_name);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("🔍 nyashstd namespace not found in stdlib");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("🔍 stdlib not initialized for method call");
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(builtin_method) = stdlib_method {
|
|
||||||
eprintln!("🌟 Calling nyashstd method: {}.{}", box_name, method);
|
|
||||||
|
|
||||||
// 引数を評価
|
|
||||||
let mut arg_values = Vec::new();
|
|
||||||
for arg in arguments {
|
|
||||||
arg_values.push(self.execute_expression(arg)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 標準ライブラリのメソッドを実行
|
|
||||||
let result = builtin_method(&arg_values)?;
|
|
||||||
eprintln!("✅ nyashstd method completed: {}.{}", box_name, method);
|
|
||||||
return Ok(Some(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ビルトインBox型メソッドディスパッチ
|
|
||||||
fn dispatch_builtin_method(
|
|
||||||
&mut self,
|
|
||||||
obj_value: &Box<dyn NyashBox>,
|
|
||||||
method: &str,
|
|
||||||
arguments: &[ASTNode],
|
|
||||||
object: &ASTNode
|
|
||||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
|
||||||
// StringBox method calls
|
|
||||||
if let Some(string_box) = obj_value.as_any().downcast_ref::<StringBox>() {
|
|
||||||
return self.execute_string_method(string_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
// Plugin-backed FileBox method calls
|
|
||||||
if let Some(pfile) = obj_value.as_any().downcast_ref::<PluginFileBox>() {
|
|
||||||
return self.execute_plugin_file_method(pfile, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResultBox method calls
|
|
||||||
if let Some(result_box) = obj_value.as_any().downcast_ref::<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
|
|
||||||
if let Some(datetime_box) = obj_value.as_any().downcast_ref::<DateTimeBox>() {
|
|
||||||
return self.execute_datetime_method(datetime_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TimerBox method calls
|
|
||||||
if let Some(timer_box) = obj_value.as_any().downcast_ref::<TimerBox>() {
|
|
||||||
return self.execute_timer_method(timer_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// MapBox method calls
|
|
||||||
if let Some(map_box) = obj_value.as_any().downcast_ref::<MapBox>() {
|
|
||||||
return self.execute_map_method(map_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomBox method calls
|
|
||||||
if let Some(random_box) = obj_value.as_any().downcast_ref::<RandomBox>() {
|
|
||||||
return self.execute_random_method(random_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SoundBox method calls
|
|
||||||
if let Some(sound_box) = obj_value.as_any().downcast_ref::<SoundBox>() {
|
|
||||||
return self.execute_sound_method(sound_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// DebugBox method calls
|
|
||||||
if let Some(debug_box) = obj_value.as_any().downcast_ref::<DebugBox>() {
|
|
||||||
return self.execute_debug_method(debug_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConsoleBox method calls
|
|
||||||
if let Some(console_box) = obj_value.as_any().downcast_ref::<crate::boxes::console_box::ConsoleBox>() {
|
|
||||||
return self.execute_console_method(console_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntentBox method calls
|
|
||||||
if let Some(intent_box) = obj_value.as_any().downcast_ref::<IntentBox>() {
|
|
||||||
return self.execute_intent_box_method(intent_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SocketBox method calls
|
|
||||||
if let Some(socket_box) = obj_value.as_any().downcast_ref::<SocketBox>() {
|
|
||||||
let result = self.execute_socket_method(socket_box, method, arguments)?;
|
|
||||||
|
|
||||||
// 🔧 FIX: Update stored variable for stateful SocketBox methods
|
|
||||||
if matches!(method, "bind" | "connect" | "close") {
|
|
||||||
self.update_stateful_socket_box(object, socket_box)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPServerBox method calls
|
|
||||||
if let Some(http_server_box) = obj_value.as_any().downcast_ref::<HTTPServerBox>() {
|
|
||||||
return self.execute_http_server_method(http_server_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPRequestBox method calls
|
|
||||||
if let Some(http_request_box) = obj_value.as_any().downcast_ref::<HTTPRequestBox>() {
|
|
||||||
return self.execute_http_request_method(http_request_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPResponseBox method calls
|
|
||||||
if let Some(http_response_box) = obj_value.as_any().downcast_ref::<HTTPResponseBox>() {
|
|
||||||
return self.execute_http_response_method(http_response_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// P2PBox method calls - Temporarily disabled
|
|
||||||
// if let Some(p2p_box) = obj_value.as_any().downcast_ref::<P2PBox>() {
|
|
||||||
// return self.execute_p2p_box_method(p2p_box, method, arguments);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// EguiBox method calls (非WASM環境のみ)
|
|
||||||
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
|
|
||||||
if let Some(egui_box) = obj_value.as_any().downcast_ref::<crate::boxes::EguiBox>() {
|
|
||||||
return self.execute_egui_method(egui_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebDisplayBox method calls (WASM環境のみ)
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
if let Some(web_display_box) = obj_value.as_any().downcast_ref::<crate::boxes::WebDisplayBox>() {
|
|
||||||
return self.execute_web_display_method(web_display_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebConsoleBox method calls (WASM環境のみ)
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
if let Some(web_console_box) = obj_value.as_any().downcast_ref::<crate::boxes::WebConsoleBox>() {
|
|
||||||
return self.execute_web_console_method(web_console_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebCanvasBox method calls (WASM環境のみ)
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
if let Some(web_canvas_box) = obj_value.as_any().downcast_ref::<crate::boxes::WebCanvasBox>() {
|
|
||||||
return self.execute_web_canvas_method(web_canvas_box, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ユーザー定義Boxのメソッド呼び出し
|
|
||||||
self.execute_user_defined_method(obj_value, method, arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute_plugin_file_method(
|
|
||||||
&mut self,
|
|
||||||
pfile: &PluginFileBox,
|
|
||||||
method: &str,
|
|
||||||
arguments: &[ASTNode],
|
|
||||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
|
||||||
match method {
|
|
||||||
"write" => {
|
|
||||||
if arguments.len() != 1 {
|
|
||||||
return Err(RuntimeError::InvalidOperation { message: "FileBox.write expects 1 argument".into() });
|
|
||||||
}
|
|
||||||
let arg0 = self.execute_expression(&arguments[0])?;
|
|
||||||
let data = arg0.to_string_box().value;
|
|
||||||
pfile.write_bytes(data.as_bytes()).map_err(|e| RuntimeError::RuntimeFailure { message: format!("plugin write error: {:?}", e) })?;
|
|
||||||
Ok(Box::new(StringBox::new("ok")))
|
|
||||||
}
|
|
||||||
"read" => {
|
|
||||||
// Default read size
|
|
||||||
let size = 1_048_576usize; // 1MB max
|
|
||||||
let bytes = pfile.read_bytes(size).map_err(|e| RuntimeError::RuntimeFailure { message: format!("plugin read error: {:?}", e) })?;
|
|
||||||
let s = String::from_utf8_lossy(&bytes).to_string();
|
|
||||||
Ok(Box::new(StringBox::new(s)))
|
|
||||||
}
|
|
||||||
"close" => {
|
|
||||||
pfile.close().map_err(|e| RuntimeError::RuntimeFailure { message: format!("plugin close error: {:?}", e) })?;
|
|
||||||
Ok(Box::new(StringBox::new("ok")))
|
|
||||||
}
|
|
||||||
_ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method FileBox.{} (plugin)", method) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SocketBoxの状態変更を反映
|
|
||||||
fn update_stateful_socket_box(
|
|
||||||
&mut self,
|
|
||||||
object: &ASTNode,
|
|
||||||
socket_box: &SocketBox
|
|
||||||
) -> Result<(), RuntimeError> {
|
|
||||||
eprintln!("🔧 DEBUG: Stateful method called, updating stored instance");
|
|
||||||
let updated_instance = socket_box.clone();
|
|
||||||
eprintln!("🔧 DEBUG: Updated instance created with ID={}", updated_instance.box_id());
|
|
||||||
|
|
||||||
match object {
|
|
||||||
ASTNode::Variable { name, .. } => {
|
|
||||||
eprintln!("🔧 DEBUG: Updating local variable '{}'", name);
|
|
||||||
if let Some(stored_var) = self.local_vars.get_mut(name) {
|
|
||||||
eprintln!("🔧 DEBUG: Found local variable '{}', updating from id={} to id={}",
|
|
||||||
name, stored_var.box_id(), updated_instance.box_id());
|
|
||||||
*stored_var = Arc::new(updated_instance);
|
|
||||||
} else {
|
|
||||||
eprintln!("🔧 DEBUG: Local variable '{}' not found", name);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ASTNode::FieldAccess { object: field_obj, field, .. } => {
|
|
||||||
eprintln!("🔧 DEBUG: Updating field access '{}'", field);
|
|
||||||
self.update_field_with_socket_box(field_obj, field, updated_instance)?;
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
eprintln!("🔧 DEBUG: Object type not handled: {:?}", object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// フィールドアクセスでのSocketBox更新
|
|
||||||
fn update_field_with_socket_box(
|
|
||||||
&mut self,
|
|
||||||
field_obj: &ASTNode,
|
|
||||||
field: &str,
|
|
||||||
updated_instance: SocketBox
|
|
||||||
) -> Result<(), RuntimeError> {
|
|
||||||
match field_obj {
|
|
||||||
ASTNode::Variable { name, .. } => {
|
|
||||||
eprintln!("🔧 DEBUG: Field object is variable '{}'", name);
|
|
||||||
if name == "me" {
|
|
||||||
eprintln!("🔧 DEBUG: Updating me.{} (via variable)", field);
|
|
||||||
if let Ok(me_instance) = self.resolve_variable("me") {
|
|
||||||
eprintln!("🔧 DEBUG: Resolved 'me' instance id={}", me_instance.box_id());
|
|
||||||
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());
|
|
||||||
let result = instance.set_field(field, Arc::new(updated_instance));
|
|
||||||
eprintln!("🔧 DEBUG: set_field result: {:?}", result);
|
|
||||||
} else {
|
|
||||||
eprintln!("🔧 DEBUG: me is not an InstanceBox, type: {}", me_instance.type_name());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("🔧 DEBUG: Failed to resolve 'me'");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("🔧 DEBUG: Field object is not 'me', it's '{}'", name);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ASTNode::Me { .. } => {
|
|
||||||
eprintln!("🔧 DEBUG: Field object is Me node, updating me.{}", field);
|
|
||||||
if let Ok(me_instance) = self.resolve_variable("me") {
|
|
||||||
eprintln!("🔧 DEBUG: Resolved 'me' instance id={}", me_instance.box_id());
|
|
||||||
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());
|
|
||||||
let result = instance.set_field(field, Arc::new(updated_instance));
|
|
||||||
eprintln!("🔧 DEBUG: set_field result: {:?}", result);
|
|
||||||
} else {
|
|
||||||
eprintln!("🔧 DEBUG: me is not an InstanceBox, type: {}", me_instance.type_name());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("🔧 DEBUG: Failed to resolve 'me'");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
eprintln!("🔧 DEBUG: Field object is not a variable or me, type: {:?}", field_obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ユーザー定義Boxメソッド実行
|
|
||||||
fn execute_user_defined_method(
|
|
||||||
&mut self,
|
|
||||||
obj_value: &Box<dyn NyashBox>,
|
|
||||||
method: &str,
|
|
||||||
arguments: &[ASTNode]
|
|
||||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
|
||||||
// InstanceBox method calls (user-defined methods)
|
|
||||||
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
|
|
||||||
return self.execute_instance_method(instance, method, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Static box method calls would be handled here if implemented
|
|
||||||
// (Currently handled via different mechanism in static function dispatch)
|
|
||||||
|
|
||||||
Err(RuntimeError::InvalidOperation {
|
|
||||||
message: format!("Method '{}' not found on type '{}'", method, obj_value.type_name()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -29,9 +29,10 @@ pub struct LoadedPluginV2 {
|
|||||||
/// v2 Plugin Box wrapper - temporary implementation
|
/// v2 Plugin Box wrapper - temporary implementation
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PluginBoxV2 {
|
pub struct PluginBoxV2 {
|
||||||
box_type: String,
|
pub box_type: String,
|
||||||
invoke_fn: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
pub type_id: u32,
|
||||||
instance_id: u32,
|
pub invoke_fn: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||||
|
pub instance_id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BoxCore for PluginBoxV2 {
|
impl BoxCore for PluginBoxV2 {
|
||||||
@ -58,11 +59,54 @@ impl BoxCore for PluginBoxV2 {
|
|||||||
|
|
||||||
impl NyashBox for PluginBoxV2 {
|
impl NyashBox for PluginBoxV2 {
|
||||||
fn type_name(&self) -> &'static str {
|
fn type_name(&self) -> &'static str {
|
||||||
"PluginBoxV2"
|
// Return the actual box type name for proper method dispatch
|
||||||
|
match self.box_type.as_str() {
|
||||||
|
"FileBox" => "FileBox",
|
||||||
|
_ => "PluginBoxV2",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||||
Box::new(StringBox::new(format!("Cannot clone plugin box {}", self.box_type)))
|
eprintln!("🔍 DEBUG: PluginBoxV2::clone_box called for {} (id={})", self.box_type, self.instance_id);
|
||||||
|
|
||||||
|
// Clone means creating a new instance by calling birth()
|
||||||
|
let mut output_buffer = vec![0u8; 1024];
|
||||||
|
let mut output_len = output_buffer.len();
|
||||||
|
let tlv_args = vec![1u8, 0, 0, 0]; // version=1, argc=0
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
(self.invoke_fn)(
|
||||||
|
self.type_id,
|
||||||
|
0, // method_id=0 (birth)
|
||||||
|
0, // instance_id=0 (static call)
|
||||||
|
tlv_args.as_ptr(),
|
||||||
|
tlv_args.len(),
|
||||||
|
output_buffer.as_mut_ptr(),
|
||||||
|
&mut output_len,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if result == 0 && output_len >= 4 {
|
||||||
|
// Extract new instance_id from output
|
||||||
|
let new_instance_id = u32::from_le_bytes([
|
||||||
|
output_buffer[0], output_buffer[1],
|
||||||
|
output_buffer[2], output_buffer[3]
|
||||||
|
]);
|
||||||
|
|
||||||
|
eprintln!("🎉 clone_box success: created new {} instance_id={}", self.box_type, new_instance_id);
|
||||||
|
|
||||||
|
// Return new PluginBoxV2 with new instance_id
|
||||||
|
Box::new(PluginBoxV2 {
|
||||||
|
box_type: self.box_type.clone(),
|
||||||
|
type_id: self.type_id,
|
||||||
|
invoke_fn: self.invoke_fn,
|
||||||
|
instance_id: new_instance_id,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
eprintln!("❌ clone_box failed: birth() returned error code {}", result);
|
||||||
|
// Fallback: return error message as StringBox
|
||||||
|
Box::new(StringBox::new(format!("Clone failed for {}", self.box_type)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string_box(&self) -> crate::box_trait::StringBox {
|
fn to_string_box(&self) -> crate::box_trait::StringBox {
|
||||||
@ -74,7 +118,15 @@ impl NyashBox for PluginBoxV2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||||
Box::new(StringBox::new(format!("Cannot share plugin box {}", self.box_type)))
|
eprintln!("🔍 DEBUG: PluginBoxV2::share_box called for {} (id={})", self.box_type, self.instance_id);
|
||||||
|
|
||||||
|
// Share means returning a new Box with the same instance_id
|
||||||
|
Box::new(PluginBoxV2 {
|
||||||
|
box_type: self.box_type.clone(),
|
||||||
|
type_id: self.type_id,
|
||||||
|
invoke_fn: self.invoke_fn,
|
||||||
|
instance_id: self.instance_id, // Same instance_id - this is sharing!
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,6 +324,7 @@ impl PluginLoaderV2 {
|
|||||||
// Create v2 plugin box wrapper with actual instance_id
|
// Create v2 plugin box wrapper with actual instance_id
|
||||||
let plugin_box = PluginBoxV2 {
|
let plugin_box = PluginBoxV2 {
|
||||||
box_type: box_type.to_string(),
|
box_type: box_type.to_string(),
|
||||||
|
type_id,
|
||||||
invoke_fn: plugin.invoke_fn,
|
invoke_fn: plugin.invoke_fn,
|
||||||
instance_id,
|
instance_id,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user