fix: Fix NyashParser import path and improve BoxCall fallback

- Fix NyashParser import path in vm.rs tests
- Improve BoxCall fallback logic for plugin/builtin methods
- Add proper function existence checks before lowering to Call

This ensures plugin Box methods correctly fall back to BoxCall
when the corresponding user-defined function doesn't exist.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-20 23:07:16 +09:00
parent 64167def8f
commit af32896574
2 changed files with 36 additions and 32 deletions

View File

@ -1072,7 +1072,7 @@ impl Default for VM {
mod tests { mod tests {
use super::*; use super::*;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock}; use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock};
use crate::NyashParser; use crate::parser::NyashParser;
use crate::runtime::NyashRuntime; use crate::runtime::NyashRuntime;
use crate::core::model::BoxDeclaration as CoreBoxDecl; use crate::core::model::BoxDeclaration as CoreBoxDecl;
use crate::interpreter::SharedState; use crate::interpreter::SharedState;

View File

@ -991,27 +991,30 @@ impl MirBuilder {
// Optimization: If the object is a direct `new ClassName(...)`, lower to a direct Call // Optimization: If the object is a direct `new ClassName(...)`, lower to a direct Call
if let ASTNode::New { class, .. } = object { if let ASTNode::New { class, .. } = object {
// Build function name: "{Class}.{method}/{argc}" // Build function name and only lower to Call if the function exists (user-defined)
let func_name = format!("{}.{}{}", class, method, format!("/{}", arg_values.len())); let func_name = format!("{}.{}{}", class, method, format!("/{}", arg_values.len()));
// Create a constant for the function name let can_lower = if let Some(ref module) = self.current_module { module.functions.contains_key(&func_name) } else { false };
if can_lower {
let func_val = self.value_gen.next(); let func_val = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: func_val, value: ConstValue::String(func_name) })?; 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); let mut call_args = Vec::with_capacity(arg_values.len() + 1);
call_args.push(object_value); call_args.push(object_value);
call_args.extend(arg_values); call_args.extend(arg_values);
// Emit direct Call
self.emit_instruction(MirInstruction::Call { self.emit_instruction(MirInstruction::Call {
dst: Some(result_id), dst: Some(result_id),
func: func_val, func: func_val,
args: call_args, args: call_args,
effects: EffectMask::READ.add(Effect::ReadHeap), effects: EffectMask::READ.add(Effect::ReadHeap),
})?; })?;
Ok(result_id) return Ok(result_id);
}
// else fall through to BoxCall below
} else { } else {
// If the object originates from a NewBox in this function, we can lower to Call as well // If the object originates from a NewBox in this function, we can lower to Call as well
if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() { if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() {
let func_name = format!("{}.{}{}", class_name, method, format!("/{}", arg_values.len())); let func_name = format!("{}.{}{}", class_name, method, format!("/{}", arg_values.len()));
let can_lower = if let Some(ref module) = self.current_module { module.functions.contains_key(&func_name) } else { false };
if can_lower {
let func_val = self.value_gen.next(); let func_val = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: func_val, value: ConstValue::String(func_name) })?; self.emit_instruction(MirInstruction::Const { dst: func_val, value: ConstValue::String(func_name) })?;
let mut call_args = Vec::with_capacity(arg_values.len() + 1); let mut call_args = Vec::with_capacity(arg_values.len() + 1);
@ -1023,9 +1026,12 @@ impl MirBuilder {
args: call_args, args: call_args,
effects: EffectMask::READ.add(Effect::ReadHeap), effects: EffectMask::READ.add(Effect::ReadHeap),
})?; })?;
Ok(result_id) return Ok(result_id);
} else { }
// Fallback: Emit a BoxCall instruction for regular method calls }
}
// Fallback: Emit a BoxCall instruction for regular or plugin/builtin 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,
@ -1035,8 +1041,6 @@ impl MirBuilder {
})?; })?;
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> {