refactor(handlers): extract boxes_object_fields module (Phase 2-1)

Split handlers/boxes.rs (1008→648 lines, -360 lines, -35.7%)

Changes:
- Created boxes_object_fields.rs (359 lines)
  - Extracted try_handle_object_fields function
  - Handles getField/setField for InstanceBox
- 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)
- Total reduction so far: 3,357 lines

Next: Phase 2-2 (boxes_instance.rs), Phase 2-3 (boxes_plugin.rs)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-01 12:32:38 +09:00
parent aacce00c38
commit c8856e1a08
3 changed files with 364 additions and 361 deletions

View File

@ -179,7 +179,7 @@ impl MirInterpreter {
}
_ => {}
}
if self.try_handle_object_fields(dst, box_val, method, args)? {
if super::boxes_object_fields::try_handle_object_fields(self, dst, box_val, method, args)? {
if method == "length" && std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] length dispatch handler=object_fields");
}
@ -286,366 +286,6 @@ impl MirInterpreter {
self.invoke_plugin_box(dst, box_val, method, args)
}
fn try_handle_object_fields(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
method: &str,
args: &[ValueId],
) -> Result<bool, VMError> {
// Local helpers to bridge NyashValue <-> VMValue for InstanceBox fields
fn vm_to_nv(v: &VMValue) -> crate::value::NyashValue {
use crate::value::NyashValue as NV;
use super::VMValue as VV;
match v {
VV::Integer(i) => NV::Integer(*i),
VV::Float(f) => NV::Float(*f),
VV::Bool(b) => NV::Bool(*b),
VV::String(s) => NV::String(s.clone()),
VV::Void => NV::Void,
VV::Future(_) => NV::Void, // not expected in fields
VV::BoxRef(_) => NV::Void, // store minimal; complex object fields are not required here
}
}
fn nv_to_vm(v: &crate::value::NyashValue) -> VMValue {
use crate::value::NyashValue as NV;
use super::VMValue as VV;
match v {
NV::Integer(i) => VV::Integer(*i),
NV::Float(f) => VV::Float(*f),
NV::Bool(b) => VV::Bool(*b),
NV::String(s) => VV::String(s.clone()),
NV::Null | NV::Void => VV::Void,
NV::Array(_) | NV::Map(_) | NV::Box(_) | NV::WeakBox(_) => VV::Void,
}
}
match method {
"getField" => {
if args.len() != 1 {
return Err(VMError::InvalidInstruction("getField expects 1 arg".into()));
}
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
let rk = match self.reg_load(box_val) {
Ok(VMValue::BoxRef(ref b)) => format!("BoxRef({})", b.type_name()),
Ok(VMValue::Integer(_)) => "Integer".to_string(),
Ok(VMValue::Float(_)) => "Float".to_string(),
Ok(VMValue::Bool(_)) => "Bool".to_string(),
Ok(VMValue::String(_)) => "String".to_string(),
Ok(VMValue::Void) => "Void".to_string(),
Ok(VMValue::Future(_)) => "Future".to_string(),
Err(_) => "<err>".to_string(),
};
eprintln!("[vm-trace] getField recv_kind={}", rk);
}
let fname = match self.reg_load(args[0])? {
VMValue::String(s) => s,
v => v.to_string(),
};
// Prefer InstanceBox internal storage (structural correctness)
if let VMValue::BoxRef(bref) = self.reg_load(box_val)? {
if let Some(inst) = bref.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField instance class={}", inst.class_name);
}
// Special-case bridge: JsonParser.length -> tokens.length()
if inst.class_name == "JsonParser" && fname == "length" {
if let Some(tokens_shared) = inst.get_field("tokens") {
let tokens_box: Box<dyn crate::box_trait::NyashBox> = tokens_shared.share_box();
if let Some(arr) = tokens_box.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
let len_box = arr.length();
if let Some(d) = dst { self.regs.insert(d, VMValue::from_nyash_box(len_box)); }
return Ok(true);
}
}
}
// First: prefer fields_ng (NyashValue) when present
if let Some(nv) = inst.get_field_ng(&fname) {
// Dev trace: JsonToken field get
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") && inst.class_name == "JsonToken" {
eprintln!("[vm-trace] JsonToken.getField name={} nv={:?}", fname, nv);
}
// Treat complex Box-like values as "missing" for internal storage so that
// legacy obj_fields (which stores BoxRef) is used instead.
// This avoids NV::Box/Array/Map being converted to Void by nv_to_vm.
let is_missing = matches!(
nv,
crate::value::NyashValue::Null
| crate::value::NyashValue::Void
| crate::value::NyashValue::Array(_)
| crate::value::NyashValue::Map(_)
| crate::value::NyashValue::Box(_)
| crate::value::NyashValue::WeakBox(_)
);
if !is_missing {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField internal {}.{} -> {:?}", inst.class_name, fname, nv);
}
if let Some(d) = dst {
// Special-case: NV::Box should surface as VMValue::BoxRef
if let crate::value::NyashValue::Box(ref arc_m) = nv {
if let Ok(guard) = arc_m.lock() {
let cloned: Box<dyn crate::box_trait::NyashBox> = guard.clone_box();
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::from(cloned);
self.regs.insert(d, VMValue::BoxRef(arc));
} else {
self.regs.insert(d, VMValue::Void);
}
} else {
self.regs.insert(d, nv_to_vm(&nv));
}
}
// Trace get
if Self::box_trace_enabled() {
let kind = match &nv {
crate::value::NyashValue::Integer(_) => "Integer",
crate::value::NyashValue::Float(_) => "Float",
crate::value::NyashValue::Bool(_) => "Bool",
crate::value::NyashValue::String(_) => "String",
crate::value::NyashValue::Null => "Null",
crate::value::NyashValue::Void => "Void",
crate::value::NyashValue::Array(_) => "Array",
crate::value::NyashValue::Map(_) => "Map",
crate::value::NyashValue::Box(_) => "Box",
crate::value::NyashValue::WeakBox(_) => "WeakBox",
};
self.box_trace_emit_get(&inst.class_name, &fname, kind);
}
return Ok(true);
} else {
// Provide pragmatic defaults for JsonScanner numeric fields
if inst.class_name == "JsonScanner" {
let def = match fname.as_str() {
"position" | "length" => Some(VMValue::Integer(0)),
"line" | "column" => Some(VMValue::Integer(1)),
"text" => Some(VMValue::String(String::new())),
_ => None,
};
if let Some(v) = def {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField default JsonScanner.{} -> {:?}", fname, v);
}
if let Some(d) = dst { self.regs.insert(d, v); }
return Ok(true);
}
}
}
} else {
// fields_ng missing entirely → try JsonScanner defaults next, otherwise fallback to legacy/opfields
if inst.class_name == "JsonScanner" {
let def = match fname.as_str() {
"position" | "length" => Some(VMValue::Integer(0)),
"line" | "column" => Some(VMValue::Integer(1)),
"text" => Some(VMValue::String(String::new())),
_ => None,
};
if let Some(v) = def {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField default(JsonScanner missing) {} -> {:?}", fname, v);
}
if let Some(d) = dst { self.regs.insert(d, v.clone()); }
if Self::box_trace_enabled() {
let kind = match &v {
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
VMValue::String(_) => "String",
VMValue::BoxRef(_) => "BoxRef",
VMValue::Void => "Void",
VMValue::Future(_) => "Future",
};
self.box_trace_emit_get(&inst.class_name, &fname, kind);
}
return Ok(true);
}
}
}
// Finally: legacy fields (SharedNyashBox) for complex values
if let Some(shared) = inst.get_field(&fname) {
if let Some(d) = dst { self.regs.insert(d, VMValue::BoxRef(shared.clone())); }
if Self::box_trace_enabled() {
self.box_trace_emit_get(&inst.class_name, &fname, "BoxRef");
}
return Ok(true);
}
}
}
let key = self.object_key_for(box_val);
let mut v = self
.obj_fields
.get(&key)
.and_then(|m| m.get(&fname))
.cloned()
.unwrap_or(VMValue::Void);
// Final safety (dev-only, narrow): if legacy path yields Void for well-known
// JsonScanner fields inside JsonScanner.{is_eof,current,advance}, provide
// pragmatic defaults to avoid Void comparisons during bring-up.
if let VMValue::Void = v {
let guard_on = std::env::var("NYASH_VM_SCANNER_DEFAULTS").ok().as_deref() == Some("1");
let fn_ctx = self.cur_fn.as_deref().unwrap_or("");
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField guard_check ctx={} guard_on={} name={}", fn_ctx, guard_on, fname);
}
if guard_on {
let fn_ctx = self.cur_fn.as_deref().unwrap_or("");
let is_scanner_ctx = matches!(
fn_ctx,
"JsonScanner.is_eof/0" | "JsonScanner.current/0" | "JsonScanner.advance/0"
);
if is_scanner_ctx {
// Try class-aware default first
if let Ok(VMValue::BoxRef(bref2)) = self.reg_load(box_val) {
if let Some(inst2) = bref2.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
if inst2.class_name == "JsonScanner" {
let fallback = match fname.as_str() {
"position" | "length" => Some(VMValue::Integer(0)),
"line" | "column" => Some(VMValue::Integer(1)),
"text" => Some(VMValue::String(String::new())),
_ => None,
};
if let Some(val) = fallback {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField final_default {} -> {:?}", fname, val);
}
v = val;
}
}
}
}
// Class nameが取得できなかった場合でも、フィールド名で限定的に適用
if matches!(v, VMValue::Void) {
let fallback2 = match fname.as_str() {
"position" | "length" => Some(VMValue::Integer(0)),
"line" | "column" => Some(VMValue::Integer(1)),
"text" => Some(VMValue::String(String::new())),
_ => None,
};
if let Some(val2) = fallback2 {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField final_default(class-agnostic) {} -> {:?}", fname, val2);
}
v = val2;
}
}
}
}
}
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
if let VMValue::BoxRef(b) = &v {
eprintln!("[vm-trace] getField legacy {} -> BoxRef({})", fname, b.type_name());
} else {
eprintln!("[vm-trace] getField legacy {} -> {:?}", fname, v);
}
}
if let Some(d) = dst { self.regs.insert(d, v.clone()); }
if Self::box_trace_enabled() {
let kind = match &v {
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
VMValue::String(_) => "String",
VMValue::BoxRef(b) => b.type_name(),
VMValue::Void => "Void",
VMValue::Future(_) => "Future",
};
// class name unknown here; use receiver type name if possible
let cls = match self.reg_load(box_val).unwrap_or(VMValue::Void) {
VMValue::BoxRef(b) => {
if let Some(inst) = b.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
inst.class_name.clone()
} else { b.type_name().to_string() }
}
_ => "<unknown>".to_string(),
};
self.box_trace_emit_get(&cls, &fname, kind);
}
Ok(true)
}
"setField" => {
if args.len() != 2 {
return Err(VMError::InvalidInstruction(
"setField expects 2 args".into(),
));
}
let fname = match self.reg_load(args[0])? {
VMValue::String(s) => s,
v => v.to_string(),
};
let valv = self.reg_load(args[1])?;
// Dev trace: JsonToken field set
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
if let VMValue::BoxRef(bref) = self.reg_load(box_val)? {
if let Some(inst) = bref.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
if inst.class_name == "JsonToken" {
eprintln!("[vm-trace] JsonToken.setField name={} vmval={:?}", fname, valv);
}
}
}
}
if Self::box_trace_enabled() {
let vkind = match &valv {
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
VMValue::String(_) => "String",
VMValue::BoxRef(b) => b.type_name(),
VMValue::Void => "Void",
VMValue::Future(_) => "Future",
};
let cls = match self.reg_load(box_val).unwrap_or(VMValue::Void) {
VMValue::BoxRef(b) => {
if let Some(inst) = b.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
inst.class_name.clone()
} else { b.type_name().to_string() }
}
_ => "<unknown>".to_string(),
};
self.box_trace_emit_set(&cls, &fname, vkind);
}
// Prefer InstanceBox internal storage
if let VMValue::BoxRef(bref) = self.reg_load(box_val)? {
if let Some(inst) = bref.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
// Primitives → 内部保存
if matches!(valv, VMValue::Integer(_) | VMValue::Float(_) | VMValue::Bool(_) | VMValue::String(_) | VMValue::Void) {
let _ = inst.set_field_ng(fname.clone(), vm_to_nv(&valv));
return Ok(true);
}
// BoxRef のうち、Integer/Float/Bool/String はプリミティブに剥がして内部保存
if let VMValue::BoxRef(bx) = &valv {
if let Some(ib) = bx.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
let _ = inst.set_field_ng(fname.clone(), crate::value::NyashValue::Integer(ib.value));
return Ok(true);
}
if let Some(fb) = bx.as_any().downcast_ref::<crate::boxes::FloatBox>() {
let _ = inst.set_field_ng(fname.clone(), crate::value::NyashValue::Float(fb.value));
return Ok(true);
}
if let Some(bb) = bx.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
let _ = inst.set_field_ng(fname.clone(), crate::value::NyashValue::Bool(bb.value));
return Ok(true);
}
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() {
let _ = inst.set_field_ng(fname.clone(), crate::value::NyashValue::String(sb.value.clone()));
return Ok(true);
}
// For complex Box values (InstanceBox/MapBox/ArrayBox...), store into
// legacy fields to preserve identity across clones/gets.
let _ = inst.set_field(fname.as_str(), std::sync::Arc::clone(bx));
return Ok(true);
}
}
}
let key = self.object_key_for(box_val);
self.obj_fields
.entry(key)
.or_default()
.insert(fname, valv);
Ok(true)
}
_ => Ok(false),
}
}
// moved: try_handle_map_box → handlers/boxes_map.rs
fn try_handle_map_box(
&mut self,

View File

@ -0,0 +1,362 @@
use super::*;
use crate::box_trait::NyashBox;
pub(super) fn try_handle_object_fields(
this: &mut MirInterpreter,
dst: Option<ValueId>,
box_val: ValueId,
method: &str,
args: &[ValueId],
) -> Result<bool, VMError> {
// Local helpers to bridge NyashValue <-> VMValue for InstanceBox fields
fn vm_to_nv(v: &VMValue) -> crate::value::NyashValue {
use crate::value::NyashValue as NV;
use super::VMValue as VV;
match v {
VV::Integer(i) => NV::Integer(*i),
VV::Float(f) => NV::Float(*f),
VV::Bool(b) => NV::Bool(*b),
VV::String(s) => NV::String(s.clone()),
VV::Void => NV::Void,
VV::Future(_) => NV::Void, // not expected in fields
VV::BoxRef(_) => NV::Void, // store minimal; complex object fields are not required here
}
}
fn nv_to_vm(v: &crate::value::NyashValue) -> VMValue {
use crate::value::NyashValue as NV;
use super::VMValue as VV;
match v {
NV::Integer(i) => VV::Integer(*i),
NV::Float(f) => VV::Float(*f),
NV::Bool(b) => VV::Bool(*b),
NV::String(s) => VV::String(s.clone()),
NV::Null | NV::Void => VV::Void,
NV::Array(_) | NV::Map(_) | NV::Box(_) | NV::WeakBox(_) => VV::Void,
}
}
match method {
"getField" => {
if args.len() != 1 {
return Err(VMError::InvalidInstruction("getField expects 1 arg".into()));
}
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
let rk = match this.reg_load(box_val) {
Ok(VMValue::BoxRef(ref b)) => format!("BoxRef({})", b.type_name()),
Ok(VMValue::Integer(_)) => "Integer".to_string(),
Ok(VMValue::Float(_)) => "Float".to_string(),
Ok(VMValue::Bool(_)) => "Bool".to_string(),
Ok(VMValue::String(_)) => "String".to_string(),
Ok(VMValue::Void) => "Void".to_string(),
Ok(VMValue::Future(_)) => "Future".to_string(),
Err(_) => "<err>".to_string(),
};
eprintln!("[vm-trace] getField recv_kind={}", rk);
}
let fname = match this.reg_load(args[0])? {
VMValue::String(s) => s,
v => v.to_string(),
};
// Prefer InstanceBox internal storage (structural correctness)
if let VMValue::BoxRef(bref) = this.reg_load(box_val)? {
if let Some(inst) = bref.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField instance class={}", inst.class_name);
}
// Special-case bridge: JsonParser.length -> tokens.length()
if inst.class_name == "JsonParser" && fname == "length" {
if let Some(tokens_shared) = inst.get_field("tokens") {
let tokens_box: Box<dyn crate::box_trait::NyashBox> = tokens_shared.share_box();
if let Some(arr) = tokens_box.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
let len_box = arr.length();
if let Some(d) = dst { this.regs.insert(d, VMValue::from_nyash_box(len_box)); }
return Ok(true);
}
}
}
// First: prefer fields_ng (NyashValue) when present
if let Some(nv) = inst.get_field_ng(&fname) {
// Dev trace: JsonToken field get
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") && inst.class_name == "JsonToken" {
eprintln!("[vm-trace] JsonToken.getField name={} nv={:?}", fname, nv);
}
// Treat complex Box-like values as "missing" for internal storage so that
// legacy obj_fields (which stores BoxRef) is used instead.
// This avoids NV::Box/Array/Map being converted to Void by nv_to_vm.
let is_missing = matches!(
nv,
crate::value::NyashValue::Null
| crate::value::NyashValue::Void
| crate::value::NyashValue::Array(_)
| crate::value::NyashValue::Map(_)
| crate::value::NyashValue::Box(_)
| crate::value::NyashValue::WeakBox(_)
);
if !is_missing {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField internal {}.{} -> {:?}", inst.class_name, fname, nv);
}
if let Some(d) = dst {
// Special-case: NV::Box should surface as VMValue::BoxRef
if let crate::value::NyashValue::Box(ref arc_m) = nv {
if let Ok(guard) = arc_m.lock() {
let cloned: Box<dyn crate::box_trait::NyashBox> = guard.clone_box();
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::from(cloned);
this.regs.insert(d, VMValue::BoxRef(arc));
} else {
this.regs.insert(d, VMValue::Void);
}
} else {
this.regs.insert(d, nv_to_vm(&nv));
}
}
// Trace get
if MirInterpreter::box_trace_enabled() {
let kind = match &nv {
crate::value::NyashValue::Integer(_) => "Integer",
crate::value::NyashValue::Float(_) => "Float",
crate::value::NyashValue::Bool(_) => "Bool",
crate::value::NyashValue::String(_) => "String",
crate::value::NyashValue::Null => "Null",
crate::value::NyashValue::Void => "Void",
crate::value::NyashValue::Array(_) => "Array",
crate::value::NyashValue::Map(_) => "Map",
crate::value::NyashValue::Box(_) => "Box",
crate::value::NyashValue::WeakBox(_) => "WeakBox",
};
this.box_trace_emit_get(&inst.class_name, &fname, kind);
}
return Ok(true);
} else {
// Provide pragmatic defaults for JsonScanner numeric fields
if inst.class_name == "JsonScanner" {
let def = match fname.as_str() {
"position" | "length" => Some(VMValue::Integer(0)),
"line" | "column" => Some(VMValue::Integer(1)),
"text" => Some(VMValue::String(String::new())),
_ => None,
};
if let Some(v) = def {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField default JsonScanner.{} -> {:?}", fname, v);
}
if let Some(d) = dst { this.regs.insert(d, v); }
return Ok(true);
}
}
}
} else {
// fields_ng missing entirely → try JsonScanner defaults next, otherwise fallback to legacy/opfields
if inst.class_name == "JsonScanner" {
let def = match fname.as_str() {
"position" | "length" => Some(VMValue::Integer(0)),
"line" | "column" => Some(VMValue::Integer(1)),
"text" => Some(VMValue::String(String::new())),
_ => None,
};
if let Some(v) = def {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField default(JsonScanner missing) {} -> {:?}", fname, v);
}
if let Some(d) = dst { this.regs.insert(d, v.clone()); }
if MirInterpreter::box_trace_enabled() {
let kind = match &v {
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
VMValue::String(_) => "String",
VMValue::BoxRef(_) => "BoxRef",
VMValue::Void => "Void",
VMValue::Future(_) => "Future",
};
this.box_trace_emit_get(&inst.class_name, &fname, kind);
}
return Ok(true);
}
}
}
// Finally: legacy fields (SharedNyashBox) for complex values
if let Some(shared) = inst.get_field(&fname) {
if let Some(d) = dst { this.regs.insert(d, VMValue::BoxRef(shared.clone())); }
if MirInterpreter::box_trace_enabled() {
this.box_trace_emit_get(&inst.class_name, &fname, "BoxRef");
}
return Ok(true);
}
}
}
let key = this.object_key_for(box_val);
let mut v = this
.obj_fields
.get(&key)
.and_then(|m| m.get(&fname))
.cloned()
.unwrap_or(VMValue::Void);
// Final safety (dev-only, narrow): if legacy path yields Void for well-known
// JsonScanner fields inside JsonScanner.{is_eof,current,advance}, provide
// pragmatic defaults to avoid Void comparisons during bring-up.
if let VMValue::Void = v {
let guard_on = std::env::var("NYASH_VM_SCANNER_DEFAULTS").ok().as_deref() == Some("1");
let fn_ctx = this.cur_fn.as_deref().unwrap_or("");
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField guard_check ctx={} guard_on={} name={}", fn_ctx, guard_on, fname);
}
if guard_on {
let fn_ctx = this.cur_fn.as_deref().unwrap_or("");
let is_scanner_ctx = matches!(
fn_ctx,
"JsonScanner.is_eof/0" | "JsonScanner.current/0" | "JsonScanner.advance/0"
);
if is_scanner_ctx {
// Try class-aware default first
if let Ok(VMValue::BoxRef(bref2)) = this.reg_load(box_val) {
if let Some(inst2) = bref2.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
if inst2.class_name == "JsonScanner" {
let fallback = match fname.as_str() {
"position" | "length" => Some(VMValue::Integer(0)),
"line" | "column" => Some(VMValue::Integer(1)),
"text" => Some(VMValue::String(String::new())),
_ => None,
};
if let Some(val) = fallback {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField final_default {} -> {:?}", fname, val);
}
v = val;
}
}
}
}
// Class nameが取得できなかった場合でも、フィールド名で限定的に適用
if matches!(v, VMValue::Void) {
let fallback2 = match fname.as_str() {
"position" | "length" => Some(VMValue::Integer(0)),
"line" | "column" => Some(VMValue::Integer(1)),
"text" => Some(VMValue::String(String::new())),
_ => None,
};
if let Some(val2) = fallback2 {
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
eprintln!("[vm-trace] getField final_default(class-agnostic) {} -> {:?}", fname, val2);
}
v = val2;
}
}
}
}
}
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
if let VMValue::BoxRef(b) = &v {
eprintln!("[vm-trace] getField legacy {} -> BoxRef({})", fname, b.type_name());
} else {
eprintln!("[vm-trace] getField legacy {} -> {:?}", fname, v);
}
}
if let Some(d) = dst { this.regs.insert(d, v.clone()); }
if MirInterpreter::box_trace_enabled() {
let kind = match &v {
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
VMValue::String(_) => "String",
VMValue::BoxRef(b) => b.type_name(),
VMValue::Void => "Void",
VMValue::Future(_) => "Future",
};
// class name unknown here; use receiver type name if possible
let cls = match this.reg_load(box_val).unwrap_or(VMValue::Void) {
VMValue::BoxRef(b) => {
if let Some(inst) = b.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
inst.class_name.clone()
} else { b.type_name().to_string() }
}
_ => "<unknown>".to_string(),
};
this.box_trace_emit_get(&cls, &fname, kind);
}
Ok(true)
}
"setField" => {
if args.len() != 2 {
return Err(VMError::InvalidInstruction(
"setField expects 2 args".into(),
));
}
let fname = match this.reg_load(args[0])? {
VMValue::String(s) => s,
v => v.to_string(),
};
let valv = this.reg_load(args[1])?;
// Dev trace: JsonToken field set
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
if let VMValue::BoxRef(bref) = this.reg_load(box_val)? {
if let Some(inst) = bref.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
if inst.class_name == "JsonToken" {
eprintln!("[vm-trace] JsonToken.setField name={} vmval={:?}", fname, valv);
}
}
}
}
if MirInterpreter::box_trace_enabled() {
let vkind = match &valv {
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
VMValue::String(_) => "String",
VMValue::BoxRef(b) => b.type_name(),
VMValue::Void => "Void",
VMValue::Future(_) => "Future",
};
let cls = match this.reg_load(box_val).unwrap_or(VMValue::Void) {
VMValue::BoxRef(b) => {
if let Some(inst) = b.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
inst.class_name.clone()
} else { b.type_name().to_string() }
}
_ => "<unknown>".to_string(),
};
this.box_trace_emit_set(&cls, &fname, vkind);
}
// Prefer InstanceBox internal storage
if let VMValue::BoxRef(bref) = this.reg_load(box_val)? {
if let Some(inst) = bref.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
// Primitives → 内部保存
if matches!(valv, VMValue::Integer(_) | VMValue::Float(_) | VMValue::Bool(_) | VMValue::String(_) | VMValue::Void) {
let _ = inst.set_field_ng(fname.clone(), vm_to_nv(&valv));
return Ok(true);
}
// BoxRef のうち、Integer/Float/Bool/String はプリミティブに剥がして内部保存
if let VMValue::BoxRef(bx) = &valv {
if let Some(ib) = bx.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
let _ = inst.set_field_ng(fname.clone(), crate::value::NyashValue::Integer(ib.value));
return Ok(true);
}
if let Some(fb) = bx.as_any().downcast_ref::<crate::boxes::FloatBox>() {
let _ = inst.set_field_ng(fname.clone(), crate::value::NyashValue::Float(fb.value));
return Ok(true);
}
if let Some(bb) = bx.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
let _ = inst.set_field_ng(fname.clone(), crate::value::NyashValue::Bool(bb.value));
return Ok(true);
}
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() {
let _ = inst.set_field_ng(fname.clone(), crate::value::NyashValue::String(sb.value.clone()));
return Ok(true);
}
// For complex Box values (InstanceBox/MapBox/ArrayBox...), store into
// legacy fields to preserve identity across clones/gets.
let _ = inst.set_field(fname.as_str(), std::sync::Arc::clone(bx));
return Ok(true);
}
}
}
let key = this.object_key_for(box_val);
this.obj_fields
.entry(key)
.or_default()
.insert(fname, valv);
Ok(true)
}
_ => Ok(false),
}
}

View File

@ -5,6 +5,7 @@ mod boxes;
mod boxes_array;
mod boxes_string;
mod boxes_map;
mod boxes_object_fields;
mod calls;
mod externals;
mod memory;