diff --git a/docs/CURRENT_TASK.md b/docs/CURRENT_TASK.md index d8d8c74a..7350dbf1 100644 --- a/docs/CURRENT_TASK.md +++ b/docs/CURRENT_TASK.md @@ -1,28 +1,80 @@ # 🎯 現在のタスク (2025-08-19 更新) -## 🔥 最優先タスク:nyash.toml v2対応 +## 🎉 FileBox v2プラグインシステム完全動作達成! -### 📍 問題の本質 -**nyash.toml v2(マルチBox型)に誰も対応していない!** +### 📍 本日の成果 +**FileBoxプラグインの全機能が正常動作!** -1. **プラグインテスター** - 古い単一Box型前提 -2. **Nyash本体のレジストリ** - 古い単一Box型前提 -3. **結果** - プラグインが正しく読み込まれない +1. **✅ 重複実装の解消** + - `method_dispatch.rs` を削除(使われていないコード) + - `calls.rs` が実際の実装であることを確認 -### 🎯 正しい実装順序 -1. **プラグインテスターをnyash.toml v2対応にする** - - マルチBox型プラグイン対応 - - nyash.tomlから型情報読み取り +2. **✅ TLVエンコーディング修正** + ```rust + // Header: version(2 bytes) + argc(2 bytes) + tlv_data.extend_from_slice(&1u16.to_le_bytes()); + tlv_data.extend_from_slice(&(arg_values.len() as u16).to_le_bytes()); -2. **プラグインテスターで動作確認** - - FileBoxプラグインが正しく認識されるか - - メソッド情報が正しく取得できるか - -3. **Nyash本体のレジストリに移植** - - プラグインテスターの実装をコピー - - 汎用プラグインBox生成が動作 + // 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()); + ``` -### 📝 nyash.toml v2形式(確認) +3. **✅ FileBox全機能テスト成功** + - open("file.txt", "w") - 書き込みモードで開く + - write("data") - データ書き込み(バイト数返却) + - read() - ファイル内容読み込み + - close() - ファイルクローズ + - 実際のファイル作成・読み書き確認済み + +### 🔥 今日の重要な発見 +**コードフローの正確な追跡の重要性** + +「深く考える」ことで、以下を発見: +- 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 [libraries] "libnyash_filebox_plugin.so" = { @@ -42,54 +94,10 @@ close = { method_id = 4 } 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日 -**次回マイルストーン**: プラグインテスターのnyash.toml v2対応 \ No newline at end of file +**次回マイルストーン**: 他のプラグインのv2移行 \ No newline at end of file diff --git a/local_tests/test_filebox_debug.nyash b/local_tests/test_filebox_debug.nyash new file mode 100644 index 00000000..46949178 --- /dev/null +++ b/local_tests/test_filebox_debug.nyash @@ -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) \ No newline at end of file diff --git a/local_tests/test_filebox_full.nyash b/local_tests/test_filebox_full.nyash new file mode 100644 index 00000000..16dfe5c1 --- /dev/null +++ b/local_tests/test_filebox_full.nyash @@ -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) \ No newline at end of file diff --git a/src/interpreter/expressions/calls.rs b/src/interpreter/expressions/calls.rs index 960b9e42..e591092d 100644 --- a/src/interpreter/expressions/calls.rs +++ b/src/interpreter/expressions/calls.rs @@ -13,6 +13,7 @@ use crate::instance::InstanceBox; use crate::channel_box::ChannelBox; use crate::interpreter::core::{NyashInterpreter, RuntimeError}; use crate::interpreter::finalization; +use crate::runtime::plugin_loader_v2::PluginBoxV2; use std::sync::Arc; impl NyashInterpreter { @@ -487,6 +488,11 @@ impl NyashInterpreter { // RangeBox method calls (将来的に追加予定) + // PluginBoxV2 method calls + if let Some(plugin_box) = obj_value.as_any().downcast_ref::() { + return self.execute_plugin_box_v2_method(plugin_box, method, arguments); + } + // InstanceBox method calls if let Some(instance) = obj_value.as_any().downcast_ref::() { // 🔥 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, 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_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())) + } + } } \ No newline at end of file diff --git a/src/interpreter/method_dispatch.rs b/src/interpreter/method_dispatch.rs deleted file mode 100644 index b7aa9d19..00000000 --- a/src/interpreter/method_dispatch.rs +++ /dev/null @@ -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, 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, 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; - 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>, 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, - method: &str, - arguments: &[ASTNode], - object: &ASTNode - ) -> Result, RuntimeError> { - // StringBox method calls - if let Some(string_box) = obj_value.as_any().downcast_ref::() { - return self.execute_string_method(string_box, method, arguments); - } - - // IntegerBox method calls - if let Some(integer_box) = obj_value.as_any().downcast_ref::() { - return self.execute_integer_method(integer_box, method, arguments); - } - - // FloatBox method calls - if let Some(float_box) = obj_value.as_any().downcast_ref::() { - return self.execute_float_method(float_box, method, arguments); - } - - // BoolBox method calls - if let Some(bool_box) = obj_value.as_any().downcast_ref::() { - return self.execute_bool_method(bool_box, method, arguments); - } - - // ArrayBox method calls - if let Some(array_box) = obj_value.as_any().downcast_ref::() { - return self.execute_array_method(array_box, method, arguments); - } - - // BufferBox method calls - if let Some(buffer_box) = obj_value.as_any().downcast_ref::() { - return self.execute_buffer_method(buffer_box, method, arguments); - } - - // FileBox method calls - if let Some(file_box) = obj_value.as_any().downcast_ref::() { - return self.execute_file_method(file_box, method, arguments); - } - // Plugin-backed FileBox method calls - if let Some(pfile) = obj_value.as_any().downcast_ref::() { - return self.execute_plugin_file_method(pfile, method, arguments); - } - - // ResultBox method calls - if let Some(result_box) = obj_value.as_any().downcast_ref::() { - return self.execute_result_method(result_box, method, arguments); - } - - // FutureBox method calls - if let Some(future_box) = obj_value.as_any().downcast_ref::() { - return self.execute_future_method(future_box, method, arguments); - } - - // ChannelBox method calls - if let Some(channel_box) = obj_value.as_any().downcast_ref::() { - return self.execute_channel_method(channel_box, method, arguments); - } - - // JSONBox method calls - if let Some(json_box) = obj_value.as_any().downcast_ref::() { - return self.execute_json_method(json_box, method, arguments); - } - - // HttpClientBox method calls - if let Some(http_box) = obj_value.as_any().downcast_ref::() { - return self.execute_http_method(http_box, method, arguments); - } - - // StreamBox method calls - if let Some(stream_box) = obj_value.as_any().downcast_ref::() { - return self.execute_stream_method(stream_box, method, arguments); - } - - // RegexBox method calls - if let Some(regex_box) = obj_value.as_any().downcast_ref::() { - return self.execute_regex_method(regex_box, method, arguments); - } - - // MathBox method calls - if let Some(math_box) = obj_value.as_any().downcast_ref::() { - return self.execute_math_method(math_box, method, arguments); - } - - // NullBox method calls - if let Some(null_box) = obj_value.as_any().downcast_ref::() { - return self.execute_null_method(null_box, method, arguments); - } - - // TimeBox method calls - if let Some(time_box) = obj_value.as_any().downcast_ref::() { - return self.execute_time_method(time_box, method, arguments); - } - - // DateTimeBox method calls - if let Some(datetime_box) = obj_value.as_any().downcast_ref::() { - return self.execute_datetime_method(datetime_box, method, arguments); - } - - // TimerBox method calls - if let Some(timer_box) = obj_value.as_any().downcast_ref::() { - return self.execute_timer_method(timer_box, method, arguments); - } - - // MapBox method calls - if let Some(map_box) = obj_value.as_any().downcast_ref::() { - return self.execute_map_method(map_box, method, arguments); - } - - // RandomBox method calls - if let Some(random_box) = obj_value.as_any().downcast_ref::() { - return self.execute_random_method(random_box, method, arguments); - } - - // SoundBox method calls - if let Some(sound_box) = obj_value.as_any().downcast_ref::() { - return self.execute_sound_method(sound_box, method, arguments); - } - - // DebugBox method calls - if let Some(debug_box) = obj_value.as_any().downcast_ref::() { - return self.execute_debug_method(debug_box, method, arguments); - } - - // ConsoleBox method calls - if let Some(console_box) = obj_value.as_any().downcast_ref::() { - return self.execute_console_method(console_box, method, arguments); - } - - // IntentBox method calls - if let Some(intent_box) = obj_value.as_any().downcast_ref::() { - return self.execute_intent_box_method(intent_box, method, arguments); - } - - // SocketBox method calls - if let Some(socket_box) = obj_value.as_any().downcast_ref::() { - 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::() { - 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::() { - 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::() { - 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::() { - // 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::() { - 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::() { - 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::() { - 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::() { - 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, 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::() { - 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::() { - 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, - method: &str, - arguments: &[ASTNode] - ) -> Result, RuntimeError> { - // InstanceBox method calls (user-defined methods) - if let Some(instance) = obj_value.as_any().downcast_ref::() { - 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()), - }) - } -} diff --git a/src/runtime/plugin_loader_v2.rs b/src/runtime/plugin_loader_v2.rs index 5513ba56..ee40d2ef 100644 --- a/src/runtime/plugin_loader_v2.rs +++ b/src/runtime/plugin_loader_v2.rs @@ -29,9 +29,10 @@ pub struct LoadedPluginV2 { /// v2 Plugin Box wrapper - temporary implementation #[derive(Debug)] pub struct PluginBoxV2 { - box_type: String, - invoke_fn: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, - instance_id: u32, + pub box_type: String, + pub type_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 { @@ -58,11 +59,54 @@ impl BoxCore for PluginBoxV2 { impl NyashBox for PluginBoxV2 { 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 { - 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 { @@ -74,7 +118,15 @@ impl NyashBox for PluginBoxV2 { } fn share_box(&self) -> Box { - 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 let plugin_box = PluginBoxV2 { box_type: box_type.to_string(), + type_id, invoke_fn: plugin.invoke_fn, instance_id, };