refactor: complete error message unification (Phase 3.5)
Migrate remaining 45 error generation patterns to unified helpers: - calls.rs: 13 sites → 0 - extern_provider.rs: 9 sites → 0 - externals.rs: 5 sites → 0 - boxes_plugin.rs: 5 sites → 0 - boxes.rs: 5 sites → 0 - boxes_string.rs: 4 sites → 0 - boxes_instance.rs: 2 sites → 0 - mod.rs + boxes_array.rs: 2 sites → 0 Error patterns now 100% unified: - All 80 InvalidInstruction sites use helpers (100%) - Consistent error formatting across entire codebase - Single source of truth for error messages Code reduction: - Phase 3.5: 50-70 lines saved - Cumulative (Phase 1+2+3+3.5): 200-267 lines removed (6-8% handlers) - Total patterns unified: 192 (destination 60 + args 52 + errors 80) Benefits: - 100% error message consistency achieved - Easy to modify error formats globally - Foundation for i18n support ready - Improved maintainability and testability Test results: ✓ Build successful, Phase 21.0 smoke tests passing Related: Phase 21.0 refactoring milestone complete Risk: Low (error messages only, behavior preserved)
This commit is contained in:
@ -11,7 +11,7 @@ impl MirInterpreter {
|
||||
) -> Result<(), VMError> {
|
||||
// Provider Lock guard (受け口・既定は挙動不変)
|
||||
if let Err(e) = crate::runtime::provider_lock::guard_before_new_box(box_type) {
|
||||
return Err(VMError::InvalidInstruction(e));
|
||||
return Err(self.err_invalid(e));
|
||||
}
|
||||
let mut converted: Vec<Box<dyn NyashBox>> = Vec::with_capacity(args.len());
|
||||
for vid in args {
|
||||
@ -22,9 +22,7 @@ impl MirInterpreter {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.create_box(box_type, &converted)
|
||||
.map_err(|e| {
|
||||
VMError::InvalidInstruction(format!("NewBox {} failed: {}", box_type, e))
|
||||
})?;
|
||||
.map_err(|e| self.err_with_context(&format!("NewBox {}", box_type), &e.to_string()))?;
|
||||
// Store created instance first so 'me' can be passed to birth
|
||||
let created_vm = VMValue::from_nyash_box(created);
|
||||
self.regs.insert(dst, created_vm.clone());
|
||||
@ -74,10 +72,10 @@ impl MirInterpreter {
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(VMError::InvalidInstruction(format!(
|
||||
"PluginInvoke {}.{} failed: {:?}",
|
||||
p.box_type, method, e
|
||||
)))
|
||||
return Err(self.err_with_context(
|
||||
&format!("PluginInvoke {}.{}", p.box_type, method),
|
||||
&format!("{:?}", e)
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -88,11 +86,7 @@ impl MirInterpreter {
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(format!(
|
||||
"PluginInvoke unsupported on {} for method {}",
|
||||
recv_box.type_name(),
|
||||
method
|
||||
)))
|
||||
Err(self.err_method_not_found(&recv_box.type_name(), method))
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,7 +182,7 @@ impl MirInterpreter {
|
||||
}
|
||||
if user_instance_class.is_some() && !crate::config::env::vm_allow_user_instance_boxcall() {
|
||||
let cls = user_instance_class.unwrap();
|
||||
return Err(VMError::InvalidInstruction(format!(
|
||||
return Err(self.err_invalid(format!(
|
||||
"User Instance BoxCall disallowed in prod: {}.{} (enable builder rewrite)",
|
||||
cls, method
|
||||
)));
|
||||
|
||||
@ -32,7 +32,7 @@ pub(super) fn try_handle_array_box(
|
||||
return Ok(true);
|
||||
}
|
||||
"pop" => {
|
||||
if !args.is_empty() { return Err(VMError::InvalidInstruction("pop expects 0 args".into())); }
|
||||
if !args.is_empty() { return Err(this.err_invalid("pop expects 0 args")); }
|
||||
let ret = ab.pop();
|
||||
this.write_result(dst, VMValue::from_nyash_box(ret));
|
||||
return Ok(true);
|
||||
|
||||
@ -92,7 +92,7 @@ pub(super) fn try_handle_instance_box(
|
||||
// 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()));
|
||||
return Err(this.err_invalid("Dev assert: birth(me==Void) is forbidden"));
|
||||
}
|
||||
}
|
||||
argv.push(recv_vm.clone());
|
||||
@ -127,7 +127,7 @@ pub(super) fn try_handle_instance_box(
|
||||
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()));
|
||||
return Err(this.err_invalid("Dev assert: birth(me==Void) is forbidden"));
|
||||
}
|
||||
}
|
||||
argv.push(recv_vm.clone());
|
||||
|
||||
@ -60,10 +60,10 @@ pub(super) fn invoke_plugin_box(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"BoxCall {}.{} failed: {:?}",
|
||||
p.box_type, method, e
|
||||
))),
|
||||
Err(e) => Err(this.err_with_context(
|
||||
&format!("BoxCall {}.{}", p.box_type, method),
|
||||
&format!("{:?}", e)
|
||||
)),
|
||||
}
|
||||
} else if let Some(string_box) = recv_box
|
||||
.as_any()
|
||||
@ -80,9 +80,7 @@ pub(super) fn invoke_plugin_box(
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"lastIndexOf requires 1 argument".into(),
|
||||
))
|
||||
Err(this.err_invalid("lastIndexOf requires 1 argument"))
|
||||
}
|
||||
}
|
||||
"indexOf" | "find" => {
|
||||
@ -94,15 +92,10 @@ pub(super) fn invoke_plugin_box(
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"indexOf/find requires 1 argument".into(),
|
||||
))
|
||||
Err(this.err_invalid("indexOf/find requires 1 argument"))
|
||||
}
|
||||
}
|
||||
_ => Err(VMError::InvalidInstruction(format!(
|
||||
"BoxCall method {} not supported on StringBox",
|
||||
method
|
||||
))),
|
||||
_ => Err(this.err_method_not_found("StringBox", method)),
|
||||
}
|
||||
} else {
|
||||
// Special-case: minimal runtime fallback for common InstanceBox methods when
|
||||
@ -209,10 +202,6 @@ pub(super) fn invoke_plugin_box(
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Err(VMError::InvalidInstruction(format!(
|
||||
"BoxCall unsupported on {}.{}",
|
||||
recv_box.type_name(),
|
||||
method
|
||||
)))
|
||||
Err(this.err_method_not_found(&recv_box.type_name(), method))
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,8 +72,8 @@ pub(super) fn try_handle_string_box(
|
||||
(n, from.max(0) as usize)
|
||||
}
|
||||
_ => {
|
||||
return Err(VMError::InvalidInstruction(
|
||||
"indexOf expects 1 or 2 args (search [, fromIndex])".into(),
|
||||
return Err(this.err_invalid(
|
||||
"indexOf expects 1 or 2 args (search [, fromIndex])"
|
||||
));
|
||||
}
|
||||
};
|
||||
@ -144,8 +144,8 @@ pub(super) fn try_handle_string_box(
|
||||
(s, e)
|
||||
}
|
||||
_ => {
|
||||
return Err(VMError::InvalidInstruction(
|
||||
"substring expects 1 or 2 args (start [, end])".into(),
|
||||
return Err(this.err_invalid(
|
||||
"substring expects 1 or 2 args (start [, end])"
|
||||
));
|
||||
}
|
||||
};
|
||||
@ -172,7 +172,7 @@ pub(super) fn try_handle_string_box(
|
||||
let s = this.reg_load(args[0])?.to_string();
|
||||
s.chars().next()
|
||||
} else {
|
||||
return Err(VMError::InvalidInstruction("is_digit_char expects 0 or 1 arg".into()));
|
||||
return Err(this.err_invalid("is_digit_char expects 0 or 1 arg"));
|
||||
};
|
||||
let is_digit = ch_opt.map(|c| c.is_ascii_digit()).unwrap_or(false);
|
||||
this.write_result(dst, VMValue::Bool(is_digit));
|
||||
@ -185,7 +185,7 @@ pub(super) fn try_handle_string_box(
|
||||
let s = this.reg_load(args[0])?.to_string();
|
||||
s.chars().next()
|
||||
} else {
|
||||
return Err(VMError::InvalidInstruction("is_hex_digit_char expects 0 or 1 arg".into()));
|
||||
return Err(this.err_invalid("is_hex_digit_char expects 0 or 1 arg"));
|
||||
};
|
||||
let is_hex = ch_opt.map(|c| c.is_ascii_hexdigit()).unwrap_or(false);
|
||||
this.write_result(dst, VMValue::Bool(is_hex));
|
||||
|
||||
@ -424,15 +424,10 @@ impl MirInterpreter {
|
||||
if let Some(s) = first_arg_str {
|
||||
match crate::host_providers::mir_builder::program_json_to_mir_json(&s) {
|
||||
Ok(out) => Ok(VMValue::String(out)),
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"env.mirbuilder.emit: {}",
|
||||
e
|
||||
))),
|
||||
Err(e) => Err(self.err_with_context("env.mirbuilder.emit", &e.to_string())),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"extern_invoke env.mirbuilder.emit expects 1 arg".into(),
|
||||
))
|
||||
Err(self.err_invalid("extern_invoke env.mirbuilder.emit expects 1 arg"))
|
||||
}
|
||||
}
|
||||
("env.codegen", "emit_object") => {
|
||||
@ -445,15 +440,10 @@ impl MirInterpreter {
|
||||
};
|
||||
match crate::host_providers::llvm_codegen::mir_json_to_object(&s, opts) {
|
||||
Ok(p) => Ok(VMValue::String(p.to_string_lossy().into_owned())),
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"env.codegen.emit_object: {}",
|
||||
e
|
||||
))),
|
||||
Err(e) => Err(self.err_with_context("env.codegen.emit_object", &e.to_string())),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"extern_invoke env.codegen.emit_object expects 1 arg".into(),
|
||||
))
|
||||
Err(self.err_invalid("extern_invoke env.codegen.emit_object expects 1 arg"))
|
||||
}
|
||||
}
|
||||
("env.codegen", "link_object") => {
|
||||
@ -611,7 +601,7 @@ impl MirInterpreter {
|
||||
if std::env::var("HAKO_CABI_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[hb:unsupported:calls] {}.{}", name, method);
|
||||
}
|
||||
Err(VMError::InvalidInstruction(format!(
|
||||
Err(self.err_invalid(format!(
|
||||
"hostbridge.extern_invoke unsupported for {}.{} [calls]",
|
||||
name, method
|
||||
)))
|
||||
@ -705,9 +695,7 @@ impl MirInterpreter {
|
||||
let new_str = format!("{}{}", s, arg_val.to_string());
|
||||
Ok(VMValue::String(new_str))
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"concat requires 1 argument".into(),
|
||||
))
|
||||
Err(self.err_invalid("concat requires 1 argument"))
|
||||
}
|
||||
}
|
||||
"replace" => {
|
||||
@ -716,9 +704,7 @@ impl MirInterpreter {
|
||||
let new = self.reg_load(args[1])?.to_string();
|
||||
Ok(VMValue::String(s.replace(&old, &new)))
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"replace requires 2 arguments".into(),
|
||||
))
|
||||
Err(self.err_invalid("replace requires 2 arguments"))
|
||||
}
|
||||
}
|
||||
"indexOf" => {
|
||||
@ -727,9 +713,7 @@ impl MirInterpreter {
|
||||
let idx = s.find(&needle).map(|i| i as i64).unwrap_or(-1);
|
||||
Ok(VMValue::Integer(idx))
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"indexOf requires 1 argument".into(),
|
||||
))
|
||||
Err(self.err_invalid("indexOf requires 1 argument"))
|
||||
}
|
||||
}
|
||||
"lastIndexOf" => {
|
||||
@ -738,9 +722,7 @@ impl MirInterpreter {
|
||||
let idx = s.rfind(&needle).map(|i| i as i64).unwrap_or(-1);
|
||||
Ok(VMValue::Integer(idx))
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"lastIndexOf requires 1 argument".into(),
|
||||
))
|
||||
Err(self.err_invalid("lastIndexOf requires 1 argument"))
|
||||
}
|
||||
}
|
||||
"substring" => {
|
||||
@ -774,9 +756,7 @@ impl MirInterpreter {
|
||||
let result_box = string_box.lastIndexOf(&needle);
|
||||
Ok(VMValue::from_nyash_box(result_box))
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"lastIndexOf requires 1 argument".into(),
|
||||
))
|
||||
Err(self.err_invalid("lastIndexOf requires 1 argument"))
|
||||
}
|
||||
}
|
||||
"indexOf" | "find" => {
|
||||
@ -785,9 +765,7 @@ impl MirInterpreter {
|
||||
let result_box = string_box.find(&needle);
|
||||
Ok(VMValue::from_nyash_box(result_box))
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"indexOf/find requires 1 argument".into(),
|
||||
))
|
||||
Err(self.err_invalid("indexOf/find requires 1 argument"))
|
||||
}
|
||||
}
|
||||
_ => Err(self.err_method_not_found("StringBox", method)),
|
||||
@ -811,17 +789,13 @@ impl MirInterpreter {
|
||||
) {
|
||||
Ok(Some(ret)) => Ok(VMValue::from_nyash_box(ret)),
|
||||
Ok(None) => Ok(VMValue::Void),
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"Plugin method {}.{} failed: {:?}",
|
||||
p.box_type, method, e
|
||||
))),
|
||||
Err(e) => Err(self.err_with_context(
|
||||
&format!("Plugin method {}.{}", p.box_type, method),
|
||||
&format!("{:?}", e)
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(format!(
|
||||
"Method {} not supported on BoxRef({})",
|
||||
method,
|
||||
box_ref.type_name()
|
||||
)))
|
||||
Err(self.err_method_not_found(&box_ref.type_name(), method))
|
||||
}
|
||||
}
|
||||
_ => Err(self.err_with_context("method call", &format!("{} not supported on {:?}", method, receiver))),
|
||||
|
||||
@ -82,7 +82,7 @@ impl MirInterpreter {
|
||||
// Only supported on C-API route; expect 1 or 2 args: obj_path [, exe_out]
|
||||
let obj_path = match args.get(0) {
|
||||
Some(v) => match self.reg_load(*v) { Ok(v) => v.to_string(), Err(e) => return Some(Err(e)) },
|
||||
None => return Some(Err(VMError::InvalidInstruction("env.codegen.link_object expects 1+ args".into()))),
|
||||
None => return Some(Err(self.err_invalid("env.codegen.link_object expects 1+ args"))),
|
||||
};
|
||||
let exe_out = match args.get(1) {
|
||||
Some(v) => Some(match self.reg_load(*v) { Ok(v) => v.to_string(), Err(e) => return Some(Err(e)) }),
|
||||
@ -114,9 +114,7 @@ impl MirInterpreter {
|
||||
eprintln!("[hb:entry:provider] hostbridge.extern_invoke");
|
||||
}
|
||||
if args.len() < 2 {
|
||||
return Some(Err(VMError::InvalidInstruction(
|
||||
"extern_invoke expects at least 2 args".into(),
|
||||
)));
|
||||
return Some(Err(self.err_invalid("extern_invoke expects at least 2 args")));
|
||||
}
|
||||
let name = match self.reg_load(args[0]) { Ok(v) => v.to_string(), Err(e) => return Some(Err(e)) };
|
||||
let method = match self.reg_load(args[1]) { Ok(v) => v.to_string(), Err(e) => return Some(Err(e)) };
|
||||
@ -186,7 +184,7 @@ impl MirInterpreter {
|
||||
_ => (v.to_string(), None),
|
||||
}
|
||||
} else {
|
||||
return Some(Err(VMError::InvalidInstruction("extern_invoke env.codegen.link_object expects args array".into())));
|
||||
return Some(Err(self.err_invalid("extern_invoke env.codegen.link_object expects args array")));
|
||||
};
|
||||
if std::env::var("NYASH_LLVM_USE_CAPI").ok().as_deref() != Some("1") ||
|
||||
std::env::var("HAKO_V1_EXTERN_PROVIDER_C_ABI").ok().as_deref() != Some("1") {
|
||||
@ -204,15 +202,10 @@ impl MirInterpreter {
|
||||
if let Some(s) = first_arg_str {
|
||||
match crate::host_providers::mir_builder::program_json_to_mir_json(&s) {
|
||||
Ok(out) => Ok(VMValue::String(Self::patch_mir_json_version(&out))),
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"env.mirbuilder.emit: {}",
|
||||
e
|
||||
))),
|
||||
Err(e) => Err(self.err_with_context("env.mirbuilder.emit", &e.to_string())),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"extern_invoke env.mirbuilder.emit expects 1 arg".into(),
|
||||
))
|
||||
Err(self.err_invalid("extern_invoke env.mirbuilder.emit expects 1 arg"))
|
||||
}
|
||||
}
|
||||
("env.codegen", "emit_object") => {
|
||||
@ -225,15 +218,10 @@ impl MirInterpreter {
|
||||
};
|
||||
match crate::host_providers::llvm_codegen::mir_json_to_object(&s, opts) {
|
||||
Ok(p) => Ok(VMValue::String(p.to_string_lossy().into_owned())),
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"env.codegen.emit_object: {}",
|
||||
e
|
||||
))),
|
||||
Err(e) => Err(self.err_with_context("env.codegen.emit_object", &e.to_string())),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"extern_invoke env.codegen.emit_object expects 1 arg".into(),
|
||||
))
|
||||
Err(self.err_invalid("extern_invoke env.codegen.emit_object expects 1 arg"))
|
||||
}
|
||||
}
|
||||
("env.codegen", "link_object") => {
|
||||
@ -267,7 +255,7 @@ impl MirInterpreter {
|
||||
}
|
||||
let objs = match obj_s {
|
||||
Some(s) => s,
|
||||
None => return Some(Err(VMError::InvalidInstruction("extern_invoke env.codegen.link_object expects args".into()))),
|
||||
None => return Some(Err(self.err_invalid("extern_invoke env.codegen.link_object expects args"))),
|
||||
};
|
||||
if std::env::var("NYASH_LLVM_USE_CAPI").ok().as_deref() != Some("1") ||
|
||||
std::env::var("HAKO_V1_EXTERN_PROVIDER_C_ABI").ok().as_deref() != Some("1") {
|
||||
@ -285,7 +273,7 @@ impl MirInterpreter {
|
||||
if std::env::var("HAKO_CABI_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[hb:unsupported:provider] {}.{}", name, method);
|
||||
}
|
||||
Err(VMError::InvalidInstruction(format!(
|
||||
Err(self.err_invalid(format!(
|
||||
"hostbridge.extern_invoke unsupported for {}.{} [provider]",
|
||||
name, method
|
||||
)))
|
||||
|
||||
@ -155,7 +155,7 @@ impl MirInterpreter {
|
||||
// Note: This branch is used for ExternCall form; provider toggles must be ON.
|
||||
if std::env::var("NYASH_LLVM_USE_CAPI").ok().as_deref() != Some("1") ||
|
||||
std::env::var("HAKO_V1_EXTERN_PROVIDER_C_ABI").ok().as_deref() != Some("1") {
|
||||
return Err(VMError::InvalidInstruction("env.codegen.link_object: C-API route disabled".into()));
|
||||
return Err(self.err_invalid("env.codegen.link_object: C-API route disabled"));
|
||||
}
|
||||
// Extract array payload
|
||||
let (obj_path, exe_out) = if let Some(a2) = args.get(2) {
|
||||
@ -177,13 +177,13 @@ impl MirInterpreter {
|
||||
_ => (v.to_string(), None),
|
||||
}
|
||||
} else {
|
||||
return Err(VMError::InvalidInstruction("extern_invoke env.codegen.link_object expects args array".into()));
|
||||
return Err(self.err_invalid("extern_invoke env.codegen.link_object expects args array"));
|
||||
};
|
||||
let extra = std::env::var("HAKO_AOT_LDFLAGS").ok();
|
||||
let obj = std::path::PathBuf::from(obj_path);
|
||||
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)))?;
|
||||
.map_err(|e| self.err_with_context("env.codegen.link_object", &e.to_string()))?;
|
||||
self.write_result(dst, VMValue::String(exe.to_string_lossy().into_owned()));
|
||||
Ok(())
|
||||
}
|
||||
@ -195,9 +195,9 @@ impl MirInterpreter {
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
return Err(VMError::InvalidInstruction("hostbridge.extern_invoke unsupported [externals]".into()));
|
||||
return Err(self.err_invalid("hostbridge.extern_invoke unsupported [externals]"));
|
||||
}
|
||||
_ => Err(VMError::InvalidInstruction(format!(
|
||||
_ => Err(self.err_invalid(format!(
|
||||
"ExternCall {}.{} not supported",
|
||||
iface, method
|
||||
))),
|
||||
|
||||
@ -96,7 +96,7 @@ impl MirInterpreter {
|
||||
| MirInstruction::Safepoint
|
||||
| MirInstruction::Nop => {}
|
||||
other => {
|
||||
return Err(VMError::InvalidInstruction(format!(
|
||||
return Err(self.err_invalid(format!(
|
||||
"MIR interp: unimplemented instruction: {:?}",
|
||||
other
|
||||
)))
|
||||
|
||||
Reference in New Issue
Block a user