diff --git a/src/backend/mir_interpreter/handlers/boxes_array.rs b/src/backend/mir_interpreter/handlers/boxes_array.rs index f75a4d1d..41dcee9b 100644 --- a/src/backend/mir_interpreter/handlers/boxes_array.rs +++ b/src/backend/mir_interpreter/handlers/boxes_array.rs @@ -25,7 +25,7 @@ pub(super) fn try_handle_array_box( return Ok(true); } "push" => { - if args.len() != 1 { return Err(VMError::InvalidInstruction("push expects 1 arg".into())); } + this.validate_args_exact("push", args, 1)?; let val = this.reg_load(args[0])?.to_nyash_box(); let _ = ab.push(val); this.write_void(dst); @@ -43,14 +43,14 @@ pub(super) fn try_handle_array_box( return Ok(true); } "get" => { - if args.len() != 1 { return Err(VMError::InvalidInstruction("get expects 1 arg".into())); } + this.validate_args_exact("get", args, 1)?; let idx = this.reg_load(args[0])?.to_nyash_box(); let ret = ab.get(idx); this.write_result(dst, VMValue::from_nyash_box(ret)); return Ok(true); } "set" => { - if args.len() != 2 { return Err(VMError::InvalidInstruction("set expects 2 args".into())); } + this.validate_args_exact("set", args, 2)?; let idx = this.reg_load(args[0])?.to_nyash_box(); let val = this.reg_load(args[1])?.to_nyash_box(); let _ = ab.set(idx, val); diff --git a/src/backend/mir_interpreter/handlers/boxes_map.rs b/src/backend/mir_interpreter/handlers/boxes_map.rs index 1f3c7a20..c96f1893 100644 --- a/src/backend/mir_interpreter/handlers/boxes_map.rs +++ b/src/backend/mir_interpreter/handlers/boxes_map.rs @@ -26,48 +26,36 @@ pub(super) fn try_handle_map_box( } // Field bridge: treat getField/setField as get/set with string key "getField" => { - if args.len() != 1 { - return Err(VMError::InvalidInstruction( - "MapBox.getField expects 1 arg".into(), - )); - } + this.validate_args_exact("MapBox.getField", args, 1)?; let k_vm = this.reg_load(args[0])?; // Field access expects a String key; otherwise return a stable tag. if !matches!(k_vm, VMValue::String(_)) { - if let Some(d) = dst { this.regs.insert(d, VMValue::String("[map/bad-key] field name must be string".to_string())); } + this.write_result(dst, VMValue::String("[map/bad-key] field name must be string".to_string())); return Ok(true); } let k = k_vm.to_nyash_box(); let ret = mb.get(k); - if let Some(d) = dst { - this.regs.insert(d, VMValue::from_nyash_box(ret)); - } + this.write_result(dst, VMValue::from_nyash_box(ret)); return Ok(true); } "setField" => { - if args.len() != 2 { - return Err(VMError::InvalidInstruction( - "MapBox.setField expects 2 args".into(), - )); - } + this.validate_args_exact("MapBox.setField", args, 2)?; let k_vm = this.reg_load(args[0])?; if !matches!(k_vm, VMValue::String(_)) { - if let Some(d) = dst { this.regs.insert(d, VMValue::String("[map/bad-key] field name must be string".to_string())); } + this.write_result(dst, VMValue::String("[map/bad-key] field name must be string".to_string())); return Ok(true); } let k = k_vm.to_nyash_box(); let v = this.reg_load(args[1])?.to_nyash_box(); let ret = mb.set(k, v); - if let Some(d) = dst { - this.regs.insert(d, VMValue::from_nyash_box(ret)); - } + this.write_result(dst, VMValue::from_nyash_box(ret)); return Ok(true); } "set" => { - if args.len() != 2 { return Err(VMError::InvalidInstruction("MapBox.set expects 2 args".into())); } + this.validate_args_exact("MapBox.set", args, 2)?; let k_vm = this.reg_load(args[0])?; if !matches!(k_vm, VMValue::String(_)) { - if let Some(d) = dst { this.regs.insert(d, VMValue::String("[map/bad-key] key must be string".to_string())); } + this.write_result(dst, VMValue::String("[map/bad-key] key must be string".to_string())); return Ok(true); } let k = k_vm.to_nyash_box(); @@ -77,10 +65,10 @@ pub(super) fn try_handle_map_box( return Ok(true); } "get" => { - if args.len() != 1 { return Err(VMError::InvalidInstruction("MapBox.get expects 1 arg".into())); } + this.validate_args_exact("MapBox.get", args, 1)?; let k_vm = this.reg_load(args[0])?; if !matches!(k_vm, VMValue::String(_)) { - if let Some(d) = dst { this.regs.insert(d, VMValue::String("[map/bad-key] key must be string".to_string())); } + this.write_result(dst, VMValue::String("[map/bad-key] key must be string".to_string())); return Ok(true); } let k = k_vm.to_nyash_box(); @@ -89,17 +77,17 @@ pub(super) fn try_handle_map_box( return Ok(true); } "has" => { - if args.len() != 1 { return Err(VMError::InvalidInstruction("MapBox.has expects 1 arg".into())); } + this.validate_args_exact("MapBox.has", args, 1)?; let k = this.reg_load(args[0])?.to_nyash_box(); let ret = mb.has(k); this.write_result(dst, VMValue::from_nyash_box(ret)); return Ok(true); } "delete" => { - if args.len() != 1 { return Err(VMError::InvalidInstruction("MapBox.delete expects 1 arg".into())); } + this.validate_args_exact("MapBox.delete", args, 1)?; let k_vm = this.reg_load(args[0])?; if !matches!(k_vm, VMValue::String(_)) { - if let Some(d) = dst { this.regs.insert(d, VMValue::String("[map/bad-key] key must be string".to_string())); } + this.write_result(dst, VMValue::String("[map/bad-key] key must be string".to_string())); return Ok(true); } let k = k_vm.to_nyash_box(); diff --git a/src/backend/mir_interpreter/handlers/boxes_object_fields.rs b/src/backend/mir_interpreter/handlers/boxes_object_fields.rs index 3aaea0b8..88ef2b42 100644 --- a/src/backend/mir_interpreter/handlers/boxes_object_fields.rs +++ b/src/backend/mir_interpreter/handlers/boxes_object_fields.rs @@ -38,9 +38,7 @@ pub(super) fn try_handle_object_fields( match method { "getField" => { - if args.len() != 1 { - return Err(VMError::InvalidInstruction("getField expects 1 arg".into())); - } + this.validate_args_exact("getField", args, 1)?; // MapBox special-case: bridge to MapBox.get, with string-only key if let Ok(VMValue::BoxRef(bref)) = this.reg_load(box_val) { if bref.as_any().downcast_ref::().is_some() { @@ -296,11 +294,7 @@ pub(super) fn try_handle_object_fields( Ok(true) } "setField" => { - if args.len() != 2 { - return Err(VMError::InvalidInstruction( - "setField expects 2 args".into(), - )); - } + this.validate_args_exact("setField", args, 2)?; // MapBox special-case: bridge to MapBox.set, with string-only key if let Ok(VMValue::BoxRef(bref)) = this.reg_load(box_val) { if bref.as_any().downcast_ref::().is_some() { diff --git a/src/backend/mir_interpreter/handlers/boxes_string.rs b/src/backend/mir_interpreter/handlers/boxes_string.rs index 0cba699c..67e7bd70 100644 --- a/src/backend/mir_interpreter/handlers/boxes_string.rs +++ b/src/backend/mir_interpreter/handlers/boxes_string.rs @@ -36,9 +36,7 @@ pub(super) fn try_handle_string_box( } "replace" => { // replace(old, new) -> string with first occurrence replaced (Rust replace = all; match Core minimal - if args.len() != 2 { - return Err(VMError::InvalidInstruction("replace expects 2 args".into())); - } + this.validate_args_exact("replace", args, 2)?; let old_s = this.reg_load(args[0])?.to_string(); let new_s = this.reg_load(args[1])?.to_string(); // Core policy: replace only the first occurrence @@ -51,9 +49,7 @@ pub(super) fn try_handle_string_box( } else { sb_norm.value.clone() }; - if let Some(d) = dst { - this.regs.insert(d, VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new(out)))); - } + this.write_result(dst, VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new(out)))); return Ok(true); } "trim" => { @@ -99,9 +95,7 @@ pub(super) fn try_handle_string_box( "contains" => { // contains(search) -> boolean (true if found, false otherwise) // Implemented as indexOf(search) >= 0 - if args.len() != 1 { - return Err(VMError::InvalidInstruction("contains expects 1 arg".into())); - } + this.validate_args_exact("contains", args, 1)?; let needle = this.reg_load(args[0])?.to_string(); let found = sb_norm.value.contains(&needle); this.write_result(dst, VMValue::Bool(found)); @@ -109,9 +103,7 @@ pub(super) fn try_handle_string_box( } "lastIndexOf" => { // lastIndexOf(substr) -> last index or -1 - if args.len() != 1 { - return Err(VMError::InvalidInstruction("lastIndexOf expects 1 arg".into())); - } + this.validate_args_exact("lastIndexOf", args, 1)?; let needle = this.reg_load(args[0])?.to_string(); let idx = sb_norm.value.rfind(&needle).map(|i| i as i64).unwrap_or(-1); this.write_result(dst, VMValue::Integer(idx)); @@ -133,9 +125,7 @@ pub(super) fn try_handle_string_box( } } quoted.push('"'); - if let Some(d) = dst { - this.regs.insert(d, VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new(quoted)))); - } + this.write_result(dst, VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new(quoted)))); return Ok(true); } "substring" => { @@ -164,16 +154,14 @@ pub(super) fn try_handle_string_box( let end = e_idx.max(start as i64).min(len) as usize; let chars: Vec = sb_norm.value.chars().collect(); let sub: String = chars[start..end].iter().collect(); - if let Some(d) = dst { this.regs.insert(d, VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new(sub)))) ; } + this.write_result(dst, VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new(sub)))); return Ok(true); } "concat" => { - if args.len() != 1 { - return Err(VMError::InvalidInstruction("concat expects 1 arg".into())); - } + this.validate_args_exact("concat", args, 1)?; let rhs = this.reg_load(args[0])?; let new_s = format!("{}{}", sb_norm.value, rhs.to_string()); - if let Some(d) = dst { this.regs.insert(d, VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new(new_s)))) ; } + this.write_result(dst, VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new(new_s)))); return Ok(true); } "is_digit_char" => { diff --git a/src/backend/mir_interpreter/handlers/externals.rs b/src/backend/mir_interpreter/handlers/externals.rs index 484d4e38..a80e340e 100644 --- a/src/backend/mir_interpreter/handlers/externals.rs +++ b/src/backend/mir_interpreter/handlers/externals.rs @@ -30,14 +30,13 @@ impl MirInterpreter { if let Some(a0) = args.get(0) { let key = self.reg_load(*a0)?.to_string(); let val = std::env::var(&key).ok(); - if let Some(d) = dst { - if let Some(s) = val { - self.regs.insert(d, VMValue::String(s)); - } else { - // Represent missing env as null-equivalent (Void) - self.regs.insert(d, VMValue::Void); - } - } + let result = if let Some(s) = val { + VMValue::String(s) + } else { + // Represent missing env as null-equivalent (Void) + VMValue::Void + }; + self.write_result(dst, result); } Ok(()) } @@ -81,9 +80,7 @@ impl MirInterpreter { let v = self.reg_load(*a0)?; fut.set_result(v.to_nyash_box()); } - if let Some(d) = dst { - self.regs.insert(d, VMValue::Future(fut)); - } + self.write_result(dst, VMValue::Future(fut)); Ok(()) } ("env.future", "set") => { @@ -96,9 +93,7 @@ impl MirInterpreter { return Err(VMError::TypeError("env.future.set expects Future".into())); } } - if let Some(d) = dst { - self.regs.insert(d, VMValue::Void); - } + self.write_void(dst); Ok(()) } ("env.future", "await") => { @@ -107,9 +102,7 @@ impl MirInterpreter { match f { VMValue::Future(fut) => { let v = fut.get(); - if let Some(d) = dst { - self.regs.insert(d, VMValue::from_nyash_box(v)); - } + self.write_result(dst, VMValue::from_nyash_box(v)); } _ => { return Err(VMError::TypeError("await expects Future".into())); @@ -120,9 +113,7 @@ impl MirInterpreter { } ("env.runtime", "checkpoint") => { crate::runtime::global_hooks::safepoint_and_poll(); - if let Some(d) = dst { - self.regs.insert(d, VMValue::Void); - } + self.write_void(dst); Ok(()) } ("env.modules", "set") => { @@ -131,9 +122,7 @@ impl MirInterpreter { let v = self.reg_load(args[1])?.to_nyash_box(); crate::runtime::modules_registry::set(k, v); } - if let Some(d) = dst { - self.regs.insert(d, VMValue::Void); - } + self.write_void(dst); Ok(()) } ("env.modules", "get") => { @@ -141,26 +130,24 @@ impl MirInterpreter { let k = self.reg_load(*a0)?.to_string(); let vb = crate::runtime::modules_registry::get(&k) .unwrap_or_else(|| Box::new(crate::box_trait::VoidBox::new())); - if let Some(d) = dst { - self.regs.insert(d, VMValue::from_nyash_box(vb)); - } + self.write_result(dst, VMValue::from_nyash_box(vb)); } Ok(()) } ("env", "get") => { // Delegate to provider let ret = self.extern_provider_dispatch("env.get", args).unwrap_or(Ok(VMValue::Void))?; - if let Some(d) = dst { self.regs.insert(d, ret); } + self.write_result(dst, ret); Ok(()) } ("env.mirbuilder", "emit") => { let ret = self.extern_provider_dispatch("env.mirbuilder.emit", args).unwrap_or(Ok(VMValue::Void))?; - if let Some(d) = dst { self.regs.insert(d, ret); } + self.write_result(dst, ret); Ok(()) } ("env.codegen", "emit_object") => { let ret = self.extern_provider_dispatch("env.codegen.emit_object", args).unwrap_or(Ok(VMValue::Void))?; - if let Some(d) = dst { self.regs.insert(d, ret); } + self.write_result(dst, ret); Ok(()) } ("env.codegen", "link_object") => { @@ -197,13 +184,13 @@ impl MirInterpreter { let exe = exe_out.map(std::path::PathBuf::from).unwrap_or_else(|| std::env::temp_dir().join("hako_link_out.exe")); crate::host_providers::llvm_codegen::link_object_capi(&obj, &exe, extra.as_deref()) .map_err(|e| VMError::InvalidInstruction(format!("env.codegen.link_object: {}", e)))?; - if let Some(d) = dst { self.regs.insert(d, VMValue::String(exe.to_string_lossy().into_owned())); } + self.write_result(dst, VMValue::String(exe.to_string_lossy().into_owned())); Ok(()) } ("hostbridge", "extern_invoke") => { if let Some(res) = self.extern_provider_dispatch("hostbridge.extern_invoke", args) { match res { - Ok(v) => { if let Some(d) = dst { self.regs.insert(d, v); } } + Ok(v) => { self.write_result(dst, v); } Err(e) => { return Err(e); } } return Ok(());