🎉 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:
Moe Charm
2025-08-19 13:57:17 +09:00
parent ecc7062e4b
commit 519560b64b
6 changed files with 349 additions and 564 deletions

View File

@ -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::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
return self.execute_plugin_box_v2_method(plugin_box, method, arguments);
}
// InstanceBox method calls
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
// 🔥 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()))
}
}
}