From 41832b2c88c09a9b689bf6abc0a75b70da5ccb6a Mon Sep 17 00:00:00 2001 From: Moe Charm Date: Wed, 20 Aug 2025 19:10:30 +0900 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20feat:=20VM=20ExternCall=E5=AE=9F?= =?UTF-8?q?=E8=A3=85=20-=20ChatGPT5=E3=81=AB=E3=82=88=E3=82=8B=20plugin=20?= =?UTF-8?q?loader=20v2=20=E7=B5=B1=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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とプラグインシステムの統合作業 --- src/backend/vm.rs | 62 +++++++++++++++++++-------------- src/mir/builder.rs | 46 +++++++++++++++++------- src/runtime/plugin_loader_v2.rs | 37 +++++++++++++++++++- 3 files changed, 104 insertions(+), 41 deletions(-) diff --git a/src/backend/vm.rs b/src/backend/vm.rs index fefa2f2f..6e655d9c 100644 --- a/src/backend/vm.rs +++ b/src/backend/vm.rs @@ -477,11 +477,21 @@ impl VM { Ok(ControlFlow::Continue) }, - MirInstruction::Call { dst, func: _, args: _, effects: _ } => { - // For now, function calls return void - // TODO: Implement proper function call handling + MirInstruction::Call { dst, func, args, effects: _ } => { + // Resolve function name from func value (expects Const String) + 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 { - self.set_value(*dst_id, VMValue::Void); + self.set_value(*dst_id, result); } Ok(ControlFlow::Continue) }, @@ -764,32 +774,30 @@ impl VM { // Phase 9.7: External Function Calls MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => { - // For VM backend, we implement a stub that logs the call - // Real implementation would route to native host functions - let arg_values: Result, _> = args.iter().map(|id| self.get_value(*id)).collect(); - let arg_values = arg_values?; - - println!("ExternCall: {}.{}({:?})", iface_name, method_name, arg_values); - - // For console.log, print the message - if iface_name == "env.console" && method_name == "log" { - for arg in &arg_values { - if let VMValue::String(s) = arg { - println!("Console: {}", s); + // Evaluate arguments as NyashBox for loader + let mut nyash_args: Vec> = Vec::new(); + for arg_id in args { + let arg_value = self.get_value(*arg_id)?; + nyash_args.push(arg_value.to_nyash_box()); + } + // Route through plugin loader v2 (also handles env.* stubs) + let loader = crate::runtime::get_global_loader_v2(); + let loader = loader.read().map_err(|_| VMError::InvalidInstruction("Plugin loader lock poisoned".into()))?; + match loader.extern_call(iface_name, method_name, &nyash_args) { + Ok(Some(result_box)) => { + 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); + } + } + Err(_) => { + return Err(VMError::InvalidInstruction(format!("ExternCall failed: {}.{}", iface_name, method_name))); + } } - - // For canvas operations, just log them for now - if iface_name == "env.canvas" { - println!("Canvas operation: {}", method_name); - } - - // Store void result if destination is provided - if let Some(dst) = dst { - self.set_value(*dst, VMValue::Void); - } - Ok(ControlFlow::Continue) }, } diff --git a/src/mir/builder.rs b/src/mir/builder.rs index b4500710..1d7884aa 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -926,15 +926,15 @@ impl MirBuilder { // Build argument expressions let mut arg_values = Vec::new(); - for arg in arguments { - arg_values.push(self.build_expression(arg)?); + for arg in &arguments { + arg_values.push(self.build_expression(arg.clone())?); } // Create result value let result_id = self.value_gen.next(); // 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()) { ("console", "log") => { // Generate ExternCall for console.log @@ -996,16 +996,36 @@ impl MirBuilder { } } - // Emit a BoxCall instruction for regular method calls - self.emit_instruction(MirInstruction::BoxCall { - dst: Some(result_id), - box_val: object_value, - method, - args: arg_values, - effects: EffectMask::READ.add(Effect::ReadHeap), // Method calls may have side effects - })?; - - Ok(result_id) + // 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 { + dst: Some(result_id), + box_val: object_value, + method, + args: arg_values, + effects: EffectMask::READ.add(Effect::ReadHeap), // Method calls may have side effects + })?; + Ok(result_id) + } } /// Build from expression: from Parent.method(arguments) diff --git a/src/runtime/plugin_loader_v2.rs b/src/runtime/plugin_loader_v2.rs index bf5929c2..9c7f4824 100644 --- a/src/runtime/plugin_loader_v2.rs +++ b/src/runtime/plugin_loader_v2.rs @@ -193,7 +193,7 @@ impl PluginBoxV2 { } /// Load all plugins from config - pub fn load_all_plugins(&self) -> BidResult<()> { + pub fn load_all_plugins(&self) -> BidResult<()> { let config = self.config.as_ref() .ok_or(BidError::PluginError)?; @@ -205,6 +205,32 @@ impl PluginBoxV2 { 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], + ) -> BidResult>> { + 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 pub fn load_plugin(&self, lib_name: &str, lib_def: &LibraryDefinition) -> BidResult<()> { @@ -413,6 +439,15 @@ mod stub { pub fn create_box(&self, _t: &str, _a: &[Box]) -> BidResult> { Err(BidError::PluginError) } + + pub fn extern_call( + &self, + _iface_name: &str, + _method_name: &str, + _args: &[Box], + ) -> BidResult>> { + Err(BidError::PluginError) + } } static GLOBAL_LOADER_V2: Lazy>> =