diff --git a/crates/nyash-llvm-compiler/src/main.rs b/crates/nyash-llvm-compiler/src/main.rs index 4059bbd3..d2d324f2 100644 --- a/crates/nyash-llvm-compiler/src/main.rs +++ b/crates/nyash-llvm-compiler/src/main.rs @@ -72,7 +72,12 @@ fn main() -> Result<()> { run_harness_dummy(&harness_path, &obj_path) .with_context(|| "failed to run harness in dummy mode")?; if emit_exe { - link_executable(&obj_path, &args.out, args.nyrt.as_ref(), args.libs.as_deref())?; + link_executable( + &obj_path, + &args.out, + args.nyrt.as_ref(), + args.libs.as_deref(), + )?; println!("[ny-llvmc] executable written: {}", args.out.display()); } else { println!("[ny-llvmc] dummy object written: {}", obj_path.display()); @@ -119,7 +124,12 @@ fn main() -> Result<()> { ) })?; if emit_exe { - link_executable(&obj_path, &args.out, args.nyrt.as_ref(), args.libs.as_deref())?; + link_executable( + &obj_path, + &args.out, + args.nyrt.as_ref(), + args.libs.as_deref(), + )?; println!("[ny-llvmc] executable written: {}", args.out.display()); } else { println!("[ny-llvmc] object written: {}", obj_path.display()); @@ -170,7 +180,12 @@ fn ensure_python() -> Result<()> { } } -fn link_executable(obj: &Path, out_exe: &Path, nyrt_dir_opt: Option<&PathBuf>, extra_libs: Option<&str>) -> Result<()> { +fn link_executable( + obj: &Path, + out_exe: &Path, + nyrt_dir_opt: Option<&PathBuf>, + extra_libs: Option<&str>, +) -> Result<()> { // Resolve nyRT static lib let nyrt_dir = if let Some(dir) = nyrt_dir_opt { dir.clone() @@ -178,21 +193,39 @@ fn link_executable(obj: &Path, out_exe: &Path, nyrt_dir_opt: Option<&PathBuf>, e // try target/release then crates/nyash_kernel/target/release let a = PathBuf::from("target/release"); let b = PathBuf::from("crates/nyash_kernel/target/release"); - if a.join("libnyash_kernel.a").exists() { a } else { b } + if a.join("libnyash_kernel.a").exists() { + a + } else { + b + } }; let libnyrt = nyrt_dir.join("libnyash_kernel.a"); if !libnyrt.exists() { - bail!("libnyash_kernel.a not found in {} (use --nyrt to specify)", nyrt_dir.display()); + bail!( + "libnyash_kernel.a not found in {} (use --nyrt to specify)", + nyrt_dir.display() + ); } // Choose a C linker - let linker = ["cc", "clang", "gcc"].into_iter().find(|c| Command::new(c).arg("--version").output().map(|o| o.status.success()).unwrap_or(false)).unwrap_or("cc"); + let linker = ["cc", "clang", "gcc"] + .into_iter() + .find(|c| { + Command::new(c) + .arg("--version") + .output() + .map(|o| o.status.success()) + .unwrap_or(false) + }) + .unwrap_or("cc"); let mut cmd = Command::new(linker); cmd.arg("-o").arg(out_exe); cmd.arg(obj); // Whole-archive libnyash_kernel to ensure all objects are linked - cmd.arg("-Wl,--whole-archive").arg(&libnyrt).arg("-Wl,--no-whole-archive"); + cmd.arg("-Wl,--whole-archive") + .arg(&libnyrt) + .arg("-Wl,--no-whole-archive"); // Common libs on Linux cmd.arg("-ldl").arg("-lpthread").arg("-lm"); if let Some(extras) = extra_libs { diff --git a/plugins/nyash-counter-plugin/src/lib.rs b/plugins/nyash-counter-plugin/src/lib.rs index 1814dbac..813d60f8 100644 --- a/plugins/nyash-counter-plugin/src/lib.rs +++ b/plugins/nyash-counter-plugin/src/lib.rs @@ -143,7 +143,9 @@ unsafe impl Sync for NyashTypeBoxFfi {} extern "C" fn counter_resolve(name: *const std::os::raw::c_char) -> u32 { unsafe { - if name.is_null() { return 0; } + if name.is_null() { + return 0; + } let s = std::ffi::CStr::from_ptr(name).to_string_lossy(); match s.as_ref() { "birth" => METHOD_BIRTH, @@ -167,34 +169,53 @@ extern "C" fn counter_invoke( match method_id { METHOD_BIRTH => { // Return new instance handle (u32 id) as raw 4 bytes (not TLV) - if result_len.is_null() { return NYB_E_INVALID_ARGS; } - if preflight(result, result_len, 4) { return NYB_E_SHORT_BUFFER; } + if result_len.is_null() { + return NYB_E_INVALID_ARGS; + } + if preflight(result, result_len, 4) { + return NYB_E_SHORT_BUFFER; + } let id = INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed); if let Ok(mut map) = INSTANCES.lock() { map.insert(id, CounterInstance { count: 0 }); - } else { return NYB_E_PLUGIN_ERROR; } + } else { + return NYB_E_PLUGIN_ERROR; + } let bytes = id.to_le_bytes(); std::ptr::copy_nonoverlapping(bytes.as_ptr(), result, 4); *result_len = 4; NYB_SUCCESS } METHOD_FINI => { - if let Ok(mut map) = INSTANCES.lock() { map.remove(&instance_id); NYB_SUCCESS } else { NYB_E_PLUGIN_ERROR } + if let Ok(mut map) = INSTANCES.lock() { + map.remove(&instance_id); + NYB_SUCCESS + } else { + NYB_E_PLUGIN_ERROR + } } METHOD_INC => { if let Ok(mut map) = INSTANCES.lock() { if let Some(inst) = map.get_mut(&instance_id) { inst.count += 1; return write_tlv_i32(inst.count, result, result_len); - } else { return NYB_E_INVALID_HANDLE; } - } else { return NYB_E_PLUGIN_ERROR; } + } else { + return NYB_E_INVALID_HANDLE; + } + } else { + return NYB_E_PLUGIN_ERROR; + } } METHOD_GET => { if let Ok(map) = INSTANCES.lock() { if let Some(inst) = map.get(&instance_id) { return write_tlv_i32(inst.count, result, result_len); - } else { return NYB_E_INVALID_HANDLE; } - } else { return NYB_E_PLUGIN_ERROR; } + } else { + return NYB_E_INVALID_HANDLE; + } + } else { + return NYB_E_PLUGIN_ERROR; + } } _ => NYB_E_INVALID_METHOD, } diff --git a/plugins/nyash-fixture-plugin/src/lib.rs b/plugins/nyash-fixture-plugin/src/lib.rs index 86c40d7d..30c48324 100644 --- a/plugins/nyash-fixture-plugin/src/lib.rs +++ b/plugins/nyash-fixture-plugin/src/lib.rs @@ -2,7 +2,10 @@ use once_cell::sync::Lazy; use std::collections::HashMap; -use std::sync::{atomic::{AtomicU32, Ordering}, Mutex}; +use std::sync::{ + atomic::{AtomicU32, Ordering}, + Mutex, +}; // ===== Error Codes (BID-1 alignment) ===== const NYB_SUCCESS: i32 = 0; @@ -42,7 +45,9 @@ pub extern "C" fn nyash_plugin_invoke( result: *mut u8, result_len: *mut usize, ) -> i32 { - if type_id != TYPE_ID_FIXTURE { return NYB_E_INVALID_TYPE; } + if type_id != TYPE_ID_FIXTURE { + return NYB_E_INVALID_TYPE; + } unsafe { dispatch(method_id, instance_id, args, args_len, result, result_len) } } @@ -72,7 +77,9 @@ unsafe impl Sync for NyashTypeBoxFfi {} extern "C" fn fixture_resolve(name: *const std::os::raw::c_char) -> u32 { unsafe { - if name.is_null() { return 0; } + if name.is_null() { + return 0; + } let s = std::ffi::CStr::from_ptr(name).to_string_lossy(); match s.as_ref() { "birth" => METHOD_BIRTH, @@ -125,12 +132,18 @@ unsafe fn dispatch( } unsafe fn birth(result: *mut u8, result_len: *mut usize) -> i32 { - if result_len.is_null() { return NYB_E_INVALID_ARGS; } - if preflight(result, result_len, 4) { return NYB_E_SHORT_BUFFER; } + if result_len.is_null() { + return NYB_E_INVALID_ARGS; + } + if preflight(result, result_len, 4) { + return NYB_E_SHORT_BUFFER; + } let id = INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed); if let Ok(mut map) = INSTANCES.lock() { map.insert(id, FixtureInstance { alive: true }); - } else { return NYB_E_PLUGIN_ERROR; } + } else { + return NYB_E_PLUGIN_ERROR; + } let bytes = id.to_le_bytes(); std::ptr::copy_nonoverlapping(bytes.as_ptr(), result, 4); *result_len = 4; @@ -141,33 +154,43 @@ unsafe fn fini(instance_id: u32) -> i32 { if let Ok(mut map) = INSTANCES.lock() { map.remove(&instance_id); NYB_SUCCESS - } else { NYB_E_PLUGIN_ERROR } + } else { + NYB_E_PLUGIN_ERROR + } } -unsafe fn echo( - args: *const u8, - args_len: usize, - result: *mut u8, - result_len: *mut usize, -) -> i32 { +unsafe fn echo(args: *const u8, args_len: usize, result: *mut u8, result_len: *mut usize) -> i32 { // Expect TLV with 1 argument: tag=6 (String) - if args.is_null() || args_len < 4 { return NYB_E_INVALID_ARGS; } + if args.is_null() || args_len < 4 { + return NYB_E_INVALID_ARGS; + } let slice = std::slice::from_raw_parts(args, args_len); // Minimal TLV parse: skip header (ver/argc) and verify first entry is String - if slice.len() < 8 { return NYB_E_INVALID_ARGS; } + if slice.len() < 8 { + return NYB_E_INVALID_ARGS; + } if slice[0] != 1 || slice[1] != 0 { /* ver=1 little endian */ } // position 4.. is first entry; [tag, rsv, sz_lo, sz_hi, payload...] let tag = slice[4]; - if tag != 6 { return NYB_E_INVALID_ARGS; } + if tag != 6 { + return NYB_E_INVALID_ARGS; + } let sz = u16::from_le_bytes([slice[6], slice[7]]) as usize; - if 8 + sz > slice.len() { return NYB_E_INVALID_ARGS; } + if 8 + sz > slice.len() { + return NYB_E_INVALID_ARGS; + } let payload = &slice[8..8 + sz]; - let s = match std::str::from_utf8(payload) { Ok(t) => t, Err(_) => return NYB_E_INVALID_ARGS }; + let s = match std::str::from_utf8(payload) { + Ok(t) => t, + Err(_) => return NYB_E_INVALID_ARGS, + }; write_tlv_str(s, result, result_len) } fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 { - if result_len.is_null() { return NYB_E_INVALID_ARGS; } + if result_len.is_null() { + return NYB_E_INVALID_ARGS; + } let needed = 4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::(); let mut buf: Vec = Vec::with_capacity(needed); buf.extend_from_slice(&1u16.to_le_bytes()); // ver @@ -195,7 +218,9 @@ fn write_tlv_str(s: &str, result: *mut u8, result_len: *mut usize) -> i32 { fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool { unsafe { - if result_len.is_null() { return false; } + if result_len.is_null() { + return false; + } if result.is_null() || *result_len < needed { *result_len = needed; return true; @@ -203,4 +228,3 @@ fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool { } false } - diff --git a/plugins/nyash-json-plugin/src/lib.rs b/plugins/nyash-json-plugin/src/lib.rs index 33df38ba..05589556 100644 --- a/plugins/nyash-json-plugin/src/lib.rs +++ b/plugins/nyash-json-plugin/src/lib.rs @@ -51,8 +51,8 @@ enum NodeRep { } struct DocInst { - root: Option>, // Serde provider - doc_ptr: Option, // Yyjson provider (opaque pointer value) + root: Option>, // Serde provider + doc_ptr: Option, // Yyjson provider (opaque pointer value) last_err: Option, } static DOCS: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); @@ -150,7 +150,14 @@ extern "C" fn jsondoc_invoke_id( JD_BIRTH => { let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); if let Ok(mut m) = DOCS.lock() { - m.insert(id, DocInst { root: None, doc_ptr: None, last_err: None }); + m.insert( + id, + DocInst { + root: None, + doc_ptr: None, + last_err: None, + }, + ); } else { return E_PLUGIN; } @@ -166,19 +173,32 @@ extern "C" fn jsondoc_invoke_id( match provider_kind() { ProviderKind::Serde => { match provider_parse(&text) { - Ok(v) => { doc.root = Some(Arc::new(v)); doc.doc_ptr = None; doc.last_err = None; } - Err(e) => { doc.root = None; doc.doc_ptr = None; doc.last_err = Some(e.to_string()); } + Ok(v) => { + doc.root = Some(Arc::new(v)); + doc.doc_ptr = None; + doc.last_err = None; + } + Err(e) => { + doc.root = None; + doc.doc_ptr = None; + doc.last_err = Some(e.to_string()); + } } return write_tlv_void(result, result_len); } ProviderKind::Yyjson => { let c = CString::new(text.as_bytes()).unwrap_or_default(); let mut ec: i32 = -1; - let p = nyjson_parse_doc(c.as_ptr(), text.len(), &mut ec as *mut i32); + let p = + nyjson_parse_doc(c.as_ptr(), text.len(), &mut ec as *mut i32); if p.is_null() { - doc.root = None; doc.doc_ptr = None; doc.last_err = Some(format!("E{}", ec)); + doc.root = None; + doc.doc_ptr = None; + doc.last_err = Some(format!("E{}", ec)); } else { - doc.root = None; doc.doc_ptr = Some(p as usize); doc.last_err = None; + doc.root = None; + doc.doc_ptr = Some(p as usize); + doc.last_err = None; } return write_tlv_void(result, result_len); } @@ -197,8 +217,15 @@ extern "C" fn jsondoc_invoke_id( ProviderKind::Serde => { if let Some(root_arc) = doc.root.as_ref().map(|r| Arc::clone(r)) { let node_id = NEXT_ID.fetch_add(1, Ordering::Relaxed); - if let Ok(mut nn) = NODES.lock() { nn.insert(node_id, NodeRep::Serde(root_arc)); } - return write_tlv_handle(T_JSON_NODE, node_id, result, result_len); + if let Ok(mut nn) = NODES.lock() { + nn.insert(node_id, NodeRep::Serde(root_arc)); + } + return write_tlv_handle( + T_JSON_NODE, + node_id, + result, + result_len, + ); } return E_PLUGIN; } @@ -206,8 +233,21 @@ extern "C" fn jsondoc_invoke_id( if let Some(dp) = doc.doc_ptr { let vp = nyjson_doc_root(dp as *mut c_void); let node_id = NEXT_ID.fetch_add(1, Ordering::Relaxed); - if let Ok(mut nn) = NODES.lock() { nn.insert(node_id, NodeRep::Yy { doc_id: instance_id, ptr: vp as usize }); } - return write_tlv_handle(T_JSON_NODE, node_id, result, result_len); + if let Ok(mut nn) = NODES.lock() { + nn.insert( + node_id, + NodeRep::Yy { + doc_id: instance_id, + ptr: vp as usize, + }, + ); + } + return write_tlv_handle( + T_JSON_NODE, + node_id, + result, + result_len, + ); } return E_PLUGIN; } @@ -231,7 +271,9 @@ extern "C" fn jsondoc_invoke_id( JD_FINI => { if let Ok(mut m) = DOCS.lock() { if let Some(mut di) = m.remove(&instance_id) { - if let Some(dp) = di.doc_ptr.take() { nyjson_doc_free(dp as *mut c_void); } + if let Some(dp) = di.doc_ptr.take() { + nyjson_doc_free(dp as *mut c_void); + } } } return write_tlv_void(result, result_len); @@ -281,146 +323,330 @@ extern "C" fn jsonnode_invoke_id( ) -> i32 { unsafe { let node_rep = match NODES.lock() { - Ok(m) => match m.get(&instance_id) { Some(v) => v.clone(), None => return E_HANDLE }, + Ok(m) => match m.get(&instance_id) { + Some(v) => v.clone(), + None => return E_HANDLE, + }, Err(_) => return E_PLUGIN, }; match method_id { JN_BIRTH => { let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); - if let Ok(mut m) = NODES.lock() { m.insert(id, NodeRep::Serde(Arc::new(Value::Null))); } else { return E_PLUGIN; } + if let Ok(mut m) = NODES.lock() { + m.insert(id, NodeRep::Serde(Arc::new(Value::Null))); + } else { + return E_PLUGIN; + } return write_u32(id, result, result_len); } - JN_KIND => { - match provider_kind() { - ProviderKind::Serde => { - let k = match node_rep { NodeRep::Serde(ref a) => match &**a { + JN_KIND => match provider_kind() { + ProviderKind::Serde => { + let k = match node_rep { + NodeRep::Serde(ref a) => match &**a { Value::Null => "null", Value::Bool(_) => "bool", - Value::Number(n) => { if n.is_i64() { "int" } else { "real" } }, + Value::Number(n) => { + if n.is_i64() { + "int" + } else { + "real" + } + } Value::String(_) => "string", Value::Array(_) => "array", Value::Object(_) => "object", - }, _ => "null"}; - write_tlv_string(k, result, result_len) - } - ProviderKind::Yyjson => { - let v = if let NodeRep::Yy { ptr, .. } = node_rep { ptr as *mut c_void } else { std::ptr::null_mut() }; - let k = if v.is_null() { "null" } - else if nyjson_is_obj(v) != 0 { "object" } - else if nyjson_is_arr(v) != 0 { "array" } - else if nyjson_is_str(v) != 0 { "string" } - else if nyjson_is_int(v) != 0 { "int" } - else if nyjson_is_real(v) != 0 { "real" } - else if nyjson_is_bool(v) != 0 { "bool" } - else { "null" }; - write_tlv_string(k, result, result_len) - } + }, + _ => "null", + }; + write_tlv_string(k, result, result_len) } - } + ProviderKind::Yyjson => { + let v = if let NodeRep::Yy { ptr, .. } = node_rep { + ptr as *mut c_void + } else { + std::ptr::null_mut() + }; + let k = if v.is_null() { + "null" + } else if nyjson_is_obj(v) != 0 { + "object" + } else if nyjson_is_arr(v) != 0 { + "array" + } else if nyjson_is_str(v) != 0 { + "string" + } else if nyjson_is_int(v) != 0 { + "int" + } else if nyjson_is_real(v) != 0 { + "real" + } else if nyjson_is_bool(v) != 0 { + "bool" + } else { + "null" + }; + write_tlv_string(k, result, result_len) + } + }, JN_GET => { - let key = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS }; + let key = match read_arg_string(args, args_len, 0) { + Some(s) => s, + None => return E_ARGS, + }; match provider_kind() { ProviderKind::Serde => { let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); if let NodeRep::Serde(ref a) = node_rep { if let Value::Object(map) = &**a { if let Some(child) = map.get(&key) { - if let Ok(mut mm) = NODES.lock() { mm.insert(id, NodeRep::Serde(Arc::new(child.clone()))); } + if let Ok(mut mm) = NODES.lock() { + mm.insert(id, NodeRep::Serde(Arc::new(child.clone()))); + } return write_tlv_handle(T_JSON_NODE, id, result, result_len); } } } - if let Ok(mut mm) = NODES.lock() { mm.insert(id, NodeRep::Serde(Arc::new(Value::Null))); } + if let Ok(mut mm) = NODES.lock() { + mm.insert(id, NodeRep::Serde(Arc::new(Value::Null))); + } write_tlv_handle(T_JSON_NODE, id, result, result_len) } ProviderKind::Yyjson => { - let v = if let NodeRep::Yy { ptr, .. } = node_rep { ptr as *mut c_void } else { std::ptr::null_mut() }; + let v = if let NodeRep::Yy { ptr, .. } = node_rep { + ptr as *mut c_void + } else { + std::ptr::null_mut() + }; let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); let mut out_ptr: *mut c_void = std::ptr::null_mut(); if !v.is_null() && nyjson_is_obj(v) != 0 { let c = CString::new(key).unwrap_or_default(); out_ptr = nyjson_obj_get_key(v, c.as_ptr()); } - let doc_id = if let NodeRep::Yy { doc_id, .. } = node_rep { doc_id } else { 0 }; + let doc_id = if let NodeRep::Yy { doc_id, .. } = node_rep { + doc_id + } else { + 0 + }; let rep = if out_ptr.is_null() { NodeRep::Yy { doc_id, ptr: 0 } } else { - NodeRep::Yy { doc_id, ptr: out_ptr as usize } + NodeRep::Yy { + doc_id, + ptr: out_ptr as usize, + } }; - if let Ok(mut mm) = NODES.lock() { mm.insert(id, rep); } + if let Ok(mut mm) = NODES.lock() { + mm.insert(id, rep); + } write_tlv_handle(T_JSON_NODE, id, result, result_len) } } } - JN_SIZE => { - match provider_kind() { - ProviderKind::Serde => { - let n = match node_rep { NodeRep::Serde(ref a) => match &**a { Value::Array(a) => a.len() as i64, Value::Object(o) => o.len() as i64, _ => 0 }, _ => 0 }; - write_tlv_i64(n, result, result_len) - } - ProviderKind::Yyjson => { - let v = if let NodeRep::Yy { ptr, .. } = node_rep { ptr as *mut c_void } else { std::ptr::null_mut() }; - let n = if !v.is_null() { if nyjson_is_arr(v) != 0 { nyjson_arr_size_val(v) as i64 } else if nyjson_is_obj(v) != 0 { nyjson_obj_size_val(v) as i64 } else { 0 } } else { 0 }; - write_tlv_i64(n, result, result_len) - } + JN_SIZE => match provider_kind() { + ProviderKind::Serde => { + let n = match node_rep { + NodeRep::Serde(ref a) => match &**a { + Value::Array(a) => a.len() as i64, + Value::Object(o) => o.len() as i64, + _ => 0, + }, + _ => 0, + }; + write_tlv_i64(n, result, result_len) } - } + ProviderKind::Yyjson => { + let v = if let NodeRep::Yy { ptr, .. } = node_rep { + ptr as *mut c_void + } else { + std::ptr::null_mut() + }; + let n = if !v.is_null() { + if nyjson_is_arr(v) != 0 { + nyjson_arr_size_val(v) as i64 + } else if nyjson_is_obj(v) != 0 { + nyjson_obj_size_val(v) as i64 + } else { + 0 + } + } else { + 0 + }; + write_tlv_i64(n, result, result_len) + } + }, JN_AT => { - let idx = match read_arg_i64(args, args_len, 0) { Some(v) => v, None => return E_ARGS }; - if idx < 0 { return E_ARGS; } + let idx = match read_arg_i64(args, args_len, 0) { + Some(v) => v, + None => return E_ARGS, + }; + if idx < 0 { + return E_ARGS; + } match provider_kind() { ProviderKind::Serde => { let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); if let NodeRep::Serde(ref a) = node_rep { if let Value::Array(arr) = &**a { let i = idx as usize; - if i < arr.len() { if let Ok(mut mm) = NODES.lock() { mm.insert(id, NodeRep::Serde(Arc::new(arr[i].clone()))); } return write_tlv_handle(T_JSON_NODE, id, result, result_len); } + if i < arr.len() { + if let Ok(mut mm) = NODES.lock() { + mm.insert(id, NodeRep::Serde(Arc::new(arr[i].clone()))); + } + return write_tlv_handle(T_JSON_NODE, id, result, result_len); + } } } - if let Ok(mut mm) = NODES.lock() { mm.insert(id, NodeRep::Serde(Arc::new(Value::Null))); } + if let Ok(mut mm) = NODES.lock() { + mm.insert(id, NodeRep::Serde(Arc::new(Value::Null))); + } write_tlv_handle(T_JSON_NODE, id, result, result_len) } ProviderKind::Yyjson => { - let v = if let NodeRep::Yy { ptr, .. } = node_rep { ptr as *mut c_void } else { std::ptr::null_mut() }; + let v = if let NodeRep::Yy { ptr, .. } = node_rep { + ptr as *mut c_void + } else { + std::ptr::null_mut() + }; let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); let mut child: *mut c_void = std::ptr::null_mut(); - if !v.is_null() && nyjson_is_arr(v) != 0 { child = nyjson_arr_get_val(v, idx as usize); } - let doc_id = if let NodeRep::Yy { doc_id, .. } = node_rep { doc_id } else { 0 }; + if !v.is_null() && nyjson_is_arr(v) != 0 { + child = nyjson_arr_get_val(v, idx as usize); + } + let doc_id = if let NodeRep::Yy { doc_id, .. } = node_rep { + doc_id + } else { + 0 + }; let rep = if child.is_null() { NodeRep::Yy { doc_id, ptr: 0 } } else { - NodeRep::Yy { doc_id, ptr: child as usize } + NodeRep::Yy { + doc_id, + ptr: child as usize, + } }; - if let Ok(mut mm) = NODES.lock() { mm.insert(id, rep); } + if let Ok(mut mm) = NODES.lock() { + mm.insert(id, rep); + } write_tlv_handle(T_JSON_NODE, id, result, result_len) } } } JN_STR => match provider_kind() { ProviderKind::Serde => { - if let NodeRep::Serde(ref a) = node_rep { match &**a { Value::String(s) => write_tlv_string(s, result, result_len), Value::Object(o) => { if let Some(Value::String(s)) = o.get("value") { write_tlv_string(s, result, result_len) } else { write_tlv_string("", result, result_len) } }, _ => write_tlv_string("", result, result_len) } } else { write_tlv_string("", result, result_len) } + if let NodeRep::Serde(ref a) = node_rep { + match &**a { + Value::String(s) => write_tlv_string(s, result, result_len), + Value::Object(o) => { + if let Some(Value::String(s)) = o.get("value") { + write_tlv_string(s, result, result_len) + } else { + write_tlv_string("", result, result_len) + } + } + _ => write_tlv_string("", result, result_len), + } + } else { + write_tlv_string("", result, result_len) + } } ProviderKind::Yyjson => { - let v = if let NodeRep::Yy { ptr, .. } = node_rep { ptr as *mut c_void } else { std::ptr::null_mut() }; - if !v.is_null() && nyjson_is_str(v) != 0 { let s = nyjson_get_str_val(v); if s.is_null() { write_tlv_string("", result, result_len) } else { let rs = CStr::from_ptr(s).to_string_lossy().to_string(); write_tlv_string(&rs, result, result_len) } } - else if !v.is_null() && nyjson_is_obj(v) != 0 { let key = CString::new("value").unwrap(); let child = nyjson_obj_get_key(v, key.as_ptr()); if !child.is_null() && nyjson_is_str(child) != 0 { let s = nyjson_get_str_val(child); if s.is_null() { write_tlv_string("", result, result_len) } else { let rs = CStr::from_ptr(s).to_string_lossy().to_string(); write_tlv_string(&rs, result, result_len) } } else { write_tlv_string("", result, result_len) } } - else { write_tlv_string("", result, result_len) } + let v = if let NodeRep::Yy { ptr, .. } = node_rep { + ptr as *mut c_void + } else { + std::ptr::null_mut() + }; + if !v.is_null() && nyjson_is_str(v) != 0 { + let s = nyjson_get_str_val(v); + if s.is_null() { + write_tlv_string("", result, result_len) + } else { + let rs = CStr::from_ptr(s).to_string_lossy().to_string(); + write_tlv_string(&rs, result, result_len) + } + } else if !v.is_null() && nyjson_is_obj(v) != 0 { + let key = CString::new("value").unwrap(); + let child = nyjson_obj_get_key(v, key.as_ptr()); + if !child.is_null() && nyjson_is_str(child) != 0 { + let s = nyjson_get_str_val(child); + if s.is_null() { + write_tlv_string("", result, result_len) + } else { + let rs = CStr::from_ptr(s).to_string_lossy().to_string(); + write_tlv_string(&rs, result, result_len) + } + } else { + write_tlv_string("", result, result_len) + } + } else { + write_tlv_string("", result, result_len) + } } }, JN_INT => match provider_kind() { ProviderKind::Serde => { - if let NodeRep::Serde(ref a) = node_rep { match &**a { Value::Number(n) => write_tlv_i64(n.as_i64().unwrap_or(0), result, result_len), Value::Object(o) => { if let Some(Value::Number(n)) = o.get("value") { write_tlv_i64(n.as_i64().unwrap_or(0), result, result_len) } else { write_tlv_i64(0, result, result_len) } }, _ => write_tlv_i64(0, result, result_len) } } else { write_tlv_i64(0, result, result_len) } + if let NodeRep::Serde(ref a) = node_rep { + match &**a { + Value::Number(n) => { + write_tlv_i64(n.as_i64().unwrap_or(0), result, result_len) + } + Value::Object(o) => { + if let Some(Value::Number(n)) = o.get("value") { + write_tlv_i64(n.as_i64().unwrap_or(0), result, result_len) + } else { + write_tlv_i64(0, result, result_len) + } + } + _ => write_tlv_i64(0, result, result_len), + } + } else { + write_tlv_i64(0, result, result_len) + } } ProviderKind::Yyjson => { - let v = if let NodeRep::Yy { ptr, .. } = node_rep { ptr as *mut c_void } else { std::ptr::null_mut() }; - if !v.is_null() && nyjson_is_int(v) != 0 { write_tlv_i64(nyjson_get_sint_val(v) as i64, result, result_len) } - else if !v.is_null() && nyjson_is_obj(v) != 0 { let key = CString::new("value").unwrap(); let child = nyjson_obj_get_key(v, key.as_ptr()); if !child.is_null() && nyjson_is_int(child) != 0 { write_tlv_i64(nyjson_get_sint_val(child) as i64, result, result_len) } else { write_tlv_i64(0, result, result_len) } } - else { write_tlv_i64(0, result, result_len) } + let v = if let NodeRep::Yy { ptr, .. } = node_rep { + ptr as *mut c_void + } else { + std::ptr::null_mut() + }; + if !v.is_null() && nyjson_is_int(v) != 0 { + write_tlv_i64(nyjson_get_sint_val(v) as i64, result, result_len) + } else if !v.is_null() && nyjson_is_obj(v) != 0 { + let key = CString::new("value").unwrap(); + let child = nyjson_obj_get_key(v, key.as_ptr()); + if !child.is_null() && nyjson_is_int(child) != 0 { + write_tlv_i64(nyjson_get_sint_val(child) as i64, result, result_len) + } else { + write_tlv_i64(0, result, result_len) + } + } else { + write_tlv_i64(0, result, result_len) + } } }, JN_BOOL => match provider_kind() { - ProviderKind::Serde => { if let NodeRep::Serde(ref a) = node_rep { if let Value::Bool(b) = **a { write_tlv_bool(b, result, result_len) } else { write_tlv_bool(false, result, result_len) } } else { write_tlv_bool(false, result, result_len) } } - ProviderKind::Yyjson => { let v = if let NodeRep::Yy { ptr, .. } = node_rep { ptr as *mut c_void } else { std::ptr::null_mut() }; if !v.is_null() && nyjson_is_bool(v) != 0 { write_tlv_bool(nyjson_get_bool_val(v) != 0, result, result_len) } else { write_tlv_bool(false, result, result_len) } } + ProviderKind::Serde => { + if let NodeRep::Serde(ref a) = node_rep { + if let Value::Bool(b) = **a { + write_tlv_bool(b, result, result_len) + } else { + write_tlv_bool(false, result, result_len) + } + } else { + write_tlv_bool(false, result, result_len) + } + } + ProviderKind::Yyjson => { + let v = if let NodeRep::Yy { ptr, .. } = node_rep { + ptr as *mut c_void + } else { + std::ptr::null_mut() + }; + if !v.is_null() && nyjson_is_bool(v) != 0 { + write_tlv_bool(nyjson_get_bool_val(v) != 0, result, result_len) + } else { + write_tlv_bool(false, result, result_len) + } + } }, _ => E_METHOD, } diff --git a/plugins/nyash-net-plugin/src/consts.rs b/plugins/nyash-net-plugin/src/consts.rs index 18bf6a90..dfdcf250 100644 --- a/plugins/nyash-net-plugin/src/consts.rs +++ b/plugins/nyash-net-plugin/src/consts.rs @@ -61,4 +61,3 @@ pub(crate) const M_CONN_SEND: u32 = 1; // bytes/string -> void pub(crate) const M_CONN_RECV: u32 = 2; // -> bytes pub(crate) const M_CONN_CLOSE: u32 = 3; // -> void pub(crate) const M_CONN_RECV_TIMEOUT: u32 = 4; // ms -> bytes (empty if timeout) - diff --git a/plugins/nyash-net-plugin/src/http_helpers.rs b/plugins/nyash-net-plugin/src/http_helpers.rs index d3861247..01e734f1 100644 --- a/plugins/nyash-net-plugin/src/http_helpers.rs +++ b/plugins/nyash-net-plugin/src/http_helpers.rs @@ -149,11 +149,17 @@ pub fn parse_client_response_into(resp_id: u32, conn_id: u32) { let mut tmp = [0u8; 2048]; loop { match s.read(&mut tmp) { - Ok(0) => { return; } + Ok(0) => { + return; + } Ok(n) => { buf.extend_from_slice(&tmp[..n]); - if find_header_end(&buf).is_some() { break; } - if buf.len() > 256 * 1024 { break; } + if find_header_end(&buf).is_some() { + break; + } + if buf.len() > 256 * 1024 { + break; + } } Err(_) => return, } @@ -191,7 +197,9 @@ pub fn parse_client_response_into(resp_id: u32, conn_id: u32) { } } } - if should_remove { map.remove(&conn_id); } + if should_remove { + map.remove(&conn_id); + } } if let Some(rp) = state::RESPONSES.lock().unwrap().get_mut(&resp_id) { rp.status = status; @@ -201,4 +209,3 @@ pub fn parse_client_response_into(resp_id: u32, conn_id: u32) { rp.client_conn_id = None; } } - diff --git a/plugins/nyash-net-plugin/src/lib.rs b/plugins/nyash-net-plugin/src/lib.rs index 1e591fe1..cd5550da 100644 --- a/plugins/nyash-net-plugin/src/lib.rs +++ b/plugins/nyash-net-plugin/src/lib.rs @@ -2,6 +2,7 @@ //! Provides ServerBox/RequestBox/ResponseBox/ClientBox and socket variants. //! Pure in-process HTTP over localhost for E2E of BoxRef args/returns. +use crate::state::{ClientState, RequestState, ResponseState, ServerState, SockConnState}; use once_cell::sync::Lazy; use std::collections::{HashMap, VecDeque}; use std::io::Write as IoWrite; @@ -11,7 +12,6 @@ use std::sync::{ Arc, Mutex, }; use std::time::Duration; -use crate::state::{ClientState, RequestState, ResponseState, ServerState, SockConnState}; // ===== Simple logger (enabled when NYASH_NET_LOG=1) ===== static LOG_ON: Lazy = Lazy::new(|| std::env::var("NYASH_NET_LOG").unwrap_or_default() == "1"); @@ -234,7 +234,9 @@ extern "C" fn sockserver_invoke_id( result: *mut u8, result_len: *mut usize, ) -> i32 { - unsafe { sockets::sock_server_invoke(method_id, instance_id, args, args_len, result, result_len) } + unsafe { + sockets::sock_server_invoke(method_id, instance_id, args, args_len, result, result_len) + } } #[no_mangle] pub static nyash_typebox_SockServerBox: NyashTypeBoxFfi = NyashTypeBoxFfi { @@ -268,7 +270,9 @@ extern "C" fn sockclient_invoke_id( result: *mut u8, result_len: *mut usize, ) -> i32 { - unsafe { sockets::sock_client_invoke(method_id, instance_id, args, args_len, result, result_len) } + unsafe { + sockets::sock_client_invoke(method_id, instance_id, args, args_len, result, result_len) + } } #[no_mangle] pub static nyash_typebox_SockClientBox: NyashTypeBoxFfi = NyashTypeBoxFfi { @@ -1012,7 +1016,8 @@ unsafe fn client_invoke( let body_len = body.len(); // Create client response handle let resp_id = state::next_response_id(); - let (_h, _p, req_bytes) = http_helpers::build_http_request("POST", &url, Some(&body), resp_id); + let (_h, _p, req_bytes) = + http_helpers::build_http_request("POST", &url, Some(&body), resp_id); let mut tcp_ok = false; if let Ok(mut stream) = TcpStream::connect(format!("{}:{}", host, port)) { let _ = stream.write_all(&req_bytes); @@ -1086,9 +1091,9 @@ unsafe fn client_invoke( // ===== Helpers ===== use ffi::slice; -mod tlv; mod http_helpers; mod sockets; +mod tlv; // ===== HTTP helpers ===== // moved diff --git a/plugins/nyash-net-plugin/src/sockets.rs b/plugins/nyash-net-plugin/src/sockets.rs index d61db9c0..0fc55e1b 100644 --- a/plugins/nyash-net-plugin/src/sockets.rs +++ b/plugins/nyash-net-plugin/src/sockets.rs @@ -1,14 +1,19 @@ use std::collections::VecDeque; use std::io::{Read, Write as IoWrite}; use std::net::{TcpListener, TcpStream}; -use std::sync::{atomic::{AtomicBool, Ordering}, Arc, Mutex}; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, +}; use std::time::Duration; use crate::consts::*; use crate::state::{self, SockConnState, SockServerState}; // Utilities provided by parent module -fn logf(s: String) { super::net_log(&s); } +fn logf(s: String) { + super::net_log(&s); +} pub(crate) unsafe fn sock_server_invoke( m: u32, @@ -51,7 +56,9 @@ pub(crate) unsafe fn sock_server_invoke( let conn_id = state::next_sock_conn_id(); state::SOCK_CONNS.lock().unwrap().insert( conn_id, - SockConnState { stream: Mutex::new(stream) }, + SockConnState { + stream: Mutex::new(stream), + }, ); logf(format!("sock:accept conn_id={}", conn_id)); pending.lock().unwrap().push_back(conn_id); @@ -161,7 +168,9 @@ pub(crate) unsafe fn sock_client_invoke( let conn_id = state::next_sock_conn_id(); state::SOCK_CONNS.lock().unwrap().insert( conn_id, - SockConnState { stream: Mutex::new(stream) }, + SockConnState { + stream: Mutex::new(stream), + }, ); logf(format!("sock:connect ok conn_id={}", conn_id)); crate::tlv::write_tlv_handle(T_SOCK_CONN, conn_id, res, res_len) @@ -190,7 +199,8 @@ pub(crate) unsafe fn sock_conn_invoke( crate::tlv::write_u32(0, res, res_len) } M_CONN_SEND => { - let bytes = crate::tlv::tlv_parse_bytes(super::ffi::slice(args, args_len)).unwrap_or_default(); + let bytes = + crate::tlv::tlv_parse_bytes(super::ffi::slice(args, args_len)).unwrap_or_default(); if let Some(conn) = state::SOCK_CONNS.lock().unwrap().get(&id) { if let Ok(mut s) = conn.stream.lock() { let _ = s.write_all(&bytes); @@ -229,11 +239,17 @@ pub(crate) unsafe fn sock_conn_invoke( match resv { Ok(n) => { buf.truncate(n); - logf(format!("sock:recvTimeout id={} n={} ms={}", id, n, timeout_ms)); + logf(format!( + "sock:recvTimeout id={} n={} ms={}", + id, n, timeout_ms + )); return crate::tlv::write_tlv_bytes(&buf, res, res_len); } Err(e) => { - logf(format!("sock:recvTimeout error id={} ms={} err={:?}", id, timeout_ms, e)); + logf(format!( + "sock:recvTimeout error id={} ms={} err={:?}", + id, timeout_ms, e + )); return E_ERR; } } @@ -249,4 +265,3 @@ pub(crate) unsafe fn sock_conn_invoke( _ => E_INV_METHOD, } } - diff --git a/plugins/nyash-string-plugin/src/lib.rs b/plugins/nyash-string-plugin/src/lib.rs index ad033729..fafaddf4 100644 --- a/plugins/nyash-string-plugin/src/lib.rs +++ b/plugins/nyash-string-plugin/src/lib.rs @@ -27,7 +27,7 @@ const M_FROM_UTF8: u32 = 5; // fromUtf8(data: String|Bytes) -> Handle(new) const M_TO_UTF8: u32 = 6; // toUtf8() -> String const M_FINI: u32 = u32::MAX; -const TYPE_ID_STRING: u32 = 10; // Match nyash.toml type_id +const TYPE_ID_STRING: u32 = 10; // Match nyash.toml type_id struct StrInstance { s: String, @@ -222,7 +222,7 @@ extern "C" fn string_resolve(name: *const c_char) -> u32 { "charCodeAt" => M_CHAR_CODE_AT, "concat" => M_CONCAT, "fromUtf8" => M_FROM_UTF8, - "toUtf8" | "toString" => M_TO_UTF8, // Map toString to toUtf8 + "toUtf8" | "toString" => M_TO_UTF8, // Map toString to toUtf8 _ => 0, } } @@ -264,10 +264,16 @@ extern "C" fn string_invoke_id( if let Ok(m) = INST.lock() { if let Some(inst) = m.get(&instance_id) { let len = inst.s.len(); - eprintln!("[StringBox] Found instance, string={:?}, len={}", inst.s, len); + eprintln!( + "[StringBox] Found instance, string={:?}, len={}", + inst.s, len + ); return write_tlv_i64(len as i64, result, result_len); } else { - eprintln!("[StringBox] Instance {} not found in INST map!", instance_id); + eprintln!( + "[StringBox] Instance {} not found in INST map!", + instance_id + ); return E_HANDLE; } } else { diff --git a/src/backend/mir_interpreter.rs b/src/backend/mir_interpreter.rs deleted file mode 100644 index 15191e08..00000000 --- a/src/backend/mir_interpreter.rs +++ /dev/null @@ -1,908 +0,0 @@ -/*! - * Minimal MIR Interpreter - * - * Executes a subset of MIR instructions for fast iteration without LLVM/JIT. - * Supported: Const, BinOp(Add/Sub/Mul/Div/Mod), Compare, Load/Store, Branch, Jump, Return, - * Print/Debug (best-effort), Barrier/Safepoint (no-op). - */ - -use std::collections::HashMap; - -use crate::backend::abi_util::{eq_vm, to_bool_vm}; -use crate::backend::vm::{VMError, VMValue}; -use crate::box_trait::NyashBox; -use crate::mir::{ - BasicBlockId, BinaryOp, Callee, CompareOp, ConstValue, MirFunction, MirInstruction, MirModule, ValueId, -}; - -pub struct MirInterpreter { - // SSA value table (per-function; swapped on call) - regs: HashMap, - // Simple local memory for Load/Store where `ptr` is a ValueId token - mem: HashMap, - // Object field storage for RefGet/RefSet (keyed by reference ValueId) - obj_fields: HashMap>, - // Function table (current module) - functions: HashMap, - // Currently executing function name (for call resolution preferences) - cur_fn: Option, -} - -impl MirInterpreter { - pub fn new() -> Self { - Self { - regs: HashMap::new(), - mem: HashMap::new(), - obj_fields: HashMap::new(), - functions: HashMap::new(), - cur_fn: None, - } - } - - /// Execute module entry (main) and return boxed result - pub fn execute_module(&mut self, module: &MirModule) -> Result, VMError> { - // Snapshot functions for call resolution - self.functions = module.functions.clone(); - let func = module - .functions - .get("main") - .ok_or_else(|| VMError::InvalidInstruction("missing main".into()))?; - let ret = self.execute_function(func)?; - Ok(ret.to_nyash_box()) - } - - fn execute_function(&mut self, func: &MirFunction) -> Result { - self._exec_function_inner(func, None) - } - - fn _exec_function_inner( - &mut self, - func: &MirFunction, - arg_vals: Option<&[VMValue]>, - ) -> Result { - // Swap in a fresh register file for this call - let saved_regs = std::mem::take(&mut self.regs); - let saved_fn = self.cur_fn.clone(); - self.cur_fn = Some(func.signature.name.clone()); - - // Bind parameters if provided - if let Some(args) = arg_vals { - for (i, pid) in func.params.iter().enumerate() { - let v = args.get(i).cloned().unwrap_or(VMValue::Void); - self.regs.insert(*pid, v); - } - } - - let mut cur = func.entry_block; - let mut last_pred: Option = None; - loop { - let block = func - .blocks - .get(&cur) - .ok_or_else(|| VMError::InvalidBasicBlock(format!("bb {:?} not found", cur)))?; - // Resolve incoming phi nodes using predecessor - for inst in &block.instructions { - if let MirInstruction::Phi { dst, inputs } = inst { - if let Some(pred) = last_pred { - if let Some((_, val)) = inputs.iter().find(|(bb, _)| *bb == pred) { - let v = self.reg_load(*val)?; - self.regs.insert(*dst, v); - } - } else { - // Entry block PHI: pick first input as a pragmatic default - if let Some((_, val)) = inputs.first() { - let v = self.reg_load(*val)?; - self.regs.insert(*dst, v); - } - } - } - } - // Execute non-phi, non-terminator instructions - for inst in block.non_phi_instructions() { - match inst { - MirInstruction::Const { dst, value } => { - let v = match value { - ConstValue::Integer(i) => VMValue::Integer(*i), - ConstValue::Float(f) => VMValue::Float(*f), - ConstValue::Bool(b) => VMValue::Bool(*b), - ConstValue::String(s) => VMValue::String(s.clone()), - ConstValue::Null | ConstValue::Void => VMValue::Void, - }; - self.regs.insert(*dst, v); - } - MirInstruction::NewBox { - dst, - box_type, - args, - } => { - // Build arg boxes - let mut a: Vec> = Vec::new(); - for vid in args { - a.push(self.reg_load(*vid)?.to_nyash_box()); - } - // Use unified global registry (plugins already initialized by runner) - let reg = crate::runtime::unified_registry::get_global_unified_registry(); - let created = - reg.lock().unwrap().create_box(box_type, &a).map_err(|e| { - VMError::InvalidInstruction(format!( - "NewBox {} failed: {}", - box_type, e - )) - })?; - self.regs.insert(*dst, VMValue::from_nyash_box(created)); - } - MirInstruction::PluginInvoke { - dst, - box_val, - method, - args, - .. - } => { - // Resolve receiver - let recv = self.reg_load(*box_val)?; - let recv_box: Box = match recv.clone() { - VMValue::BoxRef(b) => b.share_box(), - other => other.to_nyash_box(), - }; - // If PluginBoxV2 → invoke via unified plugin host - if let Some(p) = recv_box - .as_any() - .downcast_ref::( - ) { - let host = - crate::runtime::plugin_loader_unified::get_global_plugin_host(); - let host = host.read().unwrap(); - let mut argv: Vec> = Vec::new(); - for a in args { - argv.push(self.reg_load(*a)?.to_nyash_box()); - } - match host.invoke_instance_method( - &p.box_type, - method, - p.inner.instance_id, - &argv, - ) { - Ok(Some(ret)) => { - if let Some(d) = dst { - self.regs.insert(*d, VMValue::from_nyash_box(ret)); - } - } - Ok(None) => { - if let Some(d) = dst { - self.regs.insert(*d, VMValue::Void); - } - } - Err(e) => { - return Err(VMError::InvalidInstruction(format!( - "PluginInvoke {}.{} failed: {:?}", - p.box_type, method, e - ))); - } - } - } else { - // Minimal fallback: toString - if method == "toString" { - if let Some(d) = dst { - self.regs.insert( - *d, - VMValue::String(recv_box.to_string_box().value), - ); - } - } else { - return Err(VMError::InvalidInstruction(format!( - "PluginInvoke unsupported on {} for method {}", - recv_box.type_name(), - method - ))); - } - } - } - MirInstruction::BoxCall { - dst, - box_val, - method, - args, - .. - } => { - // Support getField/setField for normalized RefGet/RefSet - if method == "getField" { - if args.len() != 1 { - return Err(VMError::InvalidInstruction( - "getField expects 1 arg".into(), - )); - } - let fname = match self.reg_load(args[0].clone())? { - VMValue::String(s) => s, - v => v.to_string(), - }; - let v = self - .obj_fields - .get(box_val) - .and_then(|m| m.get(&fname)) - .cloned() - .unwrap_or(VMValue::Void); - if let Some(d) = dst { - self.regs.insert(*d, v); - } - continue; - } else if method == "setField" { - if args.len() != 2 { - return Err(VMError::InvalidInstruction( - "setField expects 2 args".into(), - )); - } - let fname = match self.reg_load(args[0].clone())? { - VMValue::String(s) => s, - v => v.to_string(), - }; - let valv = self.reg_load(args[1].clone())?; - self.obj_fields - .entry(*box_val) - .or_default() - .insert(fname, valv); - continue; - } - // Builtin StringBox minimal methods (length/concat) to bridge plugin-first gaps - { - let recv = self.reg_load(*box_val)?; - let recv_box_any: Box = match recv.clone() { - VMValue::BoxRef(b) => b.share_box(), - other => other.to_nyash_box(), - }; - if let Some(sb) = recv_box_any - .as_any() - .downcast_ref::() - { - match method.as_str() { - "length" => { - let ret = sb.length(); - if let Some(d) = dst { - self.regs.insert(*d, VMValue::from_nyash_box(ret)); - } - continue; - } - "concat" => { - if args.len() != 1 { - return Err(VMError::InvalidInstruction( - "concat expects 1 arg".into(), - )); - } - let rhs = self.reg_load(args[0])?; - let new_s = format!("{}{}", sb.value, rhs.to_string()); - if let Some(d) = dst { - self.regs.insert( - *d, - VMValue::from_nyash_box(Box::new( - crate::box_trait::StringBox::new(new_s), - )), - ); - } - continue; - } - _ => { /* fallthrough to plugin or error */ } - } - } - } - // Fallback: treat like PluginInvoke for plugin-backed boxes - let recv = self.reg_load(*box_val)?; - let recv_box: Box = match recv.clone() { - VMValue::BoxRef(b) => b.share_box(), - other => other.to_nyash_box(), - }; - if let Some(p) = recv_box - .as_any() - .downcast_ref::( - ) { - // Special-case: ConsoleBox.readLine → stdin fallback if not provided by plugin - if p.box_type == "ConsoleBox" && method == "readLine" { - use std::io::{self, Read}; - let mut s = String::new(); - let mut stdin = io::stdin(); - // Read a single line (blocking) - let mut buf = [0u8; 1]; - while let Ok(n) = stdin.read(&mut buf) { - if n == 0 { - break; - } - let ch = buf[0] as char; - if ch == '\n' { - break; - } - s.push(ch); - if s.len() > 1_000_000 { - break; - } - } - if let Some(d) = dst { - self.regs.insert(*d, VMValue::String(s)); - } - continue; - } - let host = - crate::runtime::plugin_loader_unified::get_global_plugin_host(); - let host = host.read().unwrap(); - let mut argv: Vec> = Vec::new(); - for a in args { - argv.push(self.reg_load(*a)?.to_nyash_box()); - } - match host.invoke_instance_method( - &p.box_type, - method, - p.inner.instance_id, - &argv, - ) { - Ok(Some(ret)) => { - if let Some(d) = dst { - self.regs.insert(*d, VMValue::from_nyash_box(ret)); - } - } - Ok(None) => { - if let Some(d) = dst { - self.regs.insert(*d, VMValue::Void); - } - } - Err(e) => { - return Err(VMError::InvalidInstruction(format!( - "BoxCall {}.{} failed: {:?}", - p.box_type, method, e - ))); - } - } - } else { - return Err(VMError::InvalidInstruction(format!( - "BoxCall unsupported on {}.{}", - recv_box.type_name(), - method - ))); - } - } - MirInstruction::ExternCall { - dst, - iface_name, - method_name, - args, - .. - } => { - match (iface_name.as_str(), method_name.as_str()) { - ("env.console", "log") => { - if let Some(a0) = args.get(0) { - let v = self.reg_load(*a0)?; - println!("{}", v.to_string()); - } - if let Some(d) = dst { - self.regs.insert(*d, VMValue::Void); - } - } - ("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()); - } - if let Some(d) = dst { - self.regs.insert(*d, VMValue::Future(fut)); - } - } - ("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())); - } - } - if let Some(d) = dst { - self.regs.insert(*d, VMValue::Void); - } - } - ("env.future", "await") => { - if let Some(a0) = args.get(0) { - let f = self.reg_load(*a0)?; - match f { - VMValue::Future(fut) => { - // Coarse safepoint while blocking - let v = fut.get(); - if let Some(d) = dst { - self.regs.insert(*d, VMValue::from_nyash_box(v)); - } - } - _ => return Err(VMError::TypeError("await expects Future".into())), - } - } - } - ("env.runtime", "checkpoint") => { - crate::runtime::global_hooks::safepoint_and_poll(); - if let Some(d) = dst { - self.regs.insert(*d, VMValue::Void); - } - } - ("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); - } - if let Some(d) = dst { - self.regs.insert(*d, VMValue::Void); - } - } - ("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())); - if let Some(d) = dst { - self.regs.insert(*d, VMValue::from_nyash_box(vb)); - } - } - } - _ => { - return Err(VMError::InvalidInstruction(format!( - "ExternCall {}.{} not supported", - iface_name, method_name - ))); - } - } - } - MirInstruction::RefSet { - reference, - field, - value, - } => { - let v = self.reg_load(*value)?; - self.obj_fields - .entry(*reference) - .or_default() - .insert(field.clone(), v); - } - MirInstruction::RefGet { - dst, - reference, - field, - } => { - let v = self - .obj_fields - .get(reference) - .and_then(|m| m.get(field)) - .cloned() - .unwrap_or(VMValue::Void); - self.regs.insert(*dst, v); - } - MirInstruction::BinOp { dst, op, lhs, rhs } => { - let a = self.reg_load(*lhs)?; - let b = self.reg_load(*rhs)?; - let v = self.eval_binop(*op, a, b)?; - self.regs.insert(*dst, v); - } - MirInstruction::UnaryOp { dst, op, operand } => { - let x = self.reg_load(*operand)?; - let v = match op { - crate::mir::UnaryOp::Neg => match x { - VMValue::Integer(i) => VMValue::Integer(-i), - VMValue::Float(f) => VMValue::Float(-f), - _ => { - return Err(VMError::TypeError(format!( - "neg expects number, got {:?}", - x - ))) - } - }, - crate::mir::UnaryOp::Not => { - VMValue::Bool(!to_bool_vm(&x).map_err(|e| VMError::TypeError(e))?) - } - crate::mir::UnaryOp::BitNot => match x { - VMValue::Integer(i) => VMValue::Integer(!i), - _ => { - return Err(VMError::TypeError(format!( - "bitnot expects integer, got {:?}", - x - ))) - } - }, - }; - self.regs.insert(*dst, v); - } - MirInstruction::Compare { dst, op, lhs, rhs } => { - let a = self.reg_load(*lhs)?; - let b = self.reg_load(*rhs)?; - let v = self.eval_cmp(*op, a, b)?; - self.regs.insert(*dst, VMValue::Bool(v)); - } - MirInstruction::Load { dst, ptr } => { - let v = self.mem.get(ptr).cloned().unwrap_or(VMValue::Void); - self.regs.insert(*dst, v); - } - MirInstruction::Store { value, ptr } => { - let v = self.reg_load(*value)?; - self.mem.insert(*ptr, v); - } - MirInstruction::Copy { dst, src } => { - let v = self.reg_load(*src)?; - self.regs.insert(*dst, v); - } - MirInstruction::Debug { value, message } => { - let v = self.reg_load(*value)?; - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[mir-debug] {} => {:?}", message, v); - } - } - MirInstruction::Print { value, .. } => { - let v = self.reg_load(*value)?; - println!("{}", v.to_string()); - } - // No-ops in the interpreter for now - MirInstruction::BarrierRead { .. } - | MirInstruction::BarrierWrite { .. } - | MirInstruction::Barrier { .. } - | MirInstruction::Safepoint - | MirInstruction::Nop => {} - MirInstruction::Call { - dst, - func, - callee, - args, - .. - } => { - // VM実行器Callee対応 - ChatGPT5 Pro MIR革命の最終1%! - - // Phase 1: 段階移行サポート - callee型安全解決を優先、フォールバックで従来解決 - let call_result = if let Some(callee_type) = callee { - // NEW: 型安全Callee解決(ChatGPT5 Pro設計) - self.execute_callee_call(callee_type, args)? - } else { - // LEGACY: 従来の文字列ベース解決(func: ValueId) - self.execute_legacy_call(*func, args)? - }; - - if let Some(d) = dst { - self.regs.insert(*d, call_result); - } - } - // Unimplemented but recognized — return clear error for visibility - other => { - return Err(VMError::InvalidInstruction(format!( - "MIR interp: unimplemented instruction: {:?}", - other - ))) - } - } - } - // Handle terminator - let out = match &block.terminator { - Some(MirInstruction::Return { value }) => { - if let Some(v) = value { - self.reg_load(*v) - } else { - Ok(VMValue::Void) - } - } - Some(MirInstruction::Jump { target }) => { - last_pred = Some(block.id); - cur = *target; - continue; - } - Some(MirInstruction::Branch { - condition, - then_bb, - else_bb, - }) => { - let c = self.reg_load(*condition)?; - let t = to_bool_vm(&c).map_err(|e| VMError::TypeError(e))?; - last_pred = Some(block.id); - cur = if t { *then_bb } else { *else_bb }; - continue; - } - None => { - Err(VMError::InvalidBasicBlock(format!( - "unterminated block {:?}", - block.id - ))) - } - Some(other) => { - Err(VMError::InvalidInstruction(format!( - "invalid terminator in MIR interp: {:?}", - other - ))) - } - }; - // Function finished (return or error) - // Restore previous register file and current function - let result = out; - self.cur_fn = saved_fn; - self.regs = saved_regs; - return result; - } - } - - fn reg_load(&self, id: ValueId) -> Result { - self.regs - .get(&id) - .cloned() - .ok_or_else(|| VMError::InvalidValue(format!("use of undefined value {:?}", id))) - } - - fn eval_binop(&self, op: BinaryOp, a: VMValue, b: VMValue) -> Result { - use BinaryOp::*; - use VMValue::*; - Ok(match (op, a, b) { - (Add, Integer(x), Integer(y)) => Integer(x + y), - // String concat: ifいずれかがStringなら文字列連結 - (Add, String(s), Integer(y)) => String(format!("{}{}", s, y)), - (Add, String(s), Float(y)) => String(format!("{}{}", s, y)), - (Add, String(s), Bool(y)) => String(format!("{}{}", s, y)), - (Add, String(s), String(t)) => String(format!("{}{}", s, t)), - (Add, Integer(x), String(t)) => String(format!("{}{}", x, t)), - (Add, Float(x), String(t)) => String(format!("{}{}", x, t)), - (Add, Bool(x), String(t)) => String(format!("{}{}", x, t)), - (Sub, Integer(x), Integer(y)) => Integer(x - y), - (Mul, Integer(x), Integer(y)) => Integer(x * y), - (Div, Integer(_), Integer(0)) => return Err(VMError::DivisionByZero), - (Div, Integer(x), Integer(y)) => Integer(x / y), - (Mod, Integer(_), Integer(0)) => return Err(VMError::DivisionByZero), - (Mod, Integer(x), Integer(y)) => Integer(x % y), - // Float ops (best-effort) - (Add, Float(x), Float(y)) => Float(x + y), - (Sub, Float(x), Float(y)) => Float(x - y), - (Mul, Float(x), Float(y)) => Float(x * y), - (Div, Float(_), Float(y)) if y == 0.0 => return Err(VMError::DivisionByZero), - (Div, Float(x), Float(y)) => Float(x / y), - (Mod, Float(x), Float(y)) => Float(x % y), - // Logical/bitwise on integers - (BitAnd, Integer(x), Integer(y)) => Integer(x & y), - (BitOr, Integer(x), Integer(y)) => Integer(x | y), - (BitXor, Integer(x), Integer(y)) => Integer(x ^ y), - (Shl, Integer(x), Integer(y)) => Integer(x.wrapping_shl(y as u32)), - (Shr, Integer(x), Integer(y)) => Integer(x.wrapping_shr(y as u32)), - // Fallbacks not yet supported - (opk, va, vb) => { - return Err(VMError::TypeError(format!( - "unsupported binop {:?} on {:?} and {:?}", - opk, va, vb - ))) - } - }) - } - - fn eval_cmp(&self, op: CompareOp, a: VMValue, b: VMValue) -> Result { - use CompareOp::*; - use VMValue::*; - Ok(match (op, &a, &b) { - (Eq, _, _) => eq_vm(&a, &b), - (Ne, _, _) => !eq_vm(&a, &b), - (Lt, Integer(x), Integer(y)) => x < y, - (Le, Integer(x), Integer(y)) => x <= y, - (Gt, Integer(x), Integer(y)) => x > y, - (Ge, Integer(x), Integer(y)) => x >= y, - (Lt, Float(x), Float(y)) => x < y, - (Le, Float(x), Float(y)) => x <= y, - (Gt, Float(x), Float(y)) => x > y, - (Ge, Float(x), Float(y)) => x >= y, - (opk, va, vb) => { - return Err(VMError::TypeError(format!( - "unsupported compare {:?} on {:?} and {:?}", - opk, va, vb - ))) - } - }) - } - - /// NEW: Callee型安全解決(ChatGPT5 Pro設計) - fn execute_callee_call(&mut self, callee: &Callee, args: &[ValueId]) -> Result { - match callee { - Callee::Global(func_name) => { - // グローバル関数呼び出し(nyash.builtin.print等) - self.execute_global_function(func_name, args) - } - Callee::Method { box_name, method, receiver } => { - // メソッド呼び出し(StringBox.concat等) - if let Some(recv_id) = receiver { - let recv_val = self.reg_load(*recv_id)?; - self.execute_method_call(&recv_val, method, args) - } else { - Err(VMError::InvalidInstruction(format!( - "Method call {}.{} missing receiver", - box_name, method - ))) - } - } - Callee::Constructor { box_type } => { - // コンストラクタ呼び出し(NewBox相当) - // TODO: 実際のBox生成実装(Phase 2で実装) - Err(VMError::InvalidInstruction(format!( - "Constructor calls not yet implemented for {}", - box_type - ))) - } - Callee::Closure { params: _, captures: _, me_capture: _ } => { - // クロージャ生成(NewClosure相当) - // TODO: クロージャ生成実装(Phase 2で実装) - Err(VMError::InvalidInstruction( - "Closure creation not yet implemented in VM".into() - )) - } - Callee::Value(func_val_id) => { - // 第一級関数呼び出し(クロージャ等) - let _func_val = self.reg_load(*func_val_id)?; - // TODO: 第一級関数呼び出し実装(Phase 2で拡張) - Err(VMError::InvalidInstruction( - "First-class function calls not yet implemented in VM".into() - )) - } - Callee::Extern(extern_name) => { - // 外部C ABI関数呼び出し - self.execute_extern_function(extern_name, args) - } - } - } - - /// LEGACY: 従来の文字列ベース解決(後方互換性) - fn execute_legacy_call(&mut self, func_id: ValueId, args: &[ValueId]) -> Result { - // 1) 名前を取り出す - let name_val = self.reg_load(func_id)?; - let raw = match name_val { - VMValue::String(ref s) => s.clone(), - other => other.to_string(), - }; - // 2) 直接一致を優先 - let mut pick: Option = None; - if self.functions.contains_key(&raw) { - pick = Some(raw.clone()); - } else { - let arity = args.len(); - let mut cands: Vec = Vec::new(); - // a) 末尾サフィックス一致: ".name/arity" - let suf = format!(".{}{}", raw, format!("/{}", arity)); - for k in self.functions.keys() { - if k.ends_with(&suf) { cands.push(k.clone()); } - } - // b) raw に '/' が含まれ、完全名っぽい場合はそのままも候補に(既に上で除外) - if cands.is_empty() && raw.contains('/') && self.functions.contains_key(&raw) { - cands.push(raw.clone()); - } - // c) 優先: 現在のボックス名と一致するもの - if cands.len() > 1 { - if let Some(cur) = &self.cur_fn { - let cur_box = cur.split('.').next().unwrap_or(""); - let scoped: Vec = cands - .iter() - .filter(|k| k.starts_with(&format!("{}.", cur_box))) - .cloned() - .collect(); - if scoped.len() == 1 { cands = scoped; } - } - } - if cands.len() == 1 { - pick = Some(cands.remove(0)); - } else if cands.len() > 1 { - cands.sort(); - pick = Some(cands[0].clone()); - } - } - let fname = pick.ok_or_else(|| VMError::InvalidInstruction(format!( - "call unresolved: '{}' (arity={})", - raw, - args.len() - )))?; - if std::env::var("NYASH_VM_CALL_TRACE").ok().as_deref() == Some("1") { - eprintln!("[vm] legacy-call resolved '{}' -> '{}'", raw, fname); - } - let callee = self - .functions - .get(&fname) - .cloned() - .ok_or_else(|| VMError::InvalidInstruction(format!("function not found: {}", fname)))?; - // 3) 実引数の評価 - let mut argv: Vec = Vec::new(); - for a in args { argv.push(self.reg_load(*a)?); } - // 4) 実行 - self._exec_function_inner(&callee, Some(&argv)) - } - - /// グローバル関数実行(nyash.builtin.*) - fn execute_global_function(&mut self, func_name: &str, args: &[ValueId]) -> Result { - match func_name { - "nyash.builtin.print" | "print" => { - if let Some(arg_id) = args.get(0) { - let val = self.reg_load(*arg_id)?; - println!("{}", val.to_string()); - } - Ok(VMValue::Void) - } - "nyash.console.log" => { - if let Some(arg_id) = args.get(0) { - let val = self.reg_load(*arg_id)?; - println!("{}", val.to_string()); - } - Ok(VMValue::Void) - } - "nyash.builtin.error" => { - if let Some(arg_id) = args.get(0) { - let val = self.reg_load(*arg_id)?; - eprintln!("Error: {}", val.to_string()); - } - Ok(VMValue::Void) - } - _ => Err(VMError::InvalidInstruction(format!( - "Unknown global function: {}", - func_name - ))) - } - } - - /// メソッド呼び出し実行 - fn execute_method_call(&mut self, receiver: &VMValue, method: &str, args: &[ValueId]) -> Result { - // 受信オブジェクトの型に基づいてメソッド実行 - match receiver { - VMValue::String(s) => { - match method { - "length" => Ok(VMValue::Integer(s.len() as i64)), - "concat" => { - if let Some(arg_id) = args.get(0) { - let arg_val = self.reg_load(*arg_id)?; - let new_str = format!("{}{}", s, arg_val.to_string()); - Ok(VMValue::String(new_str)) - } else { - Err(VMError::InvalidInstruction("concat requires 1 argument".into())) - } - } - _ => Err(VMError::InvalidInstruction(format!( - "Unknown String method: {}", - method - ))) - } - } - VMValue::BoxRef(box_ref) => { - // プラグインBox経由でメソッド呼び出し - if let Some(p) = box_ref.as_any().downcast_ref::() { - let host = crate::runtime::plugin_loader_unified::get_global_plugin_host(); - let host = host.read().unwrap(); - let mut argv: Vec> = Vec::new(); - for a in args { - argv.push(self.reg_load(*a)?.to_nyash_box()); - } - match host.invoke_instance_method(&p.box_type, method, p.inner.instance_id, &argv) { - 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 - ))) - } - } else { - Err(VMError::InvalidInstruction(format!( - "Method {} not supported on BoxRef({})", - method, box_ref.type_name() - ))) - } - } - _ => Err(VMError::InvalidInstruction(format!( - "Method {} not supported on {:?}", - method, receiver - ))) - } - } - - /// 外部関数実行(C ABI) - fn execute_extern_function(&mut self, extern_name: &str, args: &[ValueId]) -> Result { - match extern_name { - "exit" => { - let code = if let Some(arg_id) = args.get(0) { - self.reg_load(*arg_id)?.as_integer().unwrap_or(0) - } else { 0 }; - std::process::exit(code as i32); - } - "panic" => { - let msg = if let Some(arg_id) = args.get(0) { - self.reg_load(*arg_id)?.to_string() - } else { "VM panic".to_string() }; - panic!("{}", msg); - } - _ => Err(VMError::InvalidInstruction(format!( - "Unknown extern function: {}", - extern_name - ))) - } - } -} diff --git a/src/backend/mir_interpreter/exec.rs b/src/backend/mir_interpreter/exec.rs new file mode 100644 index 00000000..29b4608e --- /dev/null +++ b/src/backend/mir_interpreter/exec.rs @@ -0,0 +1,125 @@ +use super::*; +use crate::mir::basic_block::BasicBlock; +use std::mem; + +impl MirInterpreter { + pub(super) fn exec_function_inner( + &mut self, + func: &MirFunction, + arg_vals: Option<&[VMValue]>, + ) -> Result { + let saved_regs = mem::take(&mut self.regs); + let saved_fn = self.cur_fn.clone(); + self.cur_fn = Some(func.signature.name.clone()); + + if let Some(args) = arg_vals { + for (i, pid) in func.params.iter().enumerate() { + let v = args.get(i).cloned().unwrap_or(VMValue::Void); + self.regs.insert(*pid, v); + } + } + + let mut cur = func.entry_block; + let mut last_pred: Option = None; + + loop { + let block = func + .blocks + .get(&cur) + .ok_or_else(|| VMError::InvalidBasicBlock(format!("bb {:?} not found", cur)))?; + + self.apply_phi_nodes(block, last_pred)?; + self.execute_block_instructions(block)?; + + match self.handle_terminator(block)? { + BlockOutcome::Return(result) => { + self.cur_fn = saved_fn; + self.regs = saved_regs; + return Ok(result); + } + BlockOutcome::Next { + target, + predecessor, + } => { + last_pred = Some(predecessor); + cur = target; + } + } + } + } + + fn apply_phi_nodes( + &mut self, + block: &BasicBlock, + last_pred: Option, + ) -> Result<(), VMError> { + for inst in block.phi_instructions() { + if let MirInstruction::Phi { dst, inputs } = inst { + let dst_id = *dst; + if let Some(pred) = last_pred { + if let Some((_, val)) = inputs.iter().find(|(bb, _)| *bb == pred) { + let v = self.reg_load(*val)?; + self.regs.insert(dst_id, v); + } + } else if let Some((_, val)) = inputs.first() { + let v = self.reg_load(*val)?; + self.regs.insert(dst_id, v); + } + } + } + Ok(()) + } + + fn execute_block_instructions(&mut self, block: &BasicBlock) -> Result<(), VMError> { + for inst in block.non_phi_instructions() { + self.execute_instruction(inst)?; + } + Ok(()) + } + + fn handle_terminator(&mut self, block: &BasicBlock) -> Result { + match &block.terminator { + Some(MirInstruction::Return { value }) => { + let result = if let Some(v) = value { + self.reg_load(*v)? + } else { + VMValue::Void + }; + Ok(BlockOutcome::Return(result)) + } + Some(MirInstruction::Jump { target }) => Ok(BlockOutcome::Next { + target: *target, + predecessor: block.id, + }), + Some(MirInstruction::Branch { + condition, + then_bb, + else_bb, + }) => { + let cond = self.reg_load(*condition)?; + let branch = to_bool_vm(&cond).map_err(VMError::TypeError)?; + let target = if branch { *then_bb } else { *else_bb }; + Ok(BlockOutcome::Next { + target, + predecessor: block.id, + }) + } + None => Err(VMError::InvalidBasicBlock(format!( + "unterminated block {:?}", + block.id + ))), + Some(other) => Err(VMError::InvalidInstruction(format!( + "invalid terminator in MIR interp: {:?}", + other + ))), + } + } +} + +enum BlockOutcome { + Return(VMValue), + Next { + target: BasicBlockId, + predecessor: BasicBlockId, + }, +} diff --git a/src/backend/mir_interpreter/handlers/arithmetic.rs b/src/backend/mir_interpreter/handlers/arithmetic.rs new file mode 100644 index 00000000..1fb6d546 --- /dev/null +++ b/src/backend/mir_interpreter/handlers/arithmetic.rs @@ -0,0 +1,82 @@ +use super::*; + +impl MirInterpreter { + pub(super) fn handle_const(&mut self, dst: ValueId, value: &ConstValue) -> Result<(), VMError> { + let v = match value { + ConstValue::Integer(i) => VMValue::Integer(*i), + ConstValue::Float(f) => VMValue::Float(*f), + ConstValue::Bool(b) => VMValue::Bool(*b), + ConstValue::String(s) => VMValue::String(s.clone()), + ConstValue::Null | ConstValue::Void => VMValue::Void, + }; + self.regs.insert(dst, v); + Ok(()) + } + + pub(super) fn handle_binop( + &mut self, + dst: ValueId, + op: BinaryOp, + lhs: ValueId, + rhs: ValueId, + ) -> Result<(), VMError> { + let a = self.reg_load(lhs)?; + let b = self.reg_load(rhs)?; + let v = self.eval_binop(op, a, b)?; + self.regs.insert(dst, v); + Ok(()) + } + + pub(super) fn handle_unary_op( + &mut self, + dst: ValueId, + op: crate::mir::UnaryOp, + operand: ValueId, + ) -> Result<(), VMError> { + let x = self.reg_load(operand)?; + let v = match op { + crate::mir::UnaryOp::Neg => match x { + VMValue::Integer(i) => VMValue::Integer(-i), + VMValue::Float(f) => VMValue::Float(-f), + _ => { + return Err(VMError::TypeError(format!( + "neg expects number, got {:?}", + x + ))) + } + }, + crate::mir::UnaryOp::Not => VMValue::Bool(!to_bool_vm(&x).map_err(VMError::TypeError)?), + crate::mir::UnaryOp::BitNot => match x { + VMValue::Integer(i) => VMValue::Integer(!i), + _ => { + return Err(VMError::TypeError(format!( + "bitnot expects integer, got {:?}", + x + ))) + } + }, + }; + self.regs.insert(dst, v); + Ok(()) + } + + pub(super) fn handle_compare( + &mut self, + dst: ValueId, + op: CompareOp, + lhs: ValueId, + rhs: ValueId, + ) -> Result<(), VMError> { + let a = self.reg_load(lhs)?; + let b = self.reg_load(rhs)?; + let res = self.eval_cmp(op, a, b)?; + self.regs.insert(dst, VMValue::Bool(res)); + Ok(()) + } + + pub(super) fn handle_copy(&mut self, dst: ValueId, src: ValueId) -> Result<(), VMError> { + let v = self.reg_load(src)?; + self.regs.insert(dst, v); + Ok(()) + } +} diff --git a/src/backend/mir_interpreter/handlers/boxes.rs b/src/backend/mir_interpreter/handlers/boxes.rs new file mode 100644 index 00000000..48b7503e --- /dev/null +++ b/src/backend/mir_interpreter/handlers/boxes.rs @@ -0,0 +1,265 @@ +use super::*; +use crate::box_trait::NyashBox; + +impl MirInterpreter { + pub(super) fn handle_new_box( + &mut self, + dst: ValueId, + box_type: &str, + args: &[ValueId], + ) -> Result<(), VMError> { + let mut converted: Vec> = Vec::with_capacity(args.len()); + for vid in args { + converted.push(self.reg_load(*vid)?.to_nyash_box()); + } + let reg = crate::runtime::unified_registry::get_global_unified_registry(); + let created = reg + .lock() + .unwrap() + .create_box(box_type, &converted) + .map_err(|e| { + VMError::InvalidInstruction(format!("NewBox {} failed: {}", box_type, e)) + })?; + self.regs.insert(dst, VMValue::from_nyash_box(created)); + Ok(()) + } + + pub(super) fn handle_plugin_invoke( + &mut self, + dst: Option, + box_val: ValueId, + method: &str, + args: &[ValueId], + ) -> Result<(), VMError> { + let recv = self.reg_load(box_val)?; + let recv_box: Box = match recv.clone() { + VMValue::BoxRef(b) => b.share_box(), + other => other.to_nyash_box(), + }; + + if let Some(p) = recv_box + .as_any() + .downcast_ref::() + { + let host = crate::runtime::plugin_loader_unified::get_global_plugin_host(); + let host = host.read().unwrap(); + let mut argv: Vec> = Vec::with_capacity(args.len()); + for a in args { + argv.push(self.reg_load(*a)?.to_nyash_box()); + } + match host.invoke_instance_method(&p.box_type, method, p.inner.instance_id, &argv) { + Ok(Some(ret)) => { + if let Some(d) = dst { + self.regs.insert(d, VMValue::from_nyash_box(ret)); + } + } + Ok(None) => { + if let Some(d) = dst { + self.regs.insert(d, VMValue::Void); + } + } + Err(e) => { + return Err(VMError::InvalidInstruction(format!( + "PluginInvoke {}.{} failed: {:?}", + p.box_type, method, e + ))) + } + } + Ok(()) + } else if method == "toString" { + if let Some(d) = dst { + self.regs + .insert(d, VMValue::String(recv_box.to_string_box().value)); + } + Ok(()) + } else { + Err(VMError::InvalidInstruction(format!( + "PluginInvoke unsupported on {} for method {}", + recv_box.type_name(), + method + ))) + } + } + + pub(super) fn handle_box_call( + &mut self, + dst: Option, + box_val: ValueId, + method: &str, + args: &[ValueId], + ) -> Result<(), VMError> { + if self.try_handle_object_fields(dst, box_val, method, args)? { + return Ok(()); + } + if self.try_handle_string_box(dst, box_val, method, args)? { + return Ok(()); + } + self.invoke_plugin_box(dst, box_val, method, args) + } + + fn try_handle_object_fields( + &mut self, + dst: Option, + box_val: ValueId, + method: &str, + args: &[ValueId], + ) -> Result { + match method { + "getField" => { + if args.len() != 1 { + return Err(VMError::InvalidInstruction("getField expects 1 arg".into())); + } + let fname = match self.reg_load(args[0])? { + VMValue::String(s) => s, + v => v.to_string(), + }; + let v = self + .obj_fields + .get(&box_val) + .and_then(|m| m.get(&fname)) + .cloned() + .unwrap_or(VMValue::Void); + if let Some(d) = dst { + self.regs.insert(d, v); + } + 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])?; + self.obj_fields + .entry(box_val) + .or_default() + .insert(fname, valv); + Ok(true) + } + _ => Ok(false), + } + } + + fn try_handle_string_box( + &mut self, + dst: Option, + box_val: ValueId, + method: &str, + args: &[ValueId], + ) -> Result { + let recv = self.reg_load(box_val)?; + let recv_box_any: Box = match recv.clone() { + VMValue::BoxRef(b) => b.share_box(), + other => other.to_nyash_box(), + }; + if let Some(sb) = recv_box_any + .as_any() + .downcast_ref::() + { + match method { + "length" => { + let ret = sb.length(); + if let Some(d) = dst { + self.regs.insert(d, VMValue::from_nyash_box(ret)); + } + return Ok(true); + } + "concat" => { + if args.len() != 1 { + return Err(VMError::InvalidInstruction("concat expects 1 arg".into())); + } + let rhs = self.reg_load(args[0])?; + let new_s = format!("{}{}", sb.value, rhs.to_string()); + if let Some(d) = dst { + self.regs.insert( + d, + VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new( + new_s, + ))), + ); + } + return Ok(true); + } + _ => {} + } + } + Ok(false) + } + + fn invoke_plugin_box( + &mut self, + dst: Option, + box_val: ValueId, + method: &str, + args: &[ValueId], + ) -> Result<(), VMError> { + let recv = self.reg_load(box_val)?; + let recv_box: Box = match recv.clone() { + VMValue::BoxRef(b) => b.share_box(), + other => other.to_nyash_box(), + }; + if let Some(p) = recv_box + .as_any() + .downcast_ref::() + { + if p.box_type == "ConsoleBox" && method == "readLine" { + use std::io::{self, Read}; + let mut s = String::new(); + let mut stdin = io::stdin(); + let mut buf = [0u8; 1]; + while let Ok(n) = stdin.read(&mut buf) { + if n == 0 { + break; + } + let ch = buf[0] as char; + if ch == '\n' { + break; + } + s.push(ch); + if s.len() > 1_000_000 { + break; + } + } + if let Some(d) = dst { + self.regs.insert(d, VMValue::String(s)); + } + return Ok(()); + } + let host = crate::runtime::plugin_loader_unified::get_global_plugin_host(); + let host = host.read().unwrap(); + let mut argv: Vec> = Vec::with_capacity(args.len()); + for a in args { + argv.push(self.reg_load(*a)?.to_nyash_box()); + } + match host.invoke_instance_method(&p.box_type, method, p.inner.instance_id, &argv) { + Ok(Some(ret)) => { + if let Some(d) = dst { + self.regs.insert(d, VMValue::from_nyash_box(ret)); + } + Ok(()) + } + Ok(None) => { + if let Some(d) = dst { + self.regs.insert(d, VMValue::Void); + } + Ok(()) + } + Err(e) => Err(VMError::InvalidInstruction(format!( + "BoxCall {}.{} failed: {:?}", + p.box_type, method, e + ))), + } + } else { + Err(VMError::InvalidInstruction(format!( + "BoxCall unsupported on {}.{}", + recv_box.type_name(), + method + ))) + } + } +} diff --git a/src/backend/mir_interpreter/handlers/calls.rs b/src/backend/mir_interpreter/handlers/calls.rs new file mode 100644 index 00000000..c66d80d0 --- /dev/null +++ b/src/backend/mir_interpreter/handlers/calls.rs @@ -0,0 +1,252 @@ +use super::*; + +impl MirInterpreter { + pub(super) fn handle_call( + &mut self, + dst: Option, + func: ValueId, + callee: Option<&Callee>, + args: &[ValueId], + ) -> Result<(), VMError> { + let call_result = if let Some(callee_type) = callee { + self.execute_callee_call(callee_type, args)? + } else { + self.execute_legacy_call(func, args)? + }; + if let Some(d) = dst { + self.regs.insert(d, call_result); + } + Ok(()) + } + + pub(super) fn execute_callee_call( + &mut self, + callee: &Callee, + args: &[ValueId], + ) -> Result { + match callee { + Callee::Global(func_name) => self.execute_global_function(func_name, args), + Callee::Method { + box_name: _, + method, + receiver, + } => { + if let Some(recv_id) = receiver { + let recv_val = self.reg_load(*recv_id)?; + self.execute_method_call(&recv_val, method, args) + } else { + Err(VMError::InvalidInstruction(format!( + "Method call missing receiver for {}", + method + ))) + } + } + Callee::Constructor { box_type } => Err(VMError::InvalidInstruction(format!( + "Constructor calls not yet implemented for {}", + box_type + ))), + Callee::Closure { .. } => Err(VMError::InvalidInstruction( + "Closure creation not yet implemented in VM".into(), + )), + Callee::Value(func_val_id) => { + let _func_val = self.reg_load(*func_val_id)?; + Err(VMError::InvalidInstruction( + "First-class function calls not yet implemented in VM".into(), + )) + } + Callee::Extern(extern_name) => self.execute_extern_function(extern_name, args), + } + } + + pub(super) fn execute_legacy_call( + &mut self, + func_id: ValueId, + args: &[ValueId], + ) -> Result { + let name_val = self.reg_load(func_id)?; + let raw = match name_val { + VMValue::String(ref s) => s.clone(), + other => other.to_string(), + }; + + let mut pick: Option = None; + if self.functions.contains_key(&raw) { + pick = Some(raw.clone()); + } else { + let arity = args.len(); + let mut cands: Vec = Vec::new(); + let suf = format!(".{}{}", raw, format!("/{}", arity)); + for k in self.functions.keys() { + if k.ends_with(&suf) { + cands.push(k.clone()); + } + } + if cands.is_empty() && raw.contains('/') && self.functions.contains_key(&raw) { + cands.push(raw.clone()); + } + if cands.len() > 1 { + if let Some(cur) = &self.cur_fn { + let cur_box = cur.split('.').next().unwrap_or(""); + let scoped: Vec = cands + .iter() + .filter(|k| k.starts_with(&format!("{}.", cur_box))) + .cloned() + .collect(); + if scoped.len() == 1 { + cands = scoped; + } + } + } + if cands.len() == 1 { + pick = Some(cands.remove(0)); + } else if cands.len() > 1 { + cands.sort(); + pick = Some(cands[0].clone()); + } + } + + let fname = pick.ok_or_else(|| { + VMError::InvalidInstruction(format!( + "call unresolved: '{}' (arity={})", + raw, + args.len() + )) + })?; + + if std::env::var("NYASH_VM_CALL_TRACE").ok().as_deref() == Some("1") { + eprintln!("[vm] legacy-call resolved '{}' -> '{}'", raw, fname); + } + + let callee = + self.functions.get(&fname).cloned().ok_or_else(|| { + VMError::InvalidInstruction(format!("function not found: {}", fname)) + })?; + + let mut argv: Vec = Vec::new(); + for a in args { + argv.push(self.reg_load(*a)?); + } + self.exec_function_inner(&callee, Some(&argv)) + } + + fn execute_global_function( + &mut self, + func_name: &str, + args: &[ValueId], + ) -> Result { + match func_name { + "nyash.builtin.print" | "print" | "nyash.console.log" => { + if let Some(arg_id) = args.get(0) { + let val = self.reg_load(*arg_id)?; + println!("{}", val.to_string()); + } + Ok(VMValue::Void) + } + "nyash.builtin.error" => { + if let Some(arg_id) = args.get(0) { + let val = self.reg_load(*arg_id)?; + eprintln!("Error: {}", val.to_string()); + } + Ok(VMValue::Void) + } + _ => Err(VMError::InvalidInstruction(format!( + "Unknown global function: {}", + func_name + ))), + } + } + + fn execute_method_call( + &mut self, + receiver: &VMValue, + method: &str, + args: &[ValueId], + ) -> Result { + match receiver { + VMValue::String(s) => match method { + "length" => Ok(VMValue::Integer(s.len() as i64)), + "concat" => { + if let Some(arg_id) = args.get(0) { + let arg_val = self.reg_load(*arg_id)?; + let new_str = format!("{}{}", s, arg_val.to_string()); + Ok(VMValue::String(new_str)) + } else { + Err(VMError::InvalidInstruction( + "concat requires 1 argument".into(), + )) + } + } + _ => Err(VMError::InvalidInstruction(format!( + "Unknown String method: {}", + method + ))), + }, + VMValue::BoxRef(box_ref) => { + if let Some(p) = box_ref + .as_any() + .downcast_ref::() + { + let host = crate::runtime::plugin_loader_unified::get_global_plugin_host(); + let host = host.read().unwrap(); + let mut argv: Vec> = + Vec::with_capacity(args.len()); + for a in args { + argv.push(self.reg_load(*a)?.to_nyash_box()); + } + match host.invoke_instance_method( + &p.box_type, + method, + p.inner.instance_id, + &argv, + ) { + 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 + ))), + } + } else { + Err(VMError::InvalidInstruction(format!( + "Method {} not supported on BoxRef({})", + method, + box_ref.type_name() + ))) + } + } + _ => Err(VMError::InvalidInstruction(format!( + "Method {} not supported on {:?}", + method, receiver + ))), + } + } + + fn execute_extern_function( + &mut self, + extern_name: &str, + args: &[ValueId], + ) -> Result { + match extern_name { + "exit" => { + let code = if let Some(arg_id) = args.get(0) { + self.reg_load(*arg_id)?.as_integer().unwrap_or(0) + } else { + 0 + }; + std::process::exit(code as i32); + } + "panic" => { + let msg = if let Some(arg_id) = args.get(0) { + self.reg_load(*arg_id)?.to_string() + } else { + "VM panic".to_string() + }; + panic!("{}", msg); + } + _ => Err(VMError::InvalidInstruction(format!( + "Unknown extern function: {}", + extern_name + ))), + } + } +} diff --git a/src/backend/mir_interpreter/handlers/externals.rs b/src/backend/mir_interpreter/handlers/externals.rs new file mode 100644 index 00000000..b72f2bfc --- /dev/null +++ b/src/backend/mir_interpreter/handlers/externals.rs @@ -0,0 +1,100 @@ +use super::*; + +impl MirInterpreter { + pub(super) fn handle_extern_call( + &mut self, + dst: Option, + iface: &str, + method: &str, + args: &[ValueId], + ) -> Result<(), VMError> { + match (iface, method) { + ("env.console", "log") => { + if let Some(a0) = args.get(0) { + let v = self.reg_load(*a0)?; + println!("{}", v.to_string()); + } + if let Some(d) = dst { + self.regs.insert(d, VMValue::Void); + } + 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()); + } + if let Some(d) = dst { + self.regs.insert(d, 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())); + } + } + if let Some(d) = dst { + self.regs.insert(d, VMValue::Void); + } + 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(); + if let Some(d) = dst { + self.regs.insert(d, VMValue::from_nyash_box(v)); + } + } + _ => { + return Err(VMError::TypeError("await expects Future".into())); + } + } + } + Ok(()) + } + ("env.runtime", "checkpoint") => { + crate::runtime::global_hooks::safepoint_and_poll(); + if let Some(d) = dst { + self.regs.insert(d, VMValue::Void); + } + 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); + } + if let Some(d) = dst { + self.regs.insert(d, VMValue::Void); + } + 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())); + if let Some(d) = dst { + self.regs.insert(d, VMValue::from_nyash_box(vb)); + } + } + Ok(()) + } + _ => Err(VMError::InvalidInstruction(format!( + "ExternCall {}.{} not supported", + iface, method + ))), + } + } +} diff --git a/src/backend/mir_interpreter/handlers/memory.rs b/src/backend/mir_interpreter/handlers/memory.rs new file mode 100644 index 00000000..dc808afb --- /dev/null +++ b/src/backend/mir_interpreter/handlers/memory.rs @@ -0,0 +1,45 @@ +use super::*; + +impl MirInterpreter { + pub(super) fn handle_ref_set( + &mut self, + reference: ValueId, + field: &str, + value: ValueId, + ) -> Result<(), VMError> { + let v = self.reg_load(value)?; + self.obj_fields + .entry(reference) + .or_default() + .insert(field.into(), v); + Ok(()) + } + + pub(super) fn handle_ref_get( + &mut self, + dst: ValueId, + reference: ValueId, + field: &str, + ) -> Result<(), VMError> { + let v = self + .obj_fields + .get(&reference) + .and_then(|m| m.get(field)) + .cloned() + .unwrap_or(VMValue::Void); + self.regs.insert(dst, v); + Ok(()) + } + + pub(super) fn handle_load(&mut self, dst: ValueId, ptr: ValueId) -> Result<(), VMError> { + let v = self.mem.get(&ptr).cloned().unwrap_or(VMValue::Void); + self.regs.insert(dst, v); + Ok(()) + } + + pub(super) fn handle_store(&mut self, ptr: ValueId, value: ValueId) -> Result<(), VMError> { + let v = self.reg_load(value)?; + self.mem.insert(ptr, v); + Ok(()) + } +} diff --git a/src/backend/mir_interpreter/handlers/misc.rs b/src/backend/mir_interpreter/handlers/misc.rs new file mode 100644 index 00000000..98689603 --- /dev/null +++ b/src/backend/mir_interpreter/handlers/misc.rs @@ -0,0 +1,17 @@ +use super::*; + +impl MirInterpreter { + pub(super) fn handle_debug(&mut self, message: &str, value: ValueId) -> Result<(), VMError> { + let v = self.reg_load(value)?; + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[mir-debug] {} => {:?}", message, v); + } + Ok(()) + } + + pub(super) fn handle_print(&mut self, value: ValueId) -> Result<(), VMError> { + let v = self.reg_load(value)?; + println!("{}", v.to_string()); + Ok(()) + } +} diff --git a/src/backend/mir_interpreter/handlers/mod.rs b/src/backend/mir_interpreter/handlers/mod.rs new file mode 100644 index 00000000..27e79759 --- /dev/null +++ b/src/backend/mir_interpreter/handlers/mod.rs @@ -0,0 +1,87 @@ +use super::*; + +mod arithmetic; +mod boxes; +mod calls; +mod externals; +mod memory; +mod misc; + +impl MirInterpreter { + pub(super) fn execute_instruction(&mut self, inst: &MirInstruction) -> Result<(), VMError> { + match inst { + MirInstruction::Const { dst, value } => self.handle_const(*dst, value)?, + MirInstruction::NewBox { + dst, + box_type, + args, + } => self.handle_new_box(*dst, box_type, args)?, + MirInstruction::PluginInvoke { + dst, + box_val, + method, + args, + .. + } => self.handle_plugin_invoke(*dst, *box_val, method, args)?, + MirInstruction::BoxCall { + dst, + box_val, + method, + args, + .. + } => self.handle_box_call(*dst, *box_val, method, args)?, + MirInstruction::ExternCall { + dst, + iface_name, + method_name, + args, + .. + } => self.handle_extern_call(*dst, iface_name, method_name, args)?, + MirInstruction::RefSet { + reference, + field, + value, + } => self.handle_ref_set(*reference, field, *value)?, + MirInstruction::RefGet { + dst, + reference, + field, + } => self.handle_ref_get(*dst, *reference, field)?, + MirInstruction::BinOp { dst, op, lhs, rhs } => { + self.handle_binop(*dst, *op, *lhs, *rhs)? + } + MirInstruction::UnaryOp { dst, op, operand } => { + self.handle_unary_op(*dst, *op, *operand)? + } + MirInstruction::Compare { dst, op, lhs, rhs } => { + self.handle_compare(*dst, *op, *lhs, *rhs)? + } + MirInstruction::Copy { dst, src } => self.handle_copy(*dst, *src)?, + MirInstruction::Load { dst, ptr } => self.handle_load(*dst, *ptr)?, + MirInstruction::Store { ptr, value } => self.handle_store(*ptr, *value)?, + MirInstruction::Call { + dst, + func, + callee, + args, + .. + } => self.handle_call(*dst, *func, callee.as_ref(), args)?, + MirInstruction::Debug { message, value } => { + self.handle_debug(message, *value)?; + } + MirInstruction::Print { value, .. } => self.handle_print(*value)?, + MirInstruction::BarrierRead { .. } + | MirInstruction::BarrierWrite { .. } + | MirInstruction::Barrier { .. } + | MirInstruction::Safepoint + | MirInstruction::Nop => {} + other => { + return Err(VMError::InvalidInstruction(format!( + "MIR interp: unimplemented instruction: {:?}", + other + ))) + } + } + Ok(()) + } +} diff --git a/src/backend/mir_interpreter/helpers.rs b/src/backend/mir_interpreter/helpers.rs new file mode 100644 index 00000000..b9d5d7da --- /dev/null +++ b/src/backend/mir_interpreter/helpers.rs @@ -0,0 +1,76 @@ +use super::*; + +impl MirInterpreter { + pub(super) fn reg_load(&self, id: ValueId) -> Result { + self.regs + .get(&id) + .cloned() + .ok_or_else(|| VMError::InvalidValue(format!("use of undefined value {:?}", id))) + } + + pub(super) fn eval_binop( + &self, + op: BinaryOp, + a: VMValue, + b: VMValue, + ) -> Result { + use BinaryOp::*; + use VMValue::*; + Ok(match (op, a, b) { + (Add, Integer(x), Integer(y)) => Integer(x + y), + (Add, String(s), Integer(y)) => String(format!("{}{}", s, y)), + (Add, String(s), Float(y)) => String(format!("{}{}", s, y)), + (Add, String(s), Bool(y)) => String(format!("{}{}", s, y)), + (Add, String(s), String(t)) => String(format!("{}{}", s, t)), + (Add, Integer(x), String(t)) => String(format!("{}{}", x, t)), + (Add, Float(x), String(t)) => String(format!("{}{}", x, t)), + (Add, Bool(x), String(t)) => String(format!("{}{}", x, t)), + (Sub, Integer(x), Integer(y)) => Integer(x - y), + (Mul, Integer(x), Integer(y)) => Integer(x * y), + (Div, Integer(_), Integer(0)) => return Err(VMError::DivisionByZero), + (Div, Integer(x), Integer(y)) => Integer(x / y), + (Mod, Integer(_), Integer(0)) => return Err(VMError::DivisionByZero), + (Mod, Integer(x), Integer(y)) => Integer(x % y), + (Add, Float(x), Float(y)) => Float(x + y), + (Sub, Float(x), Float(y)) => Float(x - y), + (Mul, Float(x), Float(y)) => Float(x * y), + (Div, Float(_), Float(y)) if y == 0.0 => return Err(VMError::DivisionByZero), + (Div, Float(x), Float(y)) => Float(x / y), + (Mod, Float(x), Float(y)) => Float(x % y), + (BitAnd, Integer(x), Integer(y)) => Integer(x & y), + (BitOr, Integer(x), Integer(y)) => Integer(x | y), + (BitXor, Integer(x), Integer(y)) => Integer(x ^ y), + (Shl, Integer(x), Integer(y)) => Integer(x.wrapping_shl(y as u32)), + (Shr, Integer(x), Integer(y)) => Integer(x.wrapping_shr(y as u32)), + (opk, va, vb) => { + return Err(VMError::TypeError(format!( + "unsupported binop {:?} on {:?} and {:?}", + opk, va, vb + ))) + } + }) + } + + pub(super) fn eval_cmp(&self, op: CompareOp, a: VMValue, b: VMValue) -> Result { + use CompareOp::*; + use VMValue::*; + Ok(match (op, &a, &b) { + (Eq, _, _) => eq_vm(&a, &b), + (Ne, _, _) => !eq_vm(&a, &b), + (Lt, Integer(x), Integer(y)) => x < y, + (Le, Integer(x), Integer(y)) => x <= y, + (Gt, Integer(x), Integer(y)) => x > y, + (Ge, Integer(x), Integer(y)) => x >= y, + (Lt, Float(x), Float(y)) => x < y, + (Le, Float(x), Float(y)) => x <= y, + (Gt, Float(x), Float(y)) => x > y, + (Ge, Float(x), Float(y)) => x >= y, + (opk, va, vb) => { + return Err(VMError::TypeError(format!( + "unsupported compare {:?} on {:?} and {:?}", + opk, va, vb + ))) + } + }) + } +} diff --git a/src/backend/mir_interpreter/mod.rs b/src/backend/mir_interpreter/mod.rs new file mode 100644 index 00000000..56daa2cc --- /dev/null +++ b/src/backend/mir_interpreter/mod.rs @@ -0,0 +1,58 @@ +/*! + * Minimal MIR Interpreter + * + * Executes a subset of MIR instructions for fast iteration without LLVM/JIT. + * Supported: Const, BinOp(Add/Sub/Mul/Div/Mod), Compare, Load/Store, Branch, Jump, Return, + * Print/Debug (best-effort), Barrier/Safepoint (no-op). + */ + +use std::collections::HashMap; + +use crate::box_trait::NyashBox; + +pub(super) use crate::backend::abi_util::{eq_vm, to_bool_vm}; +pub(super) use crate::backend::vm::{VMError, VMValue}; +pub(super) use crate::mir::{ + BasicBlockId, BinaryOp, Callee, CompareOp, ConstValue, MirFunction, MirInstruction, MirModule, + ValueId, +}; + +mod exec; +mod handlers; +mod helpers; + +pub struct MirInterpreter { + pub(super) regs: HashMap, + pub(super) mem: HashMap, + pub(super) obj_fields: HashMap>, + pub(super) functions: HashMap, + pub(super) cur_fn: Option, +} + +impl MirInterpreter { + pub fn new() -> Self { + Self { + regs: HashMap::new(), + mem: HashMap::new(), + obj_fields: HashMap::new(), + functions: HashMap::new(), + cur_fn: None, + } + } + + /// Execute module entry (main) and return boxed result + pub fn execute_module(&mut self, module: &MirModule) -> Result, VMError> { + // Snapshot functions for call resolution + self.functions = module.functions.clone(); + let func = module + .functions + .get("main") + .ok_or_else(|| VMError::InvalidInstruction("missing main".into()))?; + let ret = self.execute_function(func)?; + Ok(ret.to_nyash_box()) + } + + fn execute_function(&mut self, func: &MirFunction) -> Result { + self.exec_function_inner(func, None) + } +} diff --git a/src/bin/ny_mir_builder.rs b/src/bin/ny_mir_builder.rs index 4d2067de..d62a7915 100644 --- a/src/bin/ny_mir_builder.rs +++ b/src/bin/ny_mir_builder.rs @@ -257,7 +257,11 @@ fn link_exe(obj_path: &str, out_path: &str, nyrt_dir: &str) -> Result<(), String .args([obj_path]) .args(["-L", "target/release"]) .args(["-L", &format!("{}/target/release", nyrt_dir)]) - .args(["-Wl,--whole-archive", "-lnyash_kernel", "-Wl,--no-whole-archive"]) + .args([ + "-Wl,--whole-archive", + "-lnyash_kernel", + "-Wl,--no-whole-archive", + ]) .args(["-lpthread", "-ldl", "-lm", "-o", out_path]) .status() .map_err(|e| e.to_string())?; diff --git a/tests/mir_builder_unit.rs b/tests/mir_builder_unit.rs index b62e021d..d9ae021f 100644 --- a/tests/mir_builder_unit.rs +++ b/tests/mir_builder_unit.rs @@ -1,5 +1,5 @@ +use nyash_rust::ast::{ASTNode, BinaryOperator, LiteralValue, Span}; use nyash_rust::mir::MirBuilder; -use nyash_rust::ast::{ASTNode, LiteralValue, BinaryOperator, Span}; #[test] fn test_literal_building() { @@ -64,4 +64,3 @@ fn test_if_statement_building() { let stats = function.stats(); assert!(stats.phi_count >= 1); } - diff --git a/tests/mir_instruction_unit.rs b/tests/mir_instruction_unit.rs index 4ad35f1c..e48c70de 100644 --- a/tests/mir_instruction_unit.rs +++ b/tests/mir_instruction_unit.rs @@ -1,6 +1,4 @@ -use nyash_rust::mir::{ - BinaryOp, ConstValue, Effect, EffectMask, MirInstruction, ValueId, -}; +use nyash_rust::mir::{BinaryOp, ConstValue, Effect, EffectMask, MirInstruction, ValueId}; #[test] fn test_const_instruction() { @@ -152,4 +150,3 @@ fn test_extern_call_instruction() { assert_eq!(void_inst.dst_value(), None); assert_eq!(void_inst.used_values(), vec![arg1]); } - diff --git a/tests/mir_verification_unit.rs b/tests/mir_verification_unit.rs index e55b1aaa..4767f79c 100644 --- a/tests/mir_verification_unit.rs +++ b/tests/mir_verification_unit.rs @@ -160,7 +160,10 @@ fn test_merge_use_before_phi_detected() { let mut verifier = MirVerifier::new(); let res = verifier.verify_function(&f); - assert!(res.is_err(), "Verifier should error on merge use without phi"); + assert!( + res.is_err(), + "Verifier should error on merge use without phi" + ); let errs = res.err().unwrap(); assert!( errs.iter().any(|e| matches!( @@ -320,4 +323,3 @@ fn test_loop_nested_if_phi() { mir_text ); } - diff --git a/tests/parser_stage3.rs b/tests/parser_stage3.rs index 03b4fcf4..75ed5401 100644 --- a/tests/parser_stage3.rs +++ b/tests/parser_stage3.rs @@ -23,7 +23,10 @@ fn stage3_disabled_rejects_try_and_throw() { let code_throw = "throw 1"; let res_throw = NyashParser::parse_from_string(code_throw); - assert!(res_throw.is_err(), "throw should be rejected when gate is off"); + assert!( + res_throw.is_err(), + "throw should be rejected when gate is off" + ); }); } @@ -32,7 +35,11 @@ fn stage3_enabled_accepts_throw() { with_env("NYASH_PARSER_STAGE3", Some("1"), || { let code = "throw (1 + 2)"; let res = NyashParser::parse_from_string(code); - assert!(res.is_ok(), "throw should parse when gate is on: {:?}", res.err()); + assert!( + res.is_ok(), + "throw should parse when gate is on: {:?}", + res.err() + ); }); } @@ -62,4 +69,3 @@ fn stage3_enabled_accepts_try_catch_variants() { assert!(NyashParser::parse_from_string(code3).is_ok()); }); } - diff --git a/tests/tokenizer_unit.rs b/tests/tokenizer_unit.rs index 1f6b0716..ee38324e 100644 --- a/tests/tokenizer_unit.rs +++ b/tests/tokenizer_unit.rs @@ -169,4 +169,3 @@ fn test_longest_match_sequences() { assert!(matches!(kinds[3], TokenType::RANGE)); assert!(matches!(kinds[4], TokenType::DOT)); } - diff --git a/tests/transform_golden.rs b/tests/transform_golden.rs index 3dbf2af8..e07d8667 100644 --- a/tests/transform_golden.rs +++ b/tests/transform_golden.rs @@ -42,7 +42,9 @@ fn golden_transforms() { assert!(root.exists(), "missing tests/golden/transforms directory"); for entry in fs::read_dir(root).expect("scan golden dirs") { let entry = entry.expect("dir entry"); - if !entry.file_type().expect("ft").is_dir() { continue; } + if !entry.file_type().expect("ft").is_dir() { + continue; + } let case_dir = entry.path(); let in_path = case_dir.join("in.json"); let out_path = case_dir.join("out.golden.json"); @@ -68,9 +70,10 @@ fn golden_transforms() { let exp_s = serde_json::to_string_pretty(&expected).unwrap(); panic!( "Golden mismatch in {}\n--- expected\n{}\n--- got\n{}\n", - case_dir.display(), exp_s, got_s + case_dir.display(), + exp_s, + got_s ); } } } -