🚀 feat: VM ExternCall実装 - ChatGPT5による plugin loader v2 統合
Phase 9.78b の続き: - VM::execute_instruction に ExternCall 完全実装 - plugin loader v2 経由でenv.console.log等を処理 - MirBuilder::build_method_call に最適化追加(new Class().method() → 直接Call) - extern_call メソッドを plugin_loader_v2 に追加 ChatGPT5によるVMとプラグインシステムの統合作業
This commit is contained in:
@ -477,11 +477,21 @@ impl VM {
|
|||||||
Ok(ControlFlow::Continue)
|
Ok(ControlFlow::Continue)
|
||||||
},
|
},
|
||||||
|
|
||||||
MirInstruction::Call { dst, func: _, args: _, effects: _ } => {
|
MirInstruction::Call { dst, func, args, effects: _ } => {
|
||||||
// For now, function calls return void
|
// Resolve function name from func value (expects Const String)
|
||||||
// TODO: Implement proper function call handling
|
let func_val = self.get_value(*func)?;
|
||||||
|
let func_name = match func_val {
|
||||||
|
VMValue::String(s) => s,
|
||||||
|
_ => return Err(VMError::InvalidInstruction("Call expects func to be a String name".to_string())),
|
||||||
|
};
|
||||||
|
// Gather argument VM values
|
||||||
|
let mut vm_args = Vec::new();
|
||||||
|
for arg_id in args {
|
||||||
|
vm_args.push(self.get_value(*arg_id)?);
|
||||||
|
}
|
||||||
|
let result = self.call_function_by_name(&func_name, vm_args)?;
|
||||||
if let Some(dst_id) = dst {
|
if let Some(dst_id) = dst {
|
||||||
self.set_value(*dst_id, VMValue::Void);
|
self.set_value(*dst_id, result);
|
||||||
}
|
}
|
||||||
Ok(ControlFlow::Continue)
|
Ok(ControlFlow::Continue)
|
||||||
},
|
},
|
||||||
@ -764,32 +774,30 @@ impl VM {
|
|||||||
|
|
||||||
// Phase 9.7: External Function Calls
|
// Phase 9.7: External Function Calls
|
||||||
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => {
|
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => {
|
||||||
// For VM backend, we implement a stub that logs the call
|
// Evaluate arguments as NyashBox for loader
|
||||||
// Real implementation would route to native host functions
|
let mut nyash_args: Vec<Box<dyn NyashBox>> = Vec::new();
|
||||||
let arg_values: Result<Vec<_>, _> = args.iter().map(|id| self.get_value(*id)).collect();
|
for arg_id in args {
|
||||||
let arg_values = arg_values?;
|
let arg_value = self.get_value(*arg_id)?;
|
||||||
|
nyash_args.push(arg_value.to_nyash_box());
|
||||||
println!("ExternCall: {}.{}({:?})", iface_name, method_name, arg_values);
|
}
|
||||||
|
// Route through plugin loader v2 (also handles env.* stubs)
|
||||||
// For console.log, print the message
|
let loader = crate::runtime::get_global_loader_v2();
|
||||||
if iface_name == "env.console" && method_name == "log" {
|
let loader = loader.read().map_err(|_| VMError::InvalidInstruction("Plugin loader lock poisoned".into()))?;
|
||||||
for arg in &arg_values {
|
match loader.extern_call(iface_name, method_name, &nyash_args) {
|
||||||
if let VMValue::String(s) = arg {
|
Ok(Some(result_box)) => {
|
||||||
println!("Console: {}", s);
|
if let Some(dst_id) = dst {
|
||||||
|
self.set_value(*dst_id, VMValue::from_nyash_box(result_box));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
if let Some(dst_id) = dst {
|
||||||
|
self.set_value(*dst_id, VMValue::Void);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For canvas operations, just log them for now
|
|
||||||
if iface_name == "env.canvas" {
|
|
||||||
println!("Canvas operation: {}", method_name);
|
|
||||||
}
|
}
|
||||||
|
Err(_) => {
|
||||||
// Store void result if destination is provided
|
return Err(VMError::InvalidInstruction(format!("ExternCall failed: {}.{}", iface_name, method_name)));
|
||||||
if let Some(dst) = dst {
|
}
|
||||||
self.set_value(*dst, VMValue::Void);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ControlFlow::Continue)
|
Ok(ControlFlow::Continue)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -926,15 +926,15 @@ impl MirBuilder {
|
|||||||
|
|
||||||
// Build argument expressions
|
// Build argument expressions
|
||||||
let mut arg_values = Vec::new();
|
let mut arg_values = Vec::new();
|
||||||
for arg in arguments {
|
for arg in &arguments {
|
||||||
arg_values.push(self.build_expression(arg)?);
|
arg_values.push(self.build_expression(arg.clone())?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create result value
|
// Create result value
|
||||||
let result_id = self.value_gen.next();
|
let result_id = self.value_gen.next();
|
||||||
|
|
||||||
// Check if this is an external call (console.log, canvas.fillRect, etc.)
|
// Check if this is an external call (console.log, canvas.fillRect, etc.)
|
||||||
if let ASTNode::Variable { name: object_name, .. } = object {
|
if let ASTNode::Variable { name: object_name, .. } = object.clone() {
|
||||||
match (object_name.as_str(), method.as_str()) {
|
match (object_name.as_str(), method.as_str()) {
|
||||||
("console", "log") => {
|
("console", "log") => {
|
||||||
// Generate ExternCall for console.log
|
// Generate ExternCall for console.log
|
||||||
@ -996,7 +996,27 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit a BoxCall instruction for regular method calls
|
// Optimization: If the object is a direct `new ClassName(...)`, lower to a direct Call
|
||||||
|
if let ASTNode::New { class, .. } = object {
|
||||||
|
// Build function name: "{Class}.{method}/{argc}"
|
||||||
|
let func_name = format!("{}.{}{}", class, method, format!("/{}", arg_values.len()));
|
||||||
|
// Create a constant for the function name
|
||||||
|
let func_val = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::Const { dst: func_val, value: ConstValue::String(func_name) })?;
|
||||||
|
// Prepare args: me + user args
|
||||||
|
let mut call_args = Vec::with_capacity(arg_values.len() + 1);
|
||||||
|
call_args.push(object_value);
|
||||||
|
call_args.extend(arg_values);
|
||||||
|
// Emit direct Call
|
||||||
|
self.emit_instruction(MirInstruction::Call {
|
||||||
|
dst: Some(result_id),
|
||||||
|
func: func_val,
|
||||||
|
args: call_args,
|
||||||
|
effects: EffectMask::READ.add(Effect::ReadHeap),
|
||||||
|
})?;
|
||||||
|
Ok(result_id)
|
||||||
|
} else {
|
||||||
|
// Fallback: Emit a BoxCall instruction for regular method calls
|
||||||
self.emit_instruction(MirInstruction::BoxCall {
|
self.emit_instruction(MirInstruction::BoxCall {
|
||||||
dst: Some(result_id),
|
dst: Some(result_id),
|
||||||
box_val: object_value,
|
box_val: object_value,
|
||||||
@ -1004,9 +1024,9 @@ impl MirBuilder {
|
|||||||
args: arg_values,
|
args: arg_values,
|
||||||
effects: EffectMask::READ.add(Effect::ReadHeap), // Method calls may have side effects
|
effects: EffectMask::READ.add(Effect::ReadHeap), // Method calls may have side effects
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(result_id)
|
Ok(result_id)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Build from expression: from Parent.method(arguments)
|
/// Build from expression: from Parent.method(arguments)
|
||||||
fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
|
|||||||
@ -206,6 +206,32 @@ impl PluginBoxV2 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform an external host call (env.* namespace) or return an error if unsupported
|
||||||
|
/// Returns Some(Box) for a value result, or None for void-like calls
|
||||||
|
pub fn extern_call(
|
||||||
|
&self,
|
||||||
|
iface_name: &str,
|
||||||
|
method_name: &str,
|
||||||
|
args: &[Box<dyn NyashBox>],
|
||||||
|
) -> BidResult<Option<Box<dyn NyashBox>>> {
|
||||||
|
match (iface_name, method_name) {
|
||||||
|
("env.console", "log") => {
|
||||||
|
for a in args {
|
||||||
|
println!("{}", a.to_string_box().value);
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
("env.canvas", _) => {
|
||||||
|
eprintln!("[env.canvas] {} invoked (stub)", method_name);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Future: route to plugin-defined extern interfaces via config
|
||||||
|
Err(BidError::InvalidMethod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Load single plugin
|
/// Load single plugin
|
||||||
pub fn load_plugin(&self, lib_name: &str, lib_def: &LibraryDefinition) -> BidResult<()> {
|
pub fn load_plugin(&self, lib_name: &str, lib_def: &LibraryDefinition) -> BidResult<()> {
|
||||||
// Check if already loaded
|
// Check if already loaded
|
||||||
@ -413,6 +439,15 @@ mod stub {
|
|||||||
pub fn create_box(&self, _t: &str, _a: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> {
|
pub fn create_box(&self, _t: &str, _a: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> {
|
||||||
Err(BidError::PluginError)
|
Err(BidError::PluginError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extern_call(
|
||||||
|
&self,
|
||||||
|
_iface_name: &str,
|
||||||
|
_method_name: &str,
|
||||||
|
_args: &[Box<dyn NyashBox>],
|
||||||
|
) -> BidResult<Option<Box<dyn NyashBox>>> {
|
||||||
|
Err(BidError::PluginError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static GLOBAL_LOADER_V2: Lazy<Arc<RwLock<PluginLoaderV2>>> =
|
static GLOBAL_LOADER_V2: Lazy<Arc<RwLock<PluginLoaderV2>>> =
|
||||||
|
|||||||
Reference in New Issue
Block a user