refactor(handlers): extract boxes_instance module (Phase 2-2)
Split handlers/boxes.rs (648→497 lines, -151 lines, -23.3%) Changes: - Created boxes_instance.rs (150 lines) - Extracted try_handle_instance_box function - Handles InstanceBox method dispatch - Supports Class.method/arity and ClassInstance.method/arity - Updated mod.rs to declare new module - Updated boxes.rs to redirect to new module - Build verified: 0 errors, 85 warnings Progress: - Phase 1: Legacy deletion (2,997 lines) - Phase 2-1: boxes_object_fields split (360 lines) - Phase 2-2: boxes_instance split (151 lines) - Total reduction so far: 3,508 lines Next: Phase 2-3 (boxes_plugin.rs, ~176 lines) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -212,7 +212,7 @@ impl MirInterpreter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.try_handle_instance_box(dst, box_val, method, args)? {
|
if super::boxes_instance::try_handle_instance_box(self, dst, box_val, method, args)? {
|
||||||
if method == "length" && std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
if method == "length" && std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
||||||
eprintln!("[vm-trace] length dispatch handler=instance_box");
|
eprintln!("[vm-trace] length dispatch handler=instance_box");
|
||||||
}
|
}
|
||||||
@ -308,157 +308,6 @@ impl MirInterpreter {
|
|||||||
super::boxes_string::try_handle_string_box(self, dst, box_val, method, args)
|
super::boxes_string::try_handle_string_box(self, dst, box_val, method, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_handle_instance_box(
|
|
||||||
&mut self,
|
|
||||||
dst: Option<ValueId>,
|
|
||||||
box_val: ValueId,
|
|
||||||
method: &str,
|
|
||||||
args: &[ValueId],
|
|
||||||
) -> Result<bool, VMError> {
|
|
||||||
let recv_vm = self.reg_load(box_val)?;
|
|
||||||
let recv_box_any: Box<dyn NyashBox> = match recv_vm.clone() {
|
|
||||||
VMValue::BoxRef(b) => b.share_box(),
|
|
||||||
other => other.to_nyash_box(),
|
|
||||||
};
|
|
||||||
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") && method == "toString" {
|
|
||||||
eprintln!("[vm-trace] instance-check recv_box_any.type={} args_len={}", recv_box_any.type_name(), args.len());
|
|
||||||
}
|
|
||||||
if let Some(inst) = recv_box_any.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
|
|
||||||
// Development guard: ensure JsonScanner core fields have sensible defaults
|
|
||||||
if inst.class_name == "JsonScanner" {
|
|
||||||
// populate missing fields to avoid Void in comparisons inside is_eof/advance
|
|
||||||
if inst.get_field_ng("position").is_none() {
|
|
||||||
let _ = inst.set_field_ng("position".to_string(), crate::value::NyashValue::Integer(0));
|
|
||||||
}
|
|
||||||
if inst.get_field_ng("length").is_none() {
|
|
||||||
let _ = inst.set_field_ng("length".to_string(), crate::value::NyashValue::Integer(0));
|
|
||||||
}
|
|
||||||
if inst.get_field_ng("line").is_none() {
|
|
||||||
let _ = inst.set_field_ng("line".to_string(), crate::value::NyashValue::Integer(1));
|
|
||||||
}
|
|
||||||
if inst.get_field_ng("column").is_none() {
|
|
||||||
let _ = inst.set_field_ng("column".to_string(), crate::value::NyashValue::Integer(1));
|
|
||||||
}
|
|
||||||
if inst.get_field_ng("text").is_none() {
|
|
||||||
let _ = inst.set_field_ng("text".to_string(), crate::value::NyashValue::String(String::new()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// JsonNodeInstance narrow bridges removed: rely on builder rewrite and instance dispatch
|
|
||||||
// birth: do not short-circuit; allow dispatch to lowered function "Class.birth/arity"
|
|
||||||
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") && method == "toString" {
|
|
||||||
eprintln!(
|
|
||||||
"[vm-trace] instance-check downcast=ok class={} stringify_present={{class:{}, alt:{}}}",
|
|
||||||
inst.class_name,
|
|
||||||
self.functions.contains_key(&format!("{}.stringify/0", inst.class_name)),
|
|
||||||
self.functions.contains_key(&format!("{}Instance.stringify/0", inst.class_name))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Resolve lowered method function: "Class.method/arity"
|
|
||||||
let primary = format!("{}.{}{}", inst.class_name, method, format!("/{}", args.len()));
|
|
||||||
// Alternate naming: "ClassInstance.method/arity"
|
|
||||||
let alt = format!("{}Instance.{}{}", inst.class_name, method, format!("/{}", args.len()));
|
|
||||||
// Static method variant that takes 'me' explicitly as first arg: "Class.method/(arity+1)"
|
|
||||||
let static_variant = format!("{}.{}{}", inst.class_name, method, format!("/{}", args.len() + 1));
|
|
||||||
// Special-case: toString() → stringify/0 if present
|
|
||||||
// Prefer base class (strip trailing "Instance") stringify when available.
|
|
||||||
let (stringify_base, stringify_inst) = if method == "toString" && args.is_empty() {
|
|
||||||
let base = inst
|
|
||||||
.class_name
|
|
||||||
.strip_suffix("Instance")
|
|
||||||
.map(|s| s.to_string());
|
|
||||||
let base_name = base.unwrap_or_else(|| inst.class_name.clone());
|
|
||||||
(
|
|
||||||
Some(format!("{}.stringify/0", base_name)),
|
|
||||||
Some(format!("{}.stringify/0", inst.class_name)),
|
|
||||||
)
|
|
||||||
} else { (None, None) };
|
|
||||||
|
|
||||||
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
|
||||||
eprintln!(
|
|
||||||
"[vm-trace] instance-dispatch class={} method={} arity={} candidates=[{}, {}, {}]",
|
|
||||||
inst.class_name, method, args.len(), primary, alt, static_variant
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer stringify for toString() if present (semantic alias). Try instance first, then base.
|
|
||||||
let func_opt = if let Some(ref sname) = stringify_inst {
|
|
||||||
self.functions.get(sname).cloned()
|
|
||||||
} else { None }
|
|
||||||
.or_else(|| stringify_base.as_ref().and_then(|n| self.functions.get(n).cloned()))
|
|
||||||
.or_else(|| self.functions.get(&primary).cloned())
|
|
||||||
.or_else(|| self.functions.get(&alt).cloned())
|
|
||||||
.or_else(|| self.functions.get(&static_variant).cloned());
|
|
||||||
|
|
||||||
if let Some(func) = func_opt {
|
|
||||||
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
|
||||||
eprintln!("[vm-trace] instance-dispatch hit -> {}", func.signature.name);
|
|
||||||
}
|
|
||||||
// Build argv: me + args (works for both instance and static(me, ...))
|
|
||||||
let mut argv: Vec<VMValue> = Vec::with_capacity(1 + args.len());
|
|
||||||
// Dev assert: forbid birth(me==Void)
|
|
||||||
if method == "birth" && crate::config::env::using_is_dev() {
|
|
||||||
if matches!(recv_vm, VMValue::Void) {
|
|
||||||
return Err(VMError::InvalidInstruction("Dev assert: birth(me==Void) is forbidden".into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
argv.push(recv_vm.clone());
|
|
||||||
for a in args { argv.push(self.reg_load(*a)?); }
|
|
||||||
let ret = self.exec_function_inner(&func, Some(&argv))?;
|
|
||||||
if let Some(d) = dst { self.regs.insert(d, ret); }
|
|
||||||
return Ok(true);
|
|
||||||
} else {
|
|
||||||
// Conservative fallback: search unique function by name tail ".method/arity"
|
|
||||||
let tail = format!(".{}{}", method, format!("/{}", args.len()));
|
|
||||||
let mut cands: Vec<String> = self
|
|
||||||
.functions
|
|
||||||
.keys()
|
|
||||||
.filter(|k| k.ends_with(&tail))
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
if !cands.is_empty() {
|
|
||||||
// Always narrow by receiver class prefix (and optional "Instance" suffix)
|
|
||||||
let recv_cls = inst.class_name.clone();
|
|
||||||
let pref1 = format!("{}.", recv_cls);
|
|
||||||
let pref2 = format!("{}Instance.", recv_cls);
|
|
||||||
let filtered: Vec<String> = cands
|
|
||||||
.into_iter()
|
|
||||||
.filter(|k| k.starts_with(&pref1) || k.starts_with(&pref2))
|
|
||||||
.collect();
|
|
||||||
if filtered.len() == 1 {
|
|
||||||
let fname = &filtered[0];
|
|
||||||
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
|
||||||
eprintln!("[vm-trace] instance-dispatch fallback (scoped) -> {}", fname);
|
|
||||||
}
|
|
||||||
if let Some(func) = self.functions.get(fname).cloned() {
|
|
||||||
let mut argv: Vec<VMValue> = Vec::with_capacity(1 + args.len());
|
|
||||||
if method == "birth" && crate::config::env::using_is_dev() {
|
|
||||||
if matches!(recv_vm, VMValue::Void) {
|
|
||||||
return Err(VMError::InvalidInstruction("Dev assert: birth(me==Void) is forbidden".into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
argv.push(recv_vm.clone());
|
|
||||||
for a in args { argv.push(self.reg_load(*a)?); }
|
|
||||||
let ret = self.exec_function_inner(&func, Some(&argv))?;
|
|
||||||
if let Some(d) = dst { self.regs.insert(d, ret); }
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
} else if filtered.len() > 1 {
|
|
||||||
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
|
||||||
eprintln!("[vm-trace] instance-dispatch multiple candidates after narrowing: {:?}", filtered);
|
|
||||||
}
|
|
||||||
// Ambiguous: do not dispatch cross-class
|
|
||||||
} else {
|
|
||||||
// No same-class candidate: do not dispatch cross-class
|
|
||||||
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
|
||||||
eprintln!("[vm-trace] instance-dispatch no same-class candidate for tail .{}{}", method, format!("/{}", args.len()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// moved: try_handle_array_box → handlers/boxes_array.rs
|
// moved: try_handle_array_box → handlers/boxes_array.rs
|
||||||
fn try_handle_array_box(
|
fn try_handle_array_box(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
153
src/backend/mir_interpreter/handlers/boxes_instance.rs
Normal file
153
src/backend/mir_interpreter/handlers/boxes_instance.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::box_trait::NyashBox;
|
||||||
|
|
||||||
|
pub(super) fn try_handle_instance_box(
|
||||||
|
this: &mut MirInterpreter,
|
||||||
|
dst: Option<ValueId>,
|
||||||
|
box_val: ValueId,
|
||||||
|
method: &str,
|
||||||
|
args: &[ValueId],
|
||||||
|
) -> Result<bool, VMError> {
|
||||||
|
let recv_vm = this.reg_load(box_val)?;
|
||||||
|
let recv_box_any: Box<dyn NyashBox> = match recv_vm.clone() {
|
||||||
|
VMValue::BoxRef(b) => b.share_box(),
|
||||||
|
other => other.to_nyash_box(),
|
||||||
|
};
|
||||||
|
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") && method == "toString" {
|
||||||
|
eprintln!("[vm-trace] instance-check recv_box_any.type={} args_len={}", recv_box_any.type_name(), args.len());
|
||||||
|
}
|
||||||
|
if let Some(inst) = recv_box_any.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
|
||||||
|
// Development guard: ensure JsonScanner core fields have sensible defaults
|
||||||
|
if inst.class_name == "JsonScanner" {
|
||||||
|
// populate missing fields to avoid Void in comparisons inside is_eof/advance
|
||||||
|
if inst.get_field_ng("position").is_none() {
|
||||||
|
let _ = inst.set_field_ng("position".to_string(), crate::value::NyashValue::Integer(0));
|
||||||
|
}
|
||||||
|
if inst.get_field_ng("length").is_none() {
|
||||||
|
let _ = inst.set_field_ng("length".to_string(), crate::value::NyashValue::Integer(0));
|
||||||
|
}
|
||||||
|
if inst.get_field_ng("line").is_none() {
|
||||||
|
let _ = inst.set_field_ng("line".to_string(), crate::value::NyashValue::Integer(1));
|
||||||
|
}
|
||||||
|
if inst.get_field_ng("column").is_none() {
|
||||||
|
let _ = inst.set_field_ng("column".to_string(), crate::value::NyashValue::Integer(1));
|
||||||
|
}
|
||||||
|
if inst.get_field_ng("text").is_none() {
|
||||||
|
let _ = inst.set_field_ng("text".to_string(), crate::value::NyashValue::String(String::new()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// JsonNodeInstance narrow bridges removed: rely on builder rewrite and instance dispatch
|
||||||
|
// birth: do not short-circuit; allow dispatch to lowered function "Class.birth/arity"
|
||||||
|
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") && method == "toString" {
|
||||||
|
eprintln!(
|
||||||
|
"[vm-trace] instance-check downcast=ok class={} stringify_present={{class:{}, alt:{}}}",
|
||||||
|
inst.class_name,
|
||||||
|
this.functions.contains_key(&format!("{}.stringify/0", inst.class_name)),
|
||||||
|
this.functions.contains_key(&format!("{}Instance.stringify/0", inst.class_name))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Resolve lowered method function: "Class.method/arity"
|
||||||
|
let primary = format!("{}.{}{}", inst.class_name, method, format!("/{}", args.len()));
|
||||||
|
// Alternate naming: "ClassInstance.method/arity"
|
||||||
|
let alt = format!("{}Instance.{}{}", inst.class_name, method, format!("/{}", args.len()));
|
||||||
|
// Static method variant that takes 'me' explicitly as first arg: "Class.method/(arity+1)"
|
||||||
|
let static_variant = format!("{}.{}{}", inst.class_name, method, format!("/{}", args.len() + 1));
|
||||||
|
// Special-case: toString() → stringify/0 if present
|
||||||
|
// Prefer base class (strip trailing "Instance") stringify when available.
|
||||||
|
let (stringify_base, stringify_inst) = if method == "toString" && args.is_empty() {
|
||||||
|
let base = inst
|
||||||
|
.class_name
|
||||||
|
.strip_suffix("Instance")
|
||||||
|
.map(|s| s.to_string());
|
||||||
|
let base_name = base.unwrap_or_else(|| inst.class_name.clone());
|
||||||
|
(
|
||||||
|
Some(format!("{}.stringify/0", base_name)),
|
||||||
|
Some(format!("{}.stringify/0", inst.class_name)),
|
||||||
|
)
|
||||||
|
} else { (None, None) };
|
||||||
|
|
||||||
|
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!(
|
||||||
|
"[vm-trace] instance-dispatch class={} method={} arity={} candidates=[{}, {}, {}]",
|
||||||
|
inst.class_name, method, args.len(), primary, alt, static_variant
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer stringify for toString() if present (semantic alias). Try instance first, then base.
|
||||||
|
let func_opt = if let Some(ref sname) = stringify_inst {
|
||||||
|
this.functions.get(sname).cloned()
|
||||||
|
} else { None }
|
||||||
|
.or_else(|| stringify_base.as_ref().and_then(|n| this.functions.get(n).cloned()))
|
||||||
|
.or_else(|| this.functions.get(&primary).cloned())
|
||||||
|
.or_else(|| this.functions.get(&alt).cloned())
|
||||||
|
.or_else(|| this.functions.get(&static_variant).cloned());
|
||||||
|
|
||||||
|
if let Some(func) = func_opt {
|
||||||
|
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[vm-trace] instance-dispatch hit -> {}", func.signature.name);
|
||||||
|
}
|
||||||
|
// Build argv: me + args (works for both instance and static(me, ...))
|
||||||
|
let mut argv: Vec<VMValue> = Vec::with_capacity(1 + args.len());
|
||||||
|
// Dev assert: forbid birth(me==Void)
|
||||||
|
if method == "birth" && crate::config::env::using_is_dev() {
|
||||||
|
if matches!(recv_vm, VMValue::Void) {
|
||||||
|
return Err(VMError::InvalidInstruction("Dev assert: birth(me==Void) is forbidden".into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argv.push(recv_vm.clone());
|
||||||
|
for a in args { argv.push(this.reg_load(*a)?); }
|
||||||
|
let ret = this.exec_function_inner(&func, Some(&argv))?;
|
||||||
|
if let Some(d) = dst { this.regs.insert(d, ret); }
|
||||||
|
return Ok(true);
|
||||||
|
} else {
|
||||||
|
// Conservative fallback: search unique function by name tail ".method/arity"
|
||||||
|
let tail = format!(".{}{}", method, format!("/{}", args.len()));
|
||||||
|
let mut cands: Vec<String> = this
|
||||||
|
.functions
|
||||||
|
.keys()
|
||||||
|
.filter(|k| k.ends_with(&tail))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
if !cands.is_empty() {
|
||||||
|
// Always narrow by receiver class prefix (and optional "Instance" suffix)
|
||||||
|
let recv_cls = inst.class_name.clone();
|
||||||
|
let pref1 = format!("{}.", recv_cls);
|
||||||
|
let pref2 = format!("{}Instance.", recv_cls);
|
||||||
|
let filtered: Vec<String> = cands
|
||||||
|
.into_iter()
|
||||||
|
.filter(|k| k.starts_with(&pref1) || k.starts_with(&pref2))
|
||||||
|
.collect();
|
||||||
|
if filtered.len() == 1 {
|
||||||
|
let fname = &filtered[0];
|
||||||
|
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[vm-trace] instance-dispatch fallback (scoped) -> {}", fname);
|
||||||
|
}
|
||||||
|
if let Some(func) = this.functions.get(fname).cloned() {
|
||||||
|
let mut argv: Vec<VMValue> = Vec::with_capacity(1 + args.len());
|
||||||
|
if method == "birth" && crate::config::env::using_is_dev() {
|
||||||
|
if matches!(recv_vm, VMValue::Void) {
|
||||||
|
return Err(VMError::InvalidInstruction("Dev assert: birth(me==Void) is forbidden".into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argv.push(recv_vm.clone());
|
||||||
|
for a in args { argv.push(this.reg_load(*a)?); }
|
||||||
|
let ret = this.exec_function_inner(&func, Some(&argv))?;
|
||||||
|
if let Some(d) = dst { this.regs.insert(d, ret); }
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
} else if filtered.len() > 1 {
|
||||||
|
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[vm-trace] instance-dispatch multiple candidates after narrowing: {:?}", filtered);
|
||||||
|
}
|
||||||
|
// Ambiguous: do not dispatch cross-class
|
||||||
|
} else {
|
||||||
|
// No same-class candidate: do not dispatch cross-class
|
||||||
|
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[vm-trace] instance-dispatch no same-class candidate for tail .{}{}", method, format!("/{}", args.len()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ mod boxes_array;
|
|||||||
mod boxes_string;
|
mod boxes_string;
|
||||||
mod boxes_map;
|
mod boxes_map;
|
||||||
mod boxes_object_fields;
|
mod boxes_object_fields;
|
||||||
|
mod boxes_instance;
|
||||||
mod calls;
|
mod calls;
|
||||||
mod externals;
|
mod externals;
|
||||||
mod memory;
|
mod memory;
|
||||||
|
|||||||
Reference in New Issue
Block a user