Files
hakorune/src/backend/mir_interpreter/handlers/externals.rs
nyash-codex 22b668927e 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)
2025-11-06 23:34:46 +09:00

207 lines
9.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use super::*;
use super::super::utils::*;
use serde_json::{Value as JsonValue, Map as JsonMap};
impl MirInterpreter {
#[inline]
fn ensure_mir_json_version_field(s: &str) -> String {
match serde_json::from_str::<JsonValue>(s) {
Ok(mut v) => {
if let JsonValue::Object(ref mut m) = v {
if !m.contains_key("version") {
m.insert("version".to_string(), JsonValue::from(0));
if let Ok(out) = serde_json::to_string(&v) { return out; }
}
}
s.to_string()
}
Err(_) => s.to_string(),
}
}
pub(super) fn handle_extern_call(
&mut self,
dst: Option<ValueId>,
iface: &str,
method: &str,
args: &[ValueId],
) -> Result<(), VMError> {
match (iface, method) {
("env", "get") => {
if let Some(a0) = args.get(0) {
let key = self.reg_load(*a0)?.to_string();
let val = std::env::var(&key).ok();
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(())
}
("env.console", "log") => {
if let Some(a0) = args.get(0) {
let v = self.reg_load(*a0)?;
// Dev-only: mirror print-trace for extern console.log
if Self::print_trace_enabled() { self.print_trace_emit(&v); }
// Treat VM Void and BoxRef(VoidBox) as JSON null for dev ergonomics
match &v {
VMValue::Void => { println!("null"); self.write_void(dst); return Ok(()); }
VMValue::BoxRef(bx) => {
if bx.as_any().downcast_ref::<crate::box_trait::VoidBox>().is_some() {
println!("null"); self.write_void(dst); return Ok(());
}
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() {
println!("{}", sb.value); self.write_void(dst); return Ok(());
}
}
VMValue::String(s) => { println!("{}", s); self.write_void(dst); return Ok(()); }
_ => {}
}
// Operator Box (Stringify) dev flag gated
if std::env::var("NYASH_OPERATOR_BOX_STRINGIFY").ok().as_deref() == Some("1") {
if let Some(op) = self.functions.get("StringifyOperator.apply/1").cloned() {
let out = self.exec_function_inner(&op, Some(&[v.clone()]))?;
println!("{}", out.to_string());
} else {
println!("{}", v.to_string());
}
} else {
println!("{}", v.to_string());
}
}
self.write_void(dst);
Ok(())
}
("env.future", "new") => {
let fut = crate::boxes::future::NyashFutureBox::new();
if let Some(a0) = args.get(0) {
let v = self.reg_load(*a0)?;
fut.set_result(v.to_nyash_box());
}
self.write_result(dst, VMValue::Future(fut));
Ok(())
}
("env.future", "set") => {
if args.len() >= 2 {
let f = self.reg_load(args[0])?;
let v = self.reg_load(args[1])?;
if let VMValue::Future(fut) = f {
fut.set_result(v.to_nyash_box());
} else {
return Err(VMError::TypeError("env.future.set expects Future".into()));
}
}
self.write_void(dst);
Ok(())
}
("env.future", "await") => {
if let Some(a0) = args.get(0) {
let f = self.reg_load(*a0)?;
match f {
VMValue::Future(fut) => {
let v = fut.get();
self.write_result(dst, VMValue::from_nyash_box(v));
}
_ => {
return Err(VMError::TypeError("await expects Future".into()));
}
}
}
Ok(())
}
("env.runtime", "checkpoint") => {
crate::runtime::global_hooks::safepoint_and_poll();
self.write_void(dst);
Ok(())
}
("env.modules", "set") => {
if args.len() >= 2 {
let k = self.reg_load(args[0])?.to_string();
let v = self.reg_load(args[1])?.to_nyash_box();
crate::runtime::modules_registry::set(k, v);
}
self.write_void(dst);
Ok(())
}
("env.modules", "get") => {
if let Some(a0) = args.get(0) {
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()));
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))?;
self.write_result(dst, ret);
Ok(())
}
("env.mirbuilder", "emit") => {
let ret = self.extern_provider_dispatch("env.mirbuilder.emit", args).unwrap_or(Ok(VMValue::Void))?;
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))?;
self.write_result(dst, ret);
Ok(())
}
("env.codegen", "link_object") => {
// Args in third param (ArrayBox): [obj_path, exe_out?]
// 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(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) {
let v = self.reg_load(*a2)?;
match v {
VMValue::BoxRef(b) => {
if let Some(ab) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
let idx0: Box<dyn crate::box_trait::NyashBox> = Box::new(crate::box_trait::IntegerBox::new(0));
let elem0 = ab.get(idx0).to_string_box().value;
let mut exe: Option<String> = None;
let idx1: Box<dyn crate::box_trait::NyashBox> = Box::new(crate::box_trait::IntegerBox::new(1));
let e1 = ab.get(idx1).to_string_box().value;
if !e1.is_empty() { exe = Some(e1); }
(elem0, exe)
} else {
(b.to_string_box().value, None)
}
}
_ => (v.to_string(), None),
}
} else {
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| self.err_with_context("env.codegen.link_object", &e.to_string()))?;
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) => { self.write_result(dst, v); }
Err(e) => { return Err(e); }
}
return Ok(());
}
return Err(self.err_invalid("hostbridge.extern_invoke unsupported [externals]"));
}
_ => Err(self.err_invalid(format!(
"ExternCall {}.{} not supported",
iface, method
))),
}
}
}